mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Extract cctext as independant library and update deps
This commit is contained in:
parent
12dfd1c3cb
commit
9db4ee4d56
|
@ -94,8 +94,7 @@ This readme file is used mostly to document project dependencies
|
||||||
- gunicorn
|
- gunicorn
|
||||||
- coreapi
|
- coreapi
|
||||||
- psycopg2-binary
|
- psycopg2-binary
|
||||||
- pymorphy3
|
- cctext
|
||||||
- razdel
|
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
''' Concept core text processing library. '''
|
|
||||||
# pylint: skip-file
|
|
||||||
from .syntax import RuSyntax, Capitalization
|
|
||||||
from .rumodel import Morphology, SemanticRole, WordTag, morpho, split_grams, combine_grams
|
|
||||||
from .ruparser import PhraseParser, WordToken, Collation
|
|
||||||
from .reference import EntityReference, ReferenceType, SyntacticReference, parse_reference
|
|
||||||
from .context import TermForm, Entity, TermContext
|
|
||||||
from .resolver import Reference, Position, Resolver, ResolvedReference, resolve_entity, resolve_syntactic, extract_entities
|
|
||||||
|
|
||||||
from .conceptapi import (
|
|
||||||
parse, normalize,
|
|
||||||
generate_lexeme, inflect, inflect_context, inflect_substitute, inflect_dependant,
|
|
||||||
match_all_morpho, find_substr
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: implement Part of speech transition for VERB <-> NOUN
|
|
|
@ -1,90 +0,0 @@
|
||||||
'''
|
|
||||||
Concept API Python functions.
|
|
||||||
|
|
||||||
::guarantee:: doesn't raise exceptions and returns workable outputs
|
|
||||||
'''
|
|
||||||
from cctext.rumodel import Morphology
|
|
||||||
from .syntax import RuSyntax
|
|
||||||
from .ruparser import PhraseParser
|
|
||||||
from .rumodel import split_grams
|
|
||||||
|
|
||||||
parser = PhraseParser()
|
|
||||||
|
|
||||||
|
|
||||||
def parse(text: str, require_grams: str = '') -> str:
|
|
||||||
''' Determine morpho tags for input text.
|
|
||||||
::returns:: string of comma separated grammar tags or empty string '''
|
|
||||||
model = parser.parse(text, require_grams=split_grams(require_grams))
|
|
||||||
if model is None:
|
|
||||||
return ''
|
|
||||||
result = model.get_morpho().to_text()
|
|
||||||
return result if result != 'UNKN' else ''
|
|
||||||
|
|
||||||
|
|
||||||
# def parse_variants(text: str, require_grams: str = '') -> list[tuple[str, str]]:
|
|
||||||
# ''' Get all variants of a parse.
|
|
||||||
# ::returns:: string of comma separated grammar tags or empty string '''
|
|
||||||
|
|
||||||
|
|
||||||
def generate_lexeme(text_normal: str) -> list[tuple[str, str]]:
|
|
||||||
''' Get all inflected forms belonging to same Lexeme. '''
|
|
||||||
model = parser.parse(text_normal)
|
|
||||||
if not model:
|
|
||||||
return []
|
|
||||||
result = []
|
|
||||||
for form in model.get_form().lexeme:
|
|
||||||
result.append((model.inflect(form.tag.grammemes), Morphology(form.tag).to_text()))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def normalize(text: str) -> str:
|
|
||||||
''' Generate normal form.
|
|
||||||
::returns:: normal form of input text or text itself if no parse is available '''
|
|
||||||
model = parser.parse(text)
|
|
||||||
if model is None:
|
|
||||||
return text
|
|
||||||
return model.normal_form()
|
|
||||||
|
|
||||||
|
|
||||||
def inflect(text: str, target_grams: str) -> str:
|
|
||||||
''' Inflect text to match required tags.
|
|
||||||
::returns:: infected text or initial text if infection failed '''
|
|
||||||
target_set = split_grams(target_grams)
|
|
||||||
model = parser.parse(text)
|
|
||||||
if model is None:
|
|
||||||
return text
|
|
||||||
return model.inflect(target_set)
|
|
||||||
|
|
||||||
|
|
||||||
def inflect_context(target: str, before: str = '', after: str = '') -> str:
|
|
||||||
''' Inflect text in accordance to context before and after. '''
|
|
||||||
return parser.inflect_context(target, before, after)
|
|
||||||
|
|
||||||
|
|
||||||
def inflect_substitute(substitute_normal: str, original: str) -> str:
|
|
||||||
''' Inflect substitute to match original form. '''
|
|
||||||
return parser.inflect_substitute(substitute_normal, original)
|
|
||||||
|
|
||||||
|
|
||||||
def inflect_dependant(dependant_normal: str, master: str) -> str:
|
|
||||||
''' Inflect dependant to coordinate with master text. '''
|
|
||||||
return parser.inflect_dependant(dependant_normal, master)
|
|
||||||
|
|
||||||
|
|
||||||
def match_all_morpho(text: str, filter_grams: str) -> list[list[int]]:
|
|
||||||
''' Search for all words corresponding to tags. '''
|
|
||||||
target_set = split_grams(filter_grams)
|
|
||||||
if len(target_set) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
result = []
|
|
||||||
for elem in RuSyntax.tokenize(text):
|
|
||||||
model = parser.parse(elem.text, require_grams=target_set)
|
|
||||||
if model:
|
|
||||||
result.append([elem.start, elem.stop])
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def find_substr(text: str, sub: str) -> tuple[int, int]:
|
|
||||||
''' Search for substring position in text regardless of morphology. '''
|
|
||||||
return parser.find_substr(text, sub)
|
|
|
@ -1,84 +0,0 @@
|
||||||
''' Term context for reference resolution. '''
|
|
||||||
from typing import Iterable, Optional, TypedDict
|
|
||||||
|
|
||||||
from .ruparser import PhraseParser
|
|
||||||
from .rumodel import WordTag
|
|
||||||
|
|
||||||
|
|
||||||
parser = PhraseParser()
|
|
||||||
|
|
||||||
|
|
||||||
class TermForm(TypedDict):
|
|
||||||
''' Represents term in a specific form. '''
|
|
||||||
text: str
|
|
||||||
grams: Iterable[str]
|
|
||||||
|
|
||||||
|
|
||||||
def _match_grams(query: Iterable[str], test: Iterable[str]) -> bool:
|
|
||||||
''' Check if grams from test fit query. '''
|
|
||||||
for gram in test:
|
|
||||||
if not gram in query:
|
|
||||||
if not gram in WordTag.PARTS_OF_SPEECH:
|
|
||||||
return False
|
|
||||||
for pos in WordTag.PARTS_OF_SPEECH:
|
|
||||||
if pos in query:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _search_form(query: Iterable[str], data: Iterable[TermForm]) -> Optional[str]:
|
|
||||||
for form in data:
|
|
||||||
if _match_grams(query, form['grams']):
|
|
||||||
return form['text']
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class Entity:
|
|
||||||
''' Represents text entity. '''
|
|
||||||
def __init__(self, alias: str, nominal: str, manual_forms: Optional[Iterable[TermForm]]=None):
|
|
||||||
if manual_forms is None:
|
|
||||||
self.manual = []
|
|
||||||
else:
|
|
||||||
self.manual = list(manual_forms)
|
|
||||||
self.alias = alias
|
|
||||||
self._nominal = nominal
|
|
||||||
self._cached: list[TermForm] = []
|
|
||||||
|
|
||||||
def get_nominal(self) -> str:
|
|
||||||
''' Getter for _nominal. '''
|
|
||||||
return self._nominal
|
|
||||||
|
|
||||||
def set_nominal(self, new_text: str):
|
|
||||||
''' Setter for _nominal.
|
|
||||||
Note: clears manual and cached forms. '''
|
|
||||||
if self._nominal == new_text:
|
|
||||||
return
|
|
||||||
self._nominal = new_text
|
|
||||||
self.manual = []
|
|
||||||
self._cached = []
|
|
||||||
|
|
||||||
def get_form(self, grams: Iterable[str]) -> str:
|
|
||||||
''' Get specific term form. '''
|
|
||||||
if all(False for _ in grams):
|
|
||||||
return self._nominal
|
|
||||||
text = _search_form(grams, self.manual)
|
|
||||||
if text is not None:
|
|
||||||
return text
|
|
||||||
text = _search_form(grams, self._cached)
|
|
||||||
if text is not None:
|
|
||||||
return text
|
|
||||||
|
|
||||||
model = parser.parse(self._nominal)
|
|
||||||
if model is None:
|
|
||||||
text = self._nominal
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
text = model.inflect(grams)
|
|
||||||
except ValueError as error:
|
|
||||||
text = f'!{error}!'.replace('Unknown grammeme', 'Неизвестная граммема')
|
|
||||||
self._cached.append({'text': text, 'grams': grams})
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
# Represents term context for resolving entity references.
|
|
||||||
TermContext = dict[str, Entity]
|
|
|
@ -1,60 +0,0 @@
|
||||||
''' Text reference API. '''
|
|
||||||
from enum import Enum, unique
|
|
||||||
from typing import Optional, Union
|
|
||||||
|
|
||||||
|
|
||||||
@unique
|
|
||||||
class ReferenceType(Enum):
|
|
||||||
''' Text reference types. '''
|
|
||||||
entity = 'entity'
|
|
||||||
syntactic = 'syntax'
|
|
||||||
|
|
||||||
|
|
||||||
class EntityReference:
|
|
||||||
''' Reference to entity. '''
|
|
||||||
|
|
||||||
def __init__(self, identifier: str, form: str):
|
|
||||||
self.entity = identifier
|
|
||||||
self.form = form
|
|
||||||
|
|
||||||
def get_type(self) -> ReferenceType:
|
|
||||||
return ReferenceType.entity
|
|
||||||
|
|
||||||
def to_text(self) -> str:
|
|
||||||
return f'@{{{self.entity}|{self.form}}}'
|
|
||||||
|
|
||||||
|
|
||||||
class SyntacticReference:
|
|
||||||
''' Reference to syntactic dependency on EntityReference. '''
|
|
||||||
|
|
||||||
def __init__(self, referral_offset: int, text: str):
|
|
||||||
self.nominal = text
|
|
||||||
self.offset = referral_offset
|
|
||||||
|
|
||||||
def get_type(self) -> ReferenceType:
|
|
||||||
return ReferenceType.syntactic
|
|
||||||
|
|
||||||
def to_text(self) -> str:
|
|
||||||
return f'@{{{self.offset}|{self.nominal}}}'
|
|
||||||
|
|
||||||
|
|
||||||
Reference = Union[EntityReference, SyntacticReference]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_reference(text: str) -> Optional[Reference]:
|
|
||||||
if len(text) < 4 or text[-1] != '}' or text[0:2] != '@{':
|
|
||||||
return None
|
|
||||||
blocks: list[str] = [block.strip() for block in text[2:-1].split('|')]
|
|
||||||
if len(blocks) != 2 or blocks[0] == '' or blocks[0][0] in '0':
|
|
||||||
return None
|
|
||||||
if blocks[0][0] in '-123456789':
|
|
||||||
if blocks[1] == '':
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
offset = int(blocks[0])
|
|
||||||
return SyntacticReference(offset, blocks[1])
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
form = blocks[1].replace(' ', '')
|
|
||||||
return EntityReference(blocks[0], form)
|
|
|
@ -1,140 +0,0 @@
|
||||||
''' Reference resolution API. '''
|
|
||||||
import re
|
|
||||||
from typing import cast, Optional
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
from .rumodel import split_grams
|
|
||||||
|
|
||||||
from .conceptapi import inflect_dependant
|
|
||||||
from .context import TermContext
|
|
||||||
from .reference import EntityReference, SyntacticReference, parse_reference, Reference
|
|
||||||
|
|
||||||
|
|
||||||
_REF_ENTITY_PATTERN = re.compile(r'@{([^0-9\-][^\}\|\{]*?)\|([^\}\|\{]*?)}')
|
|
||||||
|
|
||||||
|
|
||||||
def extract_entities(text: str) -> list[str]:
|
|
||||||
''' Extract list of entities that are referenced. '''
|
|
||||||
result: list[str] = []
|
|
||||||
for segment in re.finditer(_REF_ENTITY_PATTERN, text):
|
|
||||||
entity = segment.group(1)
|
|
||||||
if entity not in result:
|
|
||||||
result.append(entity)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_entity(ref: EntityReference, context: TermContext) -> str:
|
|
||||||
''' Resolve entity reference. '''
|
|
||||||
alias = ref.entity
|
|
||||||
if alias not in context:
|
|
||||||
return f'!Неизвестная сущность: {alias}!'
|
|
||||||
grams = split_grams(ref.form)
|
|
||||||
resolved = context[alias].get_form(grams)
|
|
||||||
if resolved == '':
|
|
||||||
return f'!Отсутствует термин: {alias}!'
|
|
||||||
else:
|
|
||||||
return resolved
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_syntactic(ref: SyntacticReference, index: int, references: list['ResolvedReference']) -> str:
|
|
||||||
''' Resolve syntactic reference. '''
|
|
||||||
offset = ref.offset
|
|
||||||
master: Optional['ResolvedReference'] = None
|
|
||||||
if offset > 0:
|
|
||||||
index += 1
|
|
||||||
while index < len(references):
|
|
||||||
if isinstance(references[index].ref, EntityReference):
|
|
||||||
if offset == 1:
|
|
||||||
master = references[index]
|
|
||||||
else:
|
|
||||||
offset -= 1
|
|
||||||
index += 1
|
|
||||||
else:
|
|
||||||
index -= 1
|
|
||||||
while index >= 0:
|
|
||||||
if isinstance(references[index].ref, EntityReference):
|
|
||||||
if offset == -1:
|
|
||||||
master = references[index]
|
|
||||||
else:
|
|
||||||
offset += 1
|
|
||||||
index -= 1
|
|
||||||
if master is None:
|
|
||||||
return f'!Некорректное смещение: {ref.offset}!'
|
|
||||||
return inflect_dependant(ref.nominal, master.resolved)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Position:
|
|
||||||
''' 0-indexed contiguous segment position in text. '''
|
|
||||||
start: int = 0
|
|
||||||
finish: int = 0
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
return hash((self.start, self.finish))
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ResolvedReference:
|
|
||||||
''' Resolved reference data '''
|
|
||||||
ref: Reference
|
|
||||||
resolved: str = ''
|
|
||||||
pos_input: Position = Position()
|
|
||||||
pos_output: Position = Position()
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
return hash((self.resolved, self.pos_input, self.pos_output, self.ref.to_text()))
|
|
||||||
|
|
||||||
|
|
||||||
class Resolver:
|
|
||||||
''' Text reference resolver '''
|
|
||||||
REFERENCE_PATTERN = re.compile(r'@{[^\}\{]*?}')
|
|
||||||
|
|
||||||
def __init__(self, context: TermContext):
|
|
||||||
self.context = context
|
|
||||||
self.refs = cast(list[ResolvedReference], [])
|
|
||||||
self.input = ''
|
|
||||||
self.output = ''
|
|
||||||
|
|
||||||
def resolve(self, text: str) -> str:
|
|
||||||
''' Resolve references in input text.
|
|
||||||
Note: data on references positions is accessed through class attributes '''
|
|
||||||
self._reset(text)
|
|
||||||
self._parse_refs()
|
|
||||||
if len(self.refs) == 0:
|
|
||||||
self.output = self.input
|
|
||||||
return self.output
|
|
||||||
else:
|
|
||||||
self._resolve_refs()
|
|
||||||
self._combine_output()
|
|
||||||
return self.output
|
|
||||||
|
|
||||||
def _reset(self, input_text: str):
|
|
||||||
self.refs = cast(list[ResolvedReference], [])
|
|
||||||
self.input = input_text
|
|
||||||
self.output = ''
|
|
||||||
|
|
||||||
def _parse_refs(self):
|
|
||||||
for segment in re.finditer(Resolver.REFERENCE_PATTERN, self.input):
|
|
||||||
parse = parse_reference(segment[0])
|
|
||||||
if parse is not None:
|
|
||||||
ref_info = ResolvedReference(ref=parse,
|
|
||||||
resolved='',
|
|
||||||
pos_input=Position(segment.start(0), segment.end(0)))
|
|
||||||
self.refs.append(ref_info)
|
|
||||||
|
|
||||||
def _resolve_refs(self):
|
|
||||||
for ref in self.refs:
|
|
||||||
if isinstance(ref.ref, EntityReference):
|
|
||||||
ref.resolved = resolve_entity(ref.ref, self.context)
|
|
||||||
for (index, ref) in enumerate(self.refs):
|
|
||||||
if isinstance(ref.ref, SyntacticReference):
|
|
||||||
ref.resolved = resolve_syntactic(ref.ref, index, self.refs)
|
|
||||||
|
|
||||||
def _combine_output(self):
|
|
||||||
pos_in = 0
|
|
||||||
for ref in self.refs:
|
|
||||||
self.output += self.input[pos_in : ref.pos_input.start]
|
|
||||||
self.output += ref.resolved
|
|
||||||
ref.pos_output = Position(len(self.output) - len(ref.resolved), len(self.output))
|
|
||||||
pos_in = ref.pos_input.finish
|
|
||||||
self.output += self.input[pos_in : len(self.input)]
|
|
|
@ -1,118 +0,0 @@
|
||||||
''' Russian language models. '''
|
|
||||||
from __future__ import annotations
|
|
||||||
from enum import Enum, unique
|
|
||||||
from typing import Iterable, Optional
|
|
||||||
|
|
||||||
from pymorphy3 import MorphAnalyzer
|
|
||||||
from pymorphy3.tagset import OpencorporaTag as WordTag
|
|
||||||
|
|
||||||
# ''' Morphology parser. '''
|
|
||||||
morpho = MorphAnalyzer()
|
|
||||||
Grammemes = Iterable[str]
|
|
||||||
|
|
||||||
|
|
||||||
def split_grams(text: str) -> list[str]:
|
|
||||||
''' Split grammemes string into set of items. '''
|
|
||||||
return [tag.strip() for tag in filter(None, text.split(','))]
|
|
||||||
|
|
||||||
|
|
||||||
def combine_grams(tags: Iterable[str]) -> str:
|
|
||||||
''' Combine grammemes into string. '''
|
|
||||||
return ','.join(tags)
|
|
||||||
|
|
||||||
|
|
||||||
@unique
|
|
||||||
class SemanticRole(Enum):
|
|
||||||
''' Enumerating semantic types for different parse patterns. '''
|
|
||||||
unknwn = 0
|
|
||||||
term = 1
|
|
||||||
action = 2
|
|
||||||
definition = 3
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_POS(pos: Optional[str]) -> SemanticRole:
|
|
||||||
''' Production method: types from part of speech. '''
|
|
||||||
if pos in ['NOUN', 'NPRO']:
|
|
||||||
return SemanticRole.term
|
|
||||||
elif pos in ['VERB', 'INFN', 'PRTF', 'PRTS']:
|
|
||||||
return SemanticRole.action
|
|
||||||
elif pos in ['ADJF', 'ADJS']:
|
|
||||||
return SemanticRole.definition
|
|
||||||
return SemanticRole.unknwn
|
|
||||||
|
|
||||||
|
|
||||||
class Morphology:
|
|
||||||
''' Wrapper for OpencorporaTag expanding functionality for multiword.
|
|
||||||
Full morphology tags see http://opencorpora.org/dict.php?act=gram
|
|
||||||
'''
|
|
||||||
def __init__(self, tag: WordTag, semantic=SemanticRole.unknwn):
|
|
||||||
self.tag = tag
|
|
||||||
self.semantic = semantic if semantic != SemanticRole.unknwn else SemanticRole.from_POS(tag.POS)
|
|
||||||
|
|
||||||
_TAGS_IMMUTABLE = frozenset(['INFN', 'ADVB', 'COMP', 'PNCT', 'PREP', 'CONJ', 'PRCL', 'INTJ'])
|
|
||||||
|
|
||||||
_TAGS_NO_TENSE = frozenset(['NOUN', 'NPRO', 'ADJF', 'ADJS'])
|
|
||||||
_TAGS_NO_CASE = frozenset(['GRND', 'VERB', 'ADJS', 'PRTS'])
|
|
||||||
_TAGS_NO_NUMBER = frozenset(['GRND'])
|
|
||||||
_TAGS_NO_GENDER = frozenset(['GRND', 'NOUN', 'NPRO', 'plur'])
|
|
||||||
_TAGS_NO_PERSON = frozenset(['GRND', 'NOUN', 'ADJF', 'ADJS', 'PRTF', 'PRTS', 'past'])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def can_coordinate(self) -> bool:
|
|
||||||
''' Check if coordination can change text. '''
|
|
||||||
return self.tag.POS in ['NOUN', 'NPRO', 'NUMR', 'ADJF', 'ADJS', 'PRTF', 'PRTS']
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_dependable(pos: str):
|
|
||||||
''' Check if this morphology can be dependant. '''
|
|
||||||
return pos in ['ADJF', 'ADJS', 'PRTF', 'PRTS']
|
|
||||||
|
|
||||||
@property
|
|
||||||
def effective_POS(self) -> Optional[str]:
|
|
||||||
''' Access part of speech. Pronouns are considered as nouns '''
|
|
||||||
pos: Optional[str] = self.tag.POS
|
|
||||||
if pos and self.tag.POS == 'NPRO':
|
|
||||||
return 'NOUN'
|
|
||||||
return pos
|
|
||||||
|
|
||||||
def complete_grams(self, grams: Iterable[str]) -> set[str]:
|
|
||||||
''' Add missing tags before inflection. '''
|
|
||||||
result = set(grams)
|
|
||||||
pos = self.tag.POS
|
|
||||||
if pos and result.isdisjoint(WordTag.PARTS_OF_SPEECH):
|
|
||||||
result.add(pos if pos != 'INFN' or len(result) == 0 else 'VERB')
|
|
||||||
if not result.isdisjoint(self._TAGS_IMMUTABLE):
|
|
||||||
return result
|
|
||||||
if self.tag.case and result.isdisjoint(WordTag.CASES) and result.isdisjoint(self._TAGS_NO_CASE):
|
|
||||||
result.add(self.tag.case)
|
|
||||||
if self.tag.tense and result.isdisjoint(WordTag.TENSES) and result.isdisjoint(self._TAGS_NO_TENSE):
|
|
||||||
if (self.tag.tense != 'past' or result.isdisjoint(WordTag.PERSONS)) \
|
|
||||||
and (self.tag.tense != 'pres' or result.isdisjoint(WordTag.GENDERS)):
|
|
||||||
result.add(self.tag.tense)
|
|
||||||
if self.tag.number and result.isdisjoint(WordTag.NUMBERS) and result.isdisjoint(self._TAGS_NO_NUMBER):
|
|
||||||
if self.tag.number != 'plur' or result.isdisjoint(WordTag.GENDERS):
|
|
||||||
result.add(self.tag.number)
|
|
||||||
if self.tag.gender and result.isdisjoint(WordTag.GENDERS) and result.isdisjoint(self._TAGS_NO_GENDER):
|
|
||||||
if 'PRTF' in result or 'pres' not in result:
|
|
||||||
result.add(self.tag.gender)
|
|
||||||
if self.tag.person and result.isdisjoint(WordTag.PERSONS) and result.isdisjoint(self._TAGS_NO_PERSON):
|
|
||||||
result.add(self.tag.person)
|
|
||||||
if 'plur' in result and not result.isdisjoint(WordTag.GENDERS):
|
|
||||||
result = result.difference(WordTag.GENDERS)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def coordination_grams(self) -> set[str]:
|
|
||||||
''' Return set of grammemes for inflection to keep coordination . '''
|
|
||||||
result = set()
|
|
||||||
if self.tag.case:
|
|
||||||
result.add(self.tag.case)
|
|
||||||
if self.tag:
|
|
||||||
number = self.tag.number
|
|
||||||
result.add(number)
|
|
||||||
if self.tag.gender and 'plur' not in result:
|
|
||||||
result.add(self.tag.gender)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def to_text(self) -> str:
|
|
||||||
''' Produce string of all grammemes. '''
|
|
||||||
return combine_grams(self.tag.grammemes)
|
|
|
@ -1,486 +0,0 @@
|
||||||
''' Parsing russian language using pymorphy3 and natasha libraries. '''
|
|
||||||
from __future__ import annotations
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from razdel.substring import Substring as Segment
|
|
||||||
from pymorphy3.analyzer import Parse as WordParse
|
|
||||||
|
|
||||||
from .syntax import RuSyntax, Capitalization
|
|
||||||
from .rumodel import SemanticRole, Morphology, WordTag, morpho, Grammemes
|
|
||||||
|
|
||||||
INDEX_NONE = -1
|
|
||||||
NO_COORDINATION = -1
|
|
||||||
WORD_NONE = -1
|
|
||||||
|
|
||||||
|
|
||||||
class WordToken:
|
|
||||||
''' Atomic text token. '''
|
|
||||||
def __init__(self, segment: Segment, parse: list[WordParse], main_parse: int = 0):
|
|
||||||
self.segment: Segment = segment
|
|
||||||
self.forms: list[WordParse] = parse
|
|
||||||
self.main: int = main_parse
|
|
||||||
|
|
||||||
def get_morpho(self) -> Morphology:
|
|
||||||
''' Return morphology for current token. '''
|
|
||||||
return Morphology(self.get_parse().tag)
|
|
||||||
|
|
||||||
def get_parse(self) -> WordParse:
|
|
||||||
''' Access main form. '''
|
|
||||||
return self.forms[self.main]
|
|
||||||
|
|
||||||
def inflect(self, inflection_grams: set[str]) -> Optional[WordParse]:
|
|
||||||
''' Apply inflection to segment text. Does not modify forms '''
|
|
||||||
inflected = self.get_parse().inflect(inflection_grams)
|
|
||||||
if not inflected:
|
|
||||||
return None
|
|
||||||
self.segment.text = Capitalization.from_text(self.segment.text).apply_to(inflected.word)
|
|
||||||
return inflected
|
|
||||||
|
|
||||||
|
|
||||||
class Collation:
|
|
||||||
''' Parsed data for input coordinated text. '''
|
|
||||||
def __init__(self, text: str):
|
|
||||||
self.text = text
|
|
||||||
self.words: list[WordToken] = []
|
|
||||||
self.coordination: list[int] = []
|
|
||||||
self.main_word: int = WORD_NONE
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
''' Check if data is parsed correctly '''
|
|
||||||
return self.main_word != WORD_NONE
|
|
||||||
|
|
||||||
def get_form(self) -> WordParse:
|
|
||||||
''' Access WordParse. '''
|
|
||||||
return self.words[self.main_word].get_parse()
|
|
||||||
|
|
||||||
def get_morpho(self) -> Morphology:
|
|
||||||
''' Access parsed main morphology. '''
|
|
||||||
return self.words[self.main_word].get_morpho()
|
|
||||||
|
|
||||||
def add_word(self, segment, forms: list, main_form: int, need_coordination: bool = True):
|
|
||||||
''' Add word information. '''
|
|
||||||
self.words.append(WordToken(segment, forms, main_form))
|
|
||||||
self.coordination.append(NO_COORDINATION if not need_coordination else 0)
|
|
||||||
|
|
||||||
def inflect(self, target_grams: Grammemes) -> str:
|
|
||||||
''' Inflect text to match required tags. '''
|
|
||||||
if self.is_valid():
|
|
||||||
origin = self.get_morpho()
|
|
||||||
if not origin.tag.grammemes.issuperset(target_grams):
|
|
||||||
if self._apply_inflection(origin, target_grams):
|
|
||||||
return self._generate_text()
|
|
||||||
return self.text
|
|
||||||
|
|
||||||
def inflect_like(self, base_model: Collation) -> str:
|
|
||||||
''' Create inflection to substitute base_model form. '''
|
|
||||||
if self.is_valid():
|
|
||||||
morph = base_model.get_morpho()
|
|
||||||
if morph.effective_POS:
|
|
||||||
tags = set()
|
|
||||||
tags.add(morph.effective_POS)
|
|
||||||
tags = morph.complete_grams(tags)
|
|
||||||
return self.inflect(tags)
|
|
||||||
return self.text
|
|
||||||
|
|
||||||
def inflect_dependant(self, master_model: Collation) -> str:
|
|
||||||
''' Create inflection to coordinate with master_model form. '''
|
|
||||||
assert self.is_valid()
|
|
||||||
morph = master_model.get_morpho()
|
|
||||||
tags = morph.coordination_grams()
|
|
||||||
tags = self.get_morpho().complete_grams(tags)
|
|
||||||
return self.inflect(tags)
|
|
||||||
|
|
||||||
def normal_form(self) -> str:
|
|
||||||
''' Generate normal form. '''
|
|
||||||
if self.is_valid():
|
|
||||||
main_form = self.get_form()
|
|
||||||
new_morpho = Morphology(main_form.normalized.tag)
|
|
||||||
new_grams = new_morpho.complete_grams(frozenset())
|
|
||||||
return self.inflect(new_grams)
|
|
||||||
return self.text
|
|
||||||
|
|
||||||
def _iterate_coordinated(self):
|
|
||||||
words_count = len(self.words)
|
|
||||||
current_word = self.coordination[words_count]
|
|
||||||
while current_word != words_count:
|
|
||||||
yield self.words[current_word]
|
|
||||||
current_word += self.coordination[current_word]
|
|
||||||
|
|
||||||
def _inflect_main_word(self, origin: Morphology, target_grams: Grammemes) -> Optional[Morphology]:
|
|
||||||
full_grams = origin.complete_grams(target_grams)
|
|
||||||
inflected = self.words[self.main_word].inflect(full_grams)
|
|
||||||
if not inflected:
|
|
||||||
return None
|
|
||||||
return Morphology(inflected.tag)
|
|
||||||
|
|
||||||
def _apply_inflection(self, origin: Morphology, target_grams: Grammemes) -> bool:
|
|
||||||
new_moprho = self._inflect_main_word(origin, target_grams)
|
|
||||||
if not new_moprho:
|
|
||||||
return False
|
|
||||||
inflection_grams = new_moprho.coordination_grams()
|
|
||||||
if len(inflection_grams) == 0:
|
|
||||||
return True
|
|
||||||
for word in self._iterate_coordinated():
|
|
||||||
word.inflect(inflection_grams)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _generate_text(self) -> str:
|
|
||||||
current_pos = 0
|
|
||||||
result = ''
|
|
||||||
for token in self.words:
|
|
||||||
if token.segment.start > current_pos:
|
|
||||||
result += self.text[current_pos: token.segment.start]
|
|
||||||
result += token.segment.text
|
|
||||||
current_pos = token.segment.stop
|
|
||||||
if current_pos + 1 < len(self.text):
|
|
||||||
result += self.text[current_pos:]
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class PhraseParser:
|
|
||||||
''' Russian grammar parser. '''
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
_FILTER_SCORE = 0.005
|
|
||||||
_SINGLE_SCORE_SEARCH = 0.2
|
|
||||||
_PRIORITY_NONE = NO_COORDINATION
|
|
||||||
|
|
||||||
_MAIN_WAIT_LIMIT = 10 # count words until fixing main
|
|
||||||
_MAIN_MAX_FOLLOWERS = 3 # count words after main as coordination candidates
|
|
||||||
|
|
||||||
def parse(self, text: str,
|
|
||||||
require_index: int = INDEX_NONE,
|
|
||||||
require_grams: Optional[Grammemes] = None) -> Optional[Collation]:
|
|
||||||
'''
|
|
||||||
Determine morpho tags for input text.
|
|
||||||
::returns:: Morphology of a text or None if no suitable form is available
|
|
||||||
'''
|
|
||||||
segments = list(RuSyntax.tokenize(text))
|
|
||||||
if len(segments) == 0:
|
|
||||||
return None
|
|
||||||
elif len(segments) == 1:
|
|
||||||
return self._parse_single(segments[0], require_index, require_grams)
|
|
||||||
else:
|
|
||||||
return self._parse_multiword(text, segments, require_index, require_grams)
|
|
||||||
|
|
||||||
def normalize(self, text: str):
|
|
||||||
''' Get normal form for target text. '''
|
|
||||||
processed = self.parse(text)
|
|
||||||
if processed:
|
|
||||||
return processed.normal_form()
|
|
||||||
return text
|
|
||||||
|
|
||||||
def find_substr(self, text: str, sub: str) -> tuple[int, int]:
|
|
||||||
''' Search for substring position in text regardless of morphology. '''
|
|
||||||
if not text or not sub:
|
|
||||||
return (0, 0)
|
|
||||||
query = [self.normalize(elem.text) for elem in RuSyntax.tokenize(sub)]
|
|
||||||
query_len = len(query)
|
|
||||||
start = 0
|
|
||||||
current_index = 0
|
|
||||||
for token in RuSyntax.tokenize(text):
|
|
||||||
text_word = self.normalize(token.text)
|
|
||||||
if text_word != query[current_index]:
|
|
||||||
current_index = 0
|
|
||||||
else:
|
|
||||||
if current_index == 0:
|
|
||||||
start = token.start
|
|
||||||
current_index += 1
|
|
||||||
if current_index == query_len:
|
|
||||||
return (start, token.stop)
|
|
||||||
return (0, 0)
|
|
||||||
|
|
||||||
def inflect_context(self, text: str, before: str = '', after: str = '') -> str:
|
|
||||||
''' Inflect text in accordance to context before and after. '''
|
|
||||||
target = self.parse(text)
|
|
||||||
if not target:
|
|
||||||
return text
|
|
||||||
target_morpho = target.get_morpho()
|
|
||||||
if not target_morpho or not target_morpho.can_coordinate:
|
|
||||||
return text
|
|
||||||
|
|
||||||
model_after = self.parse(after)
|
|
||||||
model_before = self.parse(before)
|
|
||||||
etalon = PhraseParser._choose_context_etalon(target_morpho, model_before, model_after)
|
|
||||||
if not etalon:
|
|
||||||
return text
|
|
||||||
etalon_moprho = etalon.get_morpho()
|
|
||||||
if not etalon_moprho.can_coordinate:
|
|
||||||
return text
|
|
||||||
|
|
||||||
new_form = PhraseParser._combine_morpho(target_morpho, etalon_moprho.tag)
|
|
||||||
return target.inflect(new_form)
|
|
||||||
|
|
||||||
def inflect_substitute(self, substitute_normal: str, original: str) -> str:
|
|
||||||
''' Inflect substitute to match original form. '''
|
|
||||||
original_model = self.parse(original)
|
|
||||||
if not original_model:
|
|
||||||
return substitute_normal
|
|
||||||
substitute_model = self.parse(substitute_normal)
|
|
||||||
if not substitute_model:
|
|
||||||
return substitute_normal
|
|
||||||
return substitute_model.inflect_like(original_model)
|
|
||||||
|
|
||||||
def inflect_dependant(self, dependant_normal: str, master: str) -> str:
|
|
||||||
''' Inflect dependant to coordinate with master text. '''
|
|
||||||
master_model = self.parse(master)
|
|
||||||
if not master_model:
|
|
||||||
return dependant_normal
|
|
||||||
dependant_model = self.parse(dependant_normal)
|
|
||||||
if not dependant_model:
|
|
||||||
return dependant_normal
|
|
||||||
return dependant_model.inflect_dependant(master_model)
|
|
||||||
|
|
||||||
def _parse_single(self, segment, require_index: int, require_grams: Optional[Grammemes]) -> Optional[Collation]:
|
|
||||||
forms = list(self._filtered_parse(segment.text))
|
|
||||||
parse_index = INDEX_NONE
|
|
||||||
if len(forms) == 0 or require_index >= len(forms):
|
|
||||||
return None
|
|
||||||
|
|
||||||
if require_index != INDEX_NONE:
|
|
||||||
tags = forms[require_index].tag
|
|
||||||
if require_grams and not tags.grammemes.issuperset(require_grams):
|
|
||||||
return None
|
|
||||||
parse_index = require_index
|
|
||||||
else:
|
|
||||||
current_score = 0
|
|
||||||
for (index, form) in enumerate(forms):
|
|
||||||
if not require_grams or form.tag.grammemes.issuperset(require_grams):
|
|
||||||
if form.tag.case == 'nomn':
|
|
||||||
parse_index = index
|
|
||||||
break
|
|
||||||
elif parse_index == INDEX_NONE:
|
|
||||||
current_score = form.score
|
|
||||||
parse_index = index
|
|
||||||
elif form.score / current_score < self._SINGLE_SCORE_SEARCH:
|
|
||||||
break
|
|
||||||
|
|
||||||
if parse_index == INDEX_NONE:
|
|
||||||
return None
|
|
||||||
result = Collation(segment.text)
|
|
||||||
result.add_word(segment, [forms[parse_index]], main_form=0, need_coordination=False)
|
|
||||||
result.coordination.append(len(result.words))
|
|
||||||
result.main_word = 0
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _parse_multiword(self, text: str, segments: list, require_index: int,
|
|
||||||
require_grams: Optional[Grammemes]) -> Optional[Collation]:
|
|
||||||
result = Collation(text)
|
|
||||||
priority_main: float = self._PRIORITY_NONE
|
|
||||||
segment_index = 0
|
|
||||||
main_wait = 0
|
|
||||||
word_index = 0
|
|
||||||
for segment in segments:
|
|
||||||
if main_wait > PhraseParser._MAIN_WAIT_LIMIT:
|
|
||||||
break
|
|
||||||
segment_index += 1
|
|
||||||
priority = self._parse_segment(result, segment, require_index, require_grams)
|
|
||||||
if priority is None:
|
|
||||||
continue # skip non-parsable entities
|
|
||||||
main_wait += 1
|
|
||||||
if priority > priority_main:
|
|
||||||
result.main_word = word_index
|
|
||||||
priority_main = priority
|
|
||||||
word_index += 1
|
|
||||||
if result.main_word == INDEX_NONE:
|
|
||||||
return None
|
|
||||||
self._finalize_coordination(result)
|
|
||||||
if segment_index < len(segments):
|
|
||||||
pass # finish to parse segments after main if needed
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _parse_segment(self,
|
|
||||||
output: Collation,
|
|
||||||
segment: Segment,
|
|
||||||
require_index: int,
|
|
||||||
require_grams: Optional[Grammemes]) -> Optional[float]:
|
|
||||||
''' Return priority for this can be a new main word '''
|
|
||||||
forms = list(self._filtered_parse(segment.text))
|
|
||||||
if len(forms) == 0:
|
|
||||||
return None
|
|
||||||
main_index: int = INDEX_NONE
|
|
||||||
segment_score: float = self._PRIORITY_NONE
|
|
||||||
needs_coordination = False
|
|
||||||
local_sum: float = 0
|
|
||||||
score_sum: float = 0
|
|
||||||
if require_index != INDEX_NONE:
|
|
||||||
form = forms[require_index]
|
|
||||||
if not require_grams or form.tag.grammemes.issuperset(require_grams):
|
|
||||||
(local_max, segment_score) = PhraseParser._get_priorities_for(form.tag)
|
|
||||||
main_index = require_index
|
|
||||||
needs_coordination = Morphology.is_dependable(form.tag.POS)
|
|
||||||
else:
|
|
||||||
local_max = self._PRIORITY_NONE
|
|
||||||
for (index, form) in enumerate(forms):
|
|
||||||
if require_grams and not form.tag.grammemes.issuperset(require_grams):
|
|
||||||
continue
|
|
||||||
(local_priority, global_priority) = PhraseParser._get_priorities_for(form.tag)
|
|
||||||
needs_coordination = needs_coordination or Morphology.is_dependable(form.tag.POS)
|
|
||||||
local_sum += global_priority * form.score
|
|
||||||
score_sum += form.score
|
|
||||||
if local_priority > local_max:
|
|
||||||
local_max = local_priority
|
|
||||||
# segment_score = global_priority
|
|
||||||
main_index = index
|
|
||||||
if score_sum == 0:
|
|
||||||
return None
|
|
||||||
segment_score = local_sum / score_sum
|
|
||||||
output.add_word(segment, forms, main_index, needs_coordination)
|
|
||||||
return segment_score
|
|
||||||
# Alternative: return segment_score
|
|
||||||
# penalty_suspicious = 0 if local_max == 0 else (1 - local_sum / local_max) * self._PRIORITY_PENALTY
|
|
||||||
# return segment_score - penalty_suspicious
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _finalize_coordination(cls, target: Collation):
|
|
||||||
main_morpho: Morphology = target.get_morpho()
|
|
||||||
main_coordinate = main_morpho.can_coordinate
|
|
||||||
target.coordination[target.main_word] = NO_COORDINATION
|
|
||||||
first_change = INDEX_NONE
|
|
||||||
current_len = 0
|
|
||||||
for (index, word) in enumerate(target.words):
|
|
||||||
if target.coordination[index] == NO_COORDINATION or index - target.main_word > cls._MAIN_MAX_FOLLOWERS:
|
|
||||||
needs_change = False
|
|
||||||
if index != target.main_word:
|
|
||||||
word.main = INDEX_NONE
|
|
||||||
else:
|
|
||||||
word.main = PhraseParser._find_coordination(word.forms, main_morpho.tag, index < target.main_word)
|
|
||||||
needs_change = word.main != INDEX_NONE
|
|
||||||
if not needs_change or not main_coordinate:
|
|
||||||
target.coordination[index] = NO_COORDINATION
|
|
||||||
current_len += 1
|
|
||||||
if needs_change and main_coordinate:
|
|
||||||
target.coordination[index] = current_len
|
|
||||||
current_len = 0
|
|
||||||
if first_change == INDEX_NONE:
|
|
||||||
first_change = index
|
|
||||||
if first_change == INDEX_NONE:
|
|
||||||
target.coordination.append(len(target.words))
|
|
||||||
return
|
|
||||||
previous_reference = first_change
|
|
||||||
current_word = len(target.words)
|
|
||||||
target.coordination.append(current_len + 1)
|
|
||||||
while target.coordination[current_word] != INDEX_NONE:
|
|
||||||
previous_word = current_word - target.coordination[current_word]
|
|
||||||
target.coordination[current_word] = previous_reference
|
|
||||||
previous_reference = current_word - previous_word
|
|
||||||
current_word = previous_word
|
|
||||||
if previous_reference == 0 or current_word < 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _find_coordination(forms: list, main_tag: WordTag, before_main: bool) -> int:
|
|
||||||
for (index, form) in enumerate(forms):
|
|
||||||
pos = form.tag.POS
|
|
||||||
case = form.tag.case
|
|
||||||
if pos not in ['ADJF', 'ADJS', 'PRTF', 'PRTS']:
|
|
||||||
continue
|
|
||||||
if SemanticRole.from_POS(pos) == SemanticRole.term and case == 'gent':
|
|
||||||
if before_main:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
return INDEX_NONE
|
|
||||||
if case == main_tag.case:
|
|
||||||
return index
|
|
||||||
elif main_tag.case in ['accs', 'gent'] and case in ['accs', 'gent']:
|
|
||||||
return index
|
|
||||||
return INDEX_NONE
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _filtered_parse(text: str):
|
|
||||||
capital = Capitalization.from_text(text)
|
|
||||||
score_filter = PhraseParser._filter_score(morpho.parse(text))
|
|
||||||
yield from PhraseParser._filter_capital(score_filter, capital)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _filter_score(generator):
|
|
||||||
for form in generator:
|
|
||||||
if form.score < PhraseParser._FILTER_SCORE:
|
|
||||||
break
|
|
||||||
yield form
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _filter_capital(generator, capital: Capitalization):
|
|
||||||
if capital in [Capitalization.upper_case, Capitalization.mixed]:
|
|
||||||
for form in generator:
|
|
||||||
if 'Abbr' not in form.tag.grammemes:
|
|
||||||
continue
|
|
||||||
yield form
|
|
||||||
else:
|
|
||||||
yield from generator
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_word(text: str, require_index: int = INDEX_NONE,
|
|
||||||
require_grams: Optional[Grammemes] = None) -> Optional[Morphology]:
|
|
||||||
parsed_variants = morpho.parse(text)
|
|
||||||
if not parsed_variants or require_index >= len(parsed_variants):
|
|
||||||
return None
|
|
||||||
if require_index != INDEX_NONE:
|
|
||||||
tags = parsed_variants[require_index].tag
|
|
||||||
if not require_grams or tags.grammemes.issuperset(require_grams):
|
|
||||||
return Morphology(tags)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
for variant in parsed_variants:
|
|
||||||
tags = variant.tag
|
|
||||||
if not require_grams or tags.grammemes.issuperset(require_grams):
|
|
||||||
return Morphology(tags)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_priorities_for(tag: WordTag) -> tuple[float, float]:
|
|
||||||
''' Return pair of local and global priorities. '''
|
|
||||||
if tag.POS in ['VERB', 'INFN']:
|
|
||||||
return (9, 10)
|
|
||||||
if tag.POS in ['NOUN', 'NPRO']:
|
|
||||||
return (10, 9) if 'nomn' in tag.grammemes and 'Fixd' not in tag.grammemes else (8, 8)
|
|
||||||
if tag.POS in ['PRTF', 'PRTS']:
|
|
||||||
return (6, 6)
|
|
||||||
if tag.POS in ['ADJF', 'ADJS']:
|
|
||||||
return (5, 5)
|
|
||||||
if tag.POS == 'ADVB':
|
|
||||||
return (7, 4)
|
|
||||||
return (0, 0)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _choose_context_etalon(target: Morphology,
|
|
||||||
before: Optional[Collation],
|
|
||||||
after: Optional[Collation]) -> Optional[Collation]:
|
|
||||||
if not before or not before.get_morpho().can_coordinate:
|
|
||||||
return after
|
|
||||||
if not after or not after.get_morpho().can_coordinate:
|
|
||||||
return before
|
|
||||||
|
|
||||||
before_semantic = before.get_morpho().semantic
|
|
||||||
after_semantic = after.get_morpho().semantic
|
|
||||||
if target.semantic == SemanticRole.definition:
|
|
||||||
if after_semantic == SemanticRole.term:
|
|
||||||
return after
|
|
||||||
if before_semantic == SemanticRole.term:
|
|
||||||
return before
|
|
||||||
if before_semantic == SemanticRole.definition:
|
|
||||||
return before
|
|
||||||
return after
|
|
||||||
|
|
||||||
if target.semantic == SemanticRole.term:
|
|
||||||
if before_semantic == SemanticRole.definition:
|
|
||||||
return before
|
|
||||||
if after_semantic == SemanticRole.definition:
|
|
||||||
return after
|
|
||||||
|
|
||||||
return before
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _combine_morpho(target: Morphology, etalon: WordTag) -> frozenset[str]:
|
|
||||||
part_of_speech = target.tag.POS
|
|
||||||
number = etalon.number
|
|
||||||
if number == 'plur':
|
|
||||||
return frozenset([part_of_speech, number, etalon.case])
|
|
||||||
else:
|
|
||||||
gender = etalon.gender if target.semantic != SemanticRole.term else target.tag.gender
|
|
||||||
return frozenset([part_of_speech, number, gender, etalon.case])
|
|
|
@ -1,87 +0,0 @@
|
||||||
''' Russian language syntax incapsulation. '''
|
|
||||||
from __future__ import annotations
|
|
||||||
from enum import Enum, unique
|
|
||||||
|
|
||||||
from razdel import tokenize
|
|
||||||
|
|
||||||
|
|
||||||
@unique
|
|
||||||
class Capitalization(Enum):
|
|
||||||
''' Enumerating capitalization types. '''
|
|
||||||
unknwn = 0
|
|
||||||
lower_case = 1
|
|
||||||
upper_case = 2
|
|
||||||
first_capital = 3
|
|
||||||
mixed = 4
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_text(text: str) -> Capitalization:
|
|
||||||
''' Fabric method to identify capitalization in text. '''
|
|
||||||
if len(text) == 0:
|
|
||||||
return Capitalization.unknwn
|
|
||||||
first_capital = Capitalization._is_capital(text[0])
|
|
||||||
has_mid_capital = False
|
|
||||||
has_lower = not first_capital
|
|
||||||
for symbol in text[1:]:
|
|
||||||
if Capitalization._is_capital(symbol):
|
|
||||||
if has_lower:
|
|
||||||
return Capitalization.mixed
|
|
||||||
has_mid_capital = True
|
|
||||||
else:
|
|
||||||
if has_mid_capital:
|
|
||||||
return Capitalization.mixed
|
|
||||||
else:
|
|
||||||
has_lower = True
|
|
||||||
if has_mid_capital:
|
|
||||||
return Capitalization.upper_case
|
|
||||||
elif first_capital:
|
|
||||||
return Capitalization.first_capital
|
|
||||||
else:
|
|
||||||
return Capitalization.lower_case
|
|
||||||
|
|
||||||
def apply_to(self, text: str) -> str:
|
|
||||||
''' Apply capitalization to text. '''
|
|
||||||
if not text or self in [Capitalization.unknwn, Capitalization.mixed]:
|
|
||||||
return text
|
|
||||||
elif self == Capitalization.lower_case:
|
|
||||||
return text.lower()
|
|
||||||
elif self == Capitalization.upper_case:
|
|
||||||
return text.upper()
|
|
||||||
else:
|
|
||||||
return text[0].upper() + text[1:]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_capital(symbol: str) -> bool:
|
|
||||||
return 'А' <= symbol <= 'Я' or 'A' <= symbol <= 'Z'
|
|
||||||
|
|
||||||
|
|
||||||
class RuSyntax:
|
|
||||||
''' Russian language syntax parser. '''
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_single_word(text: str) -> bool:
|
|
||||||
''' Test if text is a single word. '''
|
|
||||||
try:
|
|
||||||
gen = tokenize(text)
|
|
||||||
if next(gen) == '':
|
|
||||||
return True
|
|
||||||
if next(gen) == '':
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
except StopIteration:
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def tokenize(text: str):
|
|
||||||
''' Split text into words. Returns list[(start, stop, text)]. '''
|
|
||||||
return tokenize(text)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def split_words(text: str) -> list[str]:
|
|
||||||
''' Split text into words. '''
|
|
||||||
return [elem.text for elem in tokenize(text)]
|
|
|
@ -1,8 +0,0 @@
|
||||||
''' Tests. '''
|
|
||||||
from .t_reference import *
|
|
||||||
from .t_ruparser import *
|
|
||||||
from .t_syntax import *
|
|
||||||
from .t_conceptapi import *
|
|
||||||
from .t_rumodel import *
|
|
||||||
from .t_context import *
|
|
||||||
from .t_resolver import *
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,866 +0,0 @@
|
||||||
тектологические состояния
|
|
||||||
первые здания
|
|
||||||
доска
|
|
||||||
структуры
|
|
||||||
тектологические состояния
|
|
||||||
тектологические переходы
|
|
||||||
уникомплексные тектологические переходы
|
|
||||||
поликомплексные переходы
|
|
||||||
процессы
|
|
||||||
разрушаемые оружием структуры
|
|
||||||
создаваемые оружием структуры
|
|
||||||
неразрушаемые оружием структуры
|
|
||||||
несоздаваемые оружием структуры
|
|
||||||
формы движения
|
|
||||||
объекты
|
|
||||||
систенты
|
|
||||||
субъекты
|
|
||||||
системные классы
|
|
||||||
системные субъекты
|
|
||||||
системное сознание
|
|
||||||
системно познаные объекты
|
|
||||||
обсубъект
|
|
||||||
системно непознанные объекты
|
|
||||||
оценки
|
|
||||||
шкала оценок
|
|
||||||
альтернативы
|
|
||||||
критерии
|
|
||||||
сеть альтернатив
|
|
||||||
предъявление
|
|
||||||
субъект актуализации
|
|
||||||
основание
|
|
||||||
актуализированная альтернатива
|
|
||||||
цепи актуализаций
|
|
||||||
Сечения актуализации
|
|
||||||
комплексы
|
|
||||||
квазиконфликтанты
|
|
||||||
консистенции
|
|
||||||
квазиконфликтантные комплексы
|
|
||||||
коструктурный слой
|
|
||||||
коструктурное окружение
|
|
||||||
квазиконфликты
|
|
||||||
шкалы оценок квазиконфликтанта
|
|
||||||
ущемленная сторона конфликтного перехода
|
|
||||||
кооперативный переход
|
|
||||||
конфликты
|
|
||||||
консистенты
|
|
||||||
цепи нагнетания напряженности
|
|
||||||
состояние напряженности
|
|
||||||
конфликтное состояние
|
|
||||||
структурный дифференциал
|
|
||||||
конфликтоген
|
|
||||||
основание конфликта
|
|
||||||
конфликтанты
|
|
||||||
Уникомплексный переход
|
|
||||||
структурный дифференциал перехода
|
|
||||||
рабочий процесс оружия
|
|
||||||
приемы
|
|
||||||
натуральные числа
|
|
||||||
складские операции
|
|
||||||
группа организационных единиц
|
|
||||||
полные описания издений
|
|
||||||
разбиения
|
|
||||||
номера договорных поставок
|
|
||||||
номера приходных ордеров поставок
|
|
||||||
дни приходных ордеров поставок
|
|
||||||
дни
|
|
||||||
выбранное число
|
|
||||||
организационные единицы
|
|
||||||
изделия
|
|
||||||
операции
|
|
||||||
группа организаций
|
|
||||||
предприятия
|
|
||||||
организация
|
|
||||||
склад
|
|
||||||
отрезки времени
|
|
||||||
прошлые состояния склада
|
|
||||||
подразделения
|
|
||||||
поступления партий изделий
|
|
||||||
отпуск партий изделий
|
|
||||||
типы изделий
|
|
||||||
виды изделий
|
|
||||||
договорные поставки
|
|
||||||
поставки
|
|
||||||
начальные отрезки натуральных чисел
|
|
||||||
абсолютные нумерации разбиений
|
|
||||||
относительные нумерации разбиений
|
|
||||||
календари
|
|
||||||
солнечные календари
|
|
||||||
григорианские календари
|
|
||||||
квартальные григорианские календари
|
|
||||||
календарные годы
|
|
||||||
календарные кварталы
|
|
||||||
отрезки нумерации складских операций
|
|
||||||
поступление
|
|
||||||
отпуск
|
|
||||||
классы изделий
|
|
||||||
текущее состояние склада
|
|
||||||
спецификация
|
|
||||||
единицы учета, заданные спецификациями изделий
|
|
||||||
Единицы учета, заданные множествами изделий
|
|
||||||
единицы учета
|
|
||||||
прием партий изделий
|
|
||||||
отпуск изделий
|
|
||||||
характеристики
|
|
||||||
единицы измерения
|
|
||||||
числовые индексы
|
|
||||||
полные описания изделий
|
|
||||||
полные описания
|
|
||||||
самое мелкое разбиение
|
|
||||||
самое крупное разбиение
|
|
||||||
виды операций
|
|
||||||
Складские операции, выраженные в единицах учета
|
|
||||||
Поступления изделий, выраженные в единицах учета
|
|
||||||
Отпуск изделий, выраженный в единицах учета
|
|
||||||
високосные годы
|
|
||||||
невисокосные годы
|
|
||||||
годы
|
|
||||||
месяцы
|
|
||||||
кварталы
|
|
||||||
номера фондовых извещений
|
|
||||||
номера договоров
|
|
||||||
фондовые извещения
|
|
||||||
Нумерация складских операций
|
|
||||||
фонды
|
|
||||||
договора
|
|
||||||
дата
|
|
||||||
номер приходного ордера
|
|
||||||
поставки по техпомощи
|
|
||||||
Тезаурус кафедры КАиП:
|
|
||||||
Н-арное отношение
|
|
||||||
Н-местное отношение
|
|
||||||
R-интерпретация
|
|
||||||
Абельность
|
|
||||||
Абсолютное дополнение
|
|
||||||
Абстрагирование
|
|
||||||
Абстрактная система
|
|
||||||
Абстрактный процесс
|
|
||||||
Абстракция
|
|
||||||
Автоматизированная система проектирования систем организационного управления
|
|
||||||
Адаптивная система
|
|
||||||
Адекватность
|
|
||||||
Аксиома
|
|
||||||
Аксиома связности
|
|
||||||
Аксиома структуры
|
|
||||||
Аксиоматизация
|
|
||||||
Аксиоматика теории
|
|
||||||
Аксиоматическая теория
|
|
||||||
Аксиомы группы
|
|
||||||
Аксиомы синтеза
|
|
||||||
Актуальность
|
|
||||||
Алгебраическая структура
|
|
||||||
Алгебра конструктов
|
|
||||||
Альтернатива
|
|
||||||
Альтернативные методы
|
|
||||||
Анализ
|
|
||||||
Аналитическая теория
|
|
||||||
Аналогия
|
|
||||||
Аспект
|
|
||||||
Аспектная теория
|
|
||||||
АСП СОУ
|
|
||||||
АТ
|
|
||||||
Атрибут
|
|
||||||
Атрибутивная форма
|
|
||||||
Атрибутивное мышление
|
|
||||||
База данных
|
|
||||||
Базис множеств
|
|
||||||
Базисное множество
|
|
||||||
Библиотека моделей
|
|
||||||
Бинарная операция
|
|
||||||
Бинарное отношение
|
|
||||||
Богданов
|
|
||||||
Большая проекция
|
|
||||||
Боулдинг
|
|
||||||
Булеан
|
|
||||||
Бурбаки
|
|
||||||
Вершина
|
|
||||||
Взаимно дополнительные теории
|
|
||||||
Видообразование
|
|
||||||
Включение
|
|
||||||
Власть
|
|
||||||
Внешность предметной области
|
|
||||||
Внутренние термы ядра теории
|
|
||||||
Возможность
|
|
||||||
Восхождение
|
|
||||||
Вспомогательные базисные множества
|
|
||||||
Вторичная функция
|
|
||||||
Вторичный метод
|
|
||||||
Вход
|
|
||||||
Вход процесса
|
|
||||||
Выбор
|
|
||||||
Выразимость
|
|
||||||
Выразительная сила
|
|
||||||
Выразительные средства теории
|
|
||||||
Высказывание
|
|
||||||
Выход
|
|
||||||
Выход процесса
|
|
||||||
ГДМ
|
|
||||||
Генезис
|
|
||||||
Гипертеория
|
|
||||||
Гипотеза
|
|
||||||
Гипотетико-дедуктивные требования
|
|
||||||
Гипотетико-дедуктивный метод
|
|
||||||
Главная функция
|
|
||||||
Глубина проникновения
|
|
||||||
Гносеология
|
|
||||||
Гомоморфизм
|
|
||||||
ГП РС
|
|
||||||
Границы сети процессов
|
|
||||||
Граф
|
|
||||||
Граф термов
|
|
||||||
Группа
|
|
||||||
Двухместное отношение
|
|
||||||
Дебуленизация
|
|
||||||
Дедуктивная теория
|
|
||||||
Декартово произведение
|
|
||||||
Декомпозиция
|
|
||||||
Дескриптивное использование теории
|
|
||||||
Дескриптивный подход
|
|
||||||
Диаграмма Венна
|
|
||||||
Динамическая система
|
|
||||||
Дихотомия
|
|
||||||
Доказательство
|
|
||||||
Дуга
|
|
||||||
Желаемая система
|
|
||||||
Жизненный цикл
|
|
||||||
Жизненный цикл интереса
|
|
||||||
Жизненный цикл угрозы
|
|
||||||
Зависимое множество аксиом
|
|
||||||
Задача
|
|
||||||
Закон композиции
|
|
||||||
Знаковая система
|
|
||||||
Идентификация
|
|
||||||
Идеолог
|
|
||||||
Идея
|
|
||||||
Идея абстрактного выбора
|
|
||||||
Иерархическая структура
|
|
||||||
Измельчение графа термов
|
|
||||||
Изменение
|
|
||||||
Изоморфизм
|
|
||||||
Изофункциональные методы
|
|
||||||
Ильковский
|
|
||||||
Имплицитная форма
|
|
||||||
Интегрированные объекты
|
|
||||||
Интенсионализация
|
|
||||||
Интересы
|
|
||||||
Интерпретант
|
|
||||||
Интерпретатор
|
|
||||||
Интерпретация
|
|
||||||
Интерпретированная теория
|
|
||||||
Информационная система
|
|
||||||
Инцесс
|
|
||||||
Инцессант
|
|
||||||
Искусственная система
|
|
||||||
Исследовательский подход
|
|
||||||
Истинное подмножество
|
|
||||||
Исток графа термов
|
|
||||||
Исходная сущность
|
|
||||||
Исходное отношение
|
|
||||||
Исходное понятие
|
|
||||||
Исходное утверждение
|
|
||||||
КА
|
|
||||||
КАК-ориентированная система
|
|
||||||
КАНС
|
|
||||||
Карта понятий
|
|
||||||
Картезиан
|
|
||||||
Каталог методов
|
|
||||||
Категорическая теория
|
|
||||||
Качественная проблема
|
|
||||||
Качественные характеристики теории
|
|
||||||
Квазиаксиома
|
|
||||||
Классификация
|
|
||||||
Классификация множества объектов
|
|
||||||
Классы концептуальных схем
|
|
||||||
Классы систем
|
|
||||||
Класс эквивалентности
|
|
||||||
Количественная проблема
|
|
||||||
Комплексное управление развитием систем
|
|
||||||
Композиция
|
|
||||||
Композиция-2
|
|
||||||
Конвенция
|
|
||||||
Конкрест
|
|
||||||
Конкретант
|
|
||||||
Конкретизация
|
|
||||||
Конкретор
|
|
||||||
Конкреция
|
|
||||||
Конституента
|
|
||||||
Конструкт
|
|
||||||
Конструкт-конструктные конструкции
|
|
||||||
Конструкт-содержантные конструкции
|
|
||||||
Конструкция
|
|
||||||
Концепт
|
|
||||||
Концептуализация
|
|
||||||
Концептуализм
|
|
||||||
Концептуальная конструкция
|
|
||||||
Концептуальная модель
|
|
||||||
Концептуальная схема
|
|
||||||
Концептуальное мышление
|
|
||||||
Концептуальное проектирование
|
|
||||||
Концептуальный анализ
|
|
||||||
Концептуальный анализ и синтез
|
|
||||||
Концептуальный аппарат
|
|
||||||
Корректировка целей
|
|
||||||
Кортеж
|
|
||||||
КП
|
|
||||||
Критерий
|
|
||||||
Критический элемент
|
|
||||||
Круги Эйлера
|
|
||||||
КС
|
|
||||||
КТО-ориентированная система
|
|
||||||
КУРС
|
|
||||||
Лингвистическая интерпретация
|
|
||||||
Логика
|
|
||||||
Логические средства
|
|
||||||
Логическое мышление
|
|
||||||
ЛПР
|
|
||||||
Максимальное измельчение
|
|
||||||
МАКС система
|
|
||||||
Малая проекция
|
|
||||||
Математическая структура
|
|
||||||
Математическая теория
|
|
||||||
Математические средства КП СОУ
|
|
||||||
Материальная интерпретация
|
|
||||||
Машинные средства КП СОУ
|
|
||||||
М-граф
|
|
||||||
Мегатеория
|
|
||||||
Мезотеория
|
|
||||||
Меновая власть
|
|
||||||
Мероприятия по достижению частных целей
|
|
||||||
Метатеорема
|
|
||||||
Метатеория
|
|
||||||
Метаязык
|
|
||||||
Метод
|
|
||||||
Метод КП СОУ
|
|
||||||
Методология
|
|
||||||
Механизм
|
|
||||||
Механизм парирования угрозы
|
|
||||||
Микротеория
|
|
||||||
Минимальный род структуры
|
|
||||||
Множество
|
|
||||||
Множество сечений
|
|
||||||
Множество-степень
|
|
||||||
Модель
|
|
||||||
Модель желаемого выхода
|
|
||||||
Морфологическое отношение
|
|
||||||
Мощность множества
|
|
||||||
Мультиграф
|
|
||||||
Мышление
|
|
||||||
Нагрузка
|
|
||||||
Надпроцесс
|
|
||||||
Надсистема
|
|
||||||
Направления развёртывания
|
|
||||||
Н-арное отношение
|
|
||||||
Независимое множество аксиом
|
|
||||||
Непересекающиеся множества
|
|
||||||
Непротиворечивая теория
|
|
||||||
Нереализованные функции
|
|
||||||
Несовместная теория
|
|
||||||
Неформальная аксиоматическая теория
|
|
||||||
Нижнее замыкание вершины графа
|
|
||||||
Н-местное отношение
|
|
||||||
Нормативная методология
|
|
||||||
Нормативная теория
|
|
||||||
Нормативное мышление
|
|
||||||
Нормативный подход
|
|
||||||
Область значений
|
|
||||||
Область определения
|
|
||||||
Область отправления
|
|
||||||
Область прибытия
|
|
||||||
Обратная связь
|
|
||||||
Обстоятельства
|
|
||||||
Общая теория систем
|
|
||||||
Общезначимая формула
|
|
||||||
Объединение
|
|
||||||
Объект
|
|
||||||
Объектная регламентация
|
|
||||||
Объектные теории
|
|
||||||
Объект управления
|
|
||||||
Объём теории
|
|
||||||
Объяснительная теория
|
|
||||||
Объясняемая теория
|
|
||||||
Ограничение
|
|
||||||
Онтологизация
|
|
||||||
Онтология
|
|
||||||
Оператор
|
|
||||||
Операции КАНС
|
|
||||||
Операциональная схема синтеза
|
|
||||||
Операциональное понятие
|
|
||||||
Операция
|
|
||||||
Операция малая проекция
|
|
||||||
Операция повышения размерности
|
|
||||||
Определяющее свойство множества
|
|
||||||
Оптимизация
|
|
||||||
Организационное управление
|
|
||||||
Организация
|
|
||||||
Ориентированный путь
|
|
||||||
Основание классификации
|
|
||||||
Отвлечённые теории
|
|
||||||
Открытая система
|
|
||||||
Относительное дополнение
|
|
||||||
Отношение
|
|
||||||
Отношение порядка
|
|
||||||
Отношение эквивалентности
|
|
||||||
Отображение
|
|
||||||
Отождествление
|
|
||||||
Отрицание множества
|
|
||||||
ОТСУ
|
|
||||||
ПВР
|
|
||||||
Первичная функция
|
|
||||||
Первичные термины теории
|
|
||||||
Перепостулирование
|
|
||||||
Пересекающиеся множества
|
|
||||||
Пересечение множеств
|
|
||||||
Петля
|
|
||||||
ПО
|
|
||||||
Поддержание
|
|
||||||
Подмножество
|
|
||||||
Подсистема
|
|
||||||
Подтеория
|
|
||||||
Подфункция
|
|
||||||
Политика
|
|
||||||
Полифункциональные методы
|
|
||||||
Полная согласованная интерпретация
|
|
||||||
Полная теория
|
|
||||||
Полный вход
|
|
||||||
Полный выход
|
|
||||||
Понятие
|
|
||||||
Порождающие структуры
|
|
||||||
Постулирование
|
|
||||||
Потенциальная связь
|
|
||||||
Потенциально связанные процессы
|
|
||||||
Потенциальный критический элемент системы
|
|
||||||
Предмет
|
|
||||||
Предметная область
|
|
||||||
Предметный язык
|
|
||||||
Предположение
|
|
||||||
Преобразование
|
|
||||||
Препятствие
|
|
||||||
Прескриптивное использование теории
|
|
||||||
Прескриптивный подход
|
|
||||||
Приложение теории
|
|
||||||
Принадлежность
|
|
||||||
Принцип дополнительности
|
|
||||||
ПРО
|
|
||||||
Проблема
|
|
||||||
Прогнозный сценарий
|
|
||||||
Проект
|
|
||||||
Проектирование СОУ
|
|
||||||
Проектный подход
|
|
||||||
Проект СОУ
|
|
||||||
Проекция родовой структуры
|
|
||||||
Произведение множеств
|
|
||||||
Производное понятие
|
|
||||||
Производные понятия n-го ранга
|
|
||||||
Производные термы
|
|
||||||
Противоречивая теория
|
|
||||||
Процесс
|
|
||||||
Процесс КП конкретной СОУ
|
|
||||||
Процесс ограничения
|
|
||||||
Процессор
|
|
||||||
Процесс с необеспеченными связями
|
|
||||||
Процесс с ролевыми отношениями
|
|
||||||
ПРС
|
|
||||||
ПТ
|
|
||||||
Пустое множество
|
|
||||||
Разбиение множества объектов
|
|
||||||
Разбиение множества объектов по явному основанию
|
|
||||||
Развёрнутое графовое представление
|
|
||||||
Развёрнутый род структуры
|
|
||||||
Развёртывание теории
|
|
||||||
Развивающаяся система
|
|
||||||
Развитие
|
|
||||||
Разнообразие
|
|
||||||
Ранг
|
|
||||||
Ранжирование
|
|
||||||
Расчленённые множества
|
|
||||||
Редукционизм
|
|
||||||
Редукция
|
|
||||||
Реконструкция
|
|
||||||
Ресурс
|
|
||||||
Рефлексивное отношение
|
|
||||||
Решение
|
|
||||||
Р-интерпретация
|
|
||||||
Родовая структура
|
|
||||||
Родовое отношение
|
|
||||||
Родоструктурная экспликация
|
|
||||||
Род структуры
|
|
||||||
Рост
|
|
||||||
Рутинная операция
|
|
||||||
СА
|
|
||||||
Самоорганизующаяся система
|
|
||||||
САПР
|
|
||||||
Свободное мышление
|
|
||||||
Свойство
|
|
||||||
Свойство икса
|
|
||||||
Связь
|
|
||||||
Семантическая сеть
|
|
||||||
Сеть
|
|
||||||
Сеть ПРО
|
|
||||||
Сеть процессов
|
|
||||||
Сеть процессов с ролевыми отношениями
|
|
||||||
Сеть частных целей
|
|
||||||
Сечение
|
|
||||||
Сильные и слабые формы концептуализации
|
|
||||||
Симметричное отношение
|
|
||||||
Синтаксический язык
|
|
||||||
Синтез
|
|
||||||
Синтез родов структур
|
|
||||||
С-интерпретация
|
|
||||||
Система
|
|
||||||
Система автоматизированного проектирования
|
|
||||||
Система восстановления
|
|
||||||
Система организационного управления
|
|
||||||
Система поддержания
|
|
||||||
Система процессов
|
|
||||||
Система развития
|
|
||||||
Система стратегического планирования
|
|
||||||
Система стратегического управления
|
|
||||||
Система управления базами данных
|
|
||||||
Система функционирования
|
|
||||||
Системные классы
|
|
||||||
Системные объекты
|
|
||||||
Системный анализ
|
|
||||||
Слабоструктурированная проблема
|
|
||||||
Словесная форма
|
|
||||||
Сложная система
|
|
||||||
Сложная структура
|
|
||||||
Событие
|
|
||||||
Совершенствование
|
|
||||||
Совместимая теория
|
|
||||||
Содержание
|
|
||||||
Содержант
|
|
||||||
Содержательно-дедуктивное развёртывание
|
|
||||||
Соединение
|
|
||||||
Соединение множеств
|
|
||||||
Соискатель ресурсов
|
|
||||||
Соответствие
|
|
||||||
Состояние организации
|
|
||||||
СОУ
|
|
||||||
Сравнение
|
|
||||||
Среда
|
|
||||||
Средства КП СОУ
|
|
||||||
Срез графа термов
|
|
||||||
Срез подграфа термов
|
|
||||||
Сток графа термов
|
|
||||||
Стратегические цели
|
|
||||||
Стратегическое планирование
|
|
||||||
Стратегическое состояние
|
|
||||||
Стратегия
|
|
||||||
Стратификация
|
|
||||||
Строгое включение
|
|
||||||
Структ
|
|
||||||
Структура
|
|
||||||
Структура, определённая отношением порядка
|
|
||||||
Ступень
|
|
||||||
СУБД
|
|
||||||
Субъект
|
|
||||||
Субъектная регламентация
|
|
||||||
Субъект целеполагания
|
|
||||||
Сумма
|
|
||||||
Сущность
|
|
||||||
Схема
|
|
||||||
Схематизация
|
|
||||||
Творческая операция
|
|
||||||
Тектология
|
|
||||||
Тело теории
|
|
||||||
Теорема
|
|
||||||
Теоретико-множественная интерпретация
|
|
||||||
Теоретико-множественная экспликация
|
|
||||||
Теоретико-множественные операции
|
|
||||||
Теоретико-системная экспликация
|
|
||||||
Теоретико-системное мышление
|
|
||||||
Теоретическое описание объекта
|
|
||||||
Теория
|
|
||||||
Теория множеств
|
|
||||||
Теория систем
|
|
||||||
Терм
|
|
||||||
Терминальная теория
|
|
||||||
Терм-функция
|
|
||||||
Термы n-го ранга
|
|
||||||
Техническая система
|
|
||||||
Техногенема n-го порядка
|
|
||||||
Технология КП СОУ
|
|
||||||
Типизация
|
|
||||||
Типология
|
|
||||||
Типы мышления
|
|
||||||
ТМИ
|
|
||||||
ТМФ
|
|
||||||
Толчок Ильковского
|
|
||||||
Топология
|
|
||||||
Точка зрения
|
|
||||||
Транзитивное отношение
|
|
||||||
Требование проблемы
|
|
||||||
Требования к проекту СОУ
|
|
||||||
Треугольник Фреге
|
|
||||||
ТСК
|
|
||||||
Угроза
|
|
||||||
Универсальное множество
|
|
||||||
Универсум
|
|
||||||
Упорядоченная n-ка
|
|
||||||
Упорядоченная пара
|
|
||||||
Управление
|
|
||||||
Управление процессом достижения цели
|
|
||||||
Урманцев
|
|
||||||
Уровень эксплицитности
|
|
||||||
Условие проблемы
|
|
||||||
Фактормножество
|
|
||||||
Фактор-структура
|
|
||||||
Фактор-структура n-го ранга
|
|
||||||
Феноменология
|
|
||||||
Формализация
|
|
||||||
Формальная система
|
|
||||||
Формальная теория
|
|
||||||
Формально-дедуктивное развёртывание
|
|
||||||
Формальное мышление
|
|
||||||
Форма от x
|
|
||||||
Формы концептуализации
|
|
||||||
Формы мышления
|
|
||||||
Функционально-методное отношение
|
|
||||||
Функциональные свойства
|
|
||||||
Функционирование
|
|
||||||
Функция
|
|
||||||
Целевой выход
|
|
||||||
Целевой терм
|
|
||||||
Целенаправленная система
|
|
||||||
Целенаправленная система с ОС
|
|
||||||
Целеустремлённая система
|
|
||||||
Цель
|
|
||||||
Цикл
|
|
||||||
ЦНС
|
|
||||||
Частная цель
|
|
||||||
Член множества
|
|
||||||
Членство
|
|
||||||
Шкала множеств
|
|
||||||
Шкала ступеней
|
|
||||||
Эквивалентность
|
|
||||||
Экспликация
|
|
||||||
Эксплицитное мышление
|
|
||||||
Экстенсионализация
|
|
||||||
Элемент
|
|
||||||
Элементарная теория
|
|
||||||
Элементарный объект
|
|
||||||
Элемент входа
|
|
||||||
Элемент выхода
|
|
||||||
Эмпирический материал
|
|
||||||
Эпистемология
|
|
||||||
Этапы разработки концептуальной схемы
|
|
||||||
Этиология
|
|
||||||
Ядро
|
|
||||||
Язык-объект
|
|
||||||
Ярус графа
|
|
||||||
Тезаурус по экологии:
|
|
||||||
жизнедеятельность человека
|
|
||||||
технологический процесс
|
|
||||||
пространственный объем
|
|
||||||
естественная экологическая система
|
|
||||||
ценность природного объекта
|
|
||||||
ценность природной среды
|
|
||||||
субъект сохраняемой природной среды
|
|
||||||
экологически значимый отход
|
|
||||||
естественный состав природной среды
|
|
||||||
искусственное изменение природной среды
|
|
||||||
повреждение природного объекта
|
|
||||||
восстановление природного объекта
|
|
||||||
восстановление природной среды
|
|
||||||
восстановимое изменение природной среды
|
|
||||||
физически необратимое изменение природной среды
|
|
||||||
источник воздействия хозяйственной деятельности
|
|
||||||
благоприятный экологический эффект хозяйственной деятельности
|
|
||||||
резерв ресурса целевой экологической зоны под декларации
|
|
||||||
установленный запас ресурса целевой экологической зоны
|
|
||||||
заявленное воздействие
|
|
||||||
экологический аудит
|
|
||||||
благоприятная природная среда жизнедеятельности
|
|
||||||
благоприятная природная среда хозяйственной деятельности
|
|
||||||
благоприятная экологическая зона
|
|
||||||
виды экологической экспертизы
|
|
||||||
государственное оперативное экологическое управление
|
|
||||||
государственное экологическое управление
|
|
||||||
границы применимости методики
|
|
||||||
деклараций о выбросах, сбросах и физических воздействиях
|
|
||||||
декларируемые воздействия
|
|
||||||
декларируемые опосредованные воздействия
|
|
||||||
документы методического обеспечения экологических отношений
|
|
||||||
документы оценки воздействия
|
|
||||||
документы экологического планирования
|
|
||||||
Единый экологический реестр
|
|
||||||
законодательство в области экологических отношений
|
|
||||||
заключение экологического нормоконтроля
|
|
||||||
зона экологического бедствия
|
|
||||||
информационное обеспечение ЭО
|
|
||||||
карта экологического зонирования
|
|
||||||
муниципальное оперативное экологическое управление
|
|
||||||
муниципальное экологическое управление
|
|
||||||
муниципальный экологический контроль
|
|
||||||
муниципальный экологический мониторинг
|
|
||||||
научное обеспечение экологических отношений
|
|
||||||
научный экологический совет
|
|
||||||
неблагоприятная экологическая зона
|
|
||||||
неопределенная экологическая зона
|
|
||||||
неприемлемая экологическая зона
|
|
||||||
общественное экологическое управление
|
|
||||||
общественные экологические объединения
|
|
||||||
общественный экологический контроль
|
|
||||||
общественный экологический мониторинг
|
|
||||||
объект оценки
|
|
||||||
объект экологического нормоконтроля
|
|
||||||
объект, подлежащий обязательному экологическому страхованию
|
|
||||||
обязательное экологическое страхование
|
|
||||||
опасная экологическая зона
|
|
||||||
организации, осуществляющие научное обеспечение экологических отношений
|
|
||||||
основания для ответственности в области экологических отношений
|
|
||||||
приемлемая экологическая зона
|
|
||||||
программа мероприятий по благоприятному изменению природной среды и снижению риска возникновения негативных экологических последствий
|
|
||||||
производственное оперативное экологическое управление
|
|
||||||
производственное экологическое управление
|
|
||||||
производственный экологический контроль
|
|
||||||
производственный экологический мониторинг
|
|
||||||
прокурорский экологический надзор
|
|
||||||
разрешение на воздействие на природную среду
|
|
||||||
разрешение на выбросы, сбросы и физические воздействия
|
|
||||||
разрешенные воздействия
|
|
||||||
разрешенные опосредованные воздействия
|
|
||||||
региональный экологический нормоконтроль
|
|
||||||
региональное оперативное экологическое управление
|
|
||||||
региональное экологическое нормирование
|
|
||||||
региональное экологическое управление
|
|
||||||
региональные экологические советы
|
|
||||||
региональный экологический контроль
|
|
||||||
региональный экологический мониторинг
|
|
||||||
региональный экологический надзор
|
|
||||||
способы возмещения экологического вреда
|
|
||||||
стратегическая экологическая оценка
|
|
||||||
субъект обязательного экологического страхования
|
|
||||||
схема экологического зонирования
|
|
||||||
технические и технологические экологические нормативы
|
|
||||||
территория зонирования
|
|
||||||
условия применимости норматива
|
|
||||||
участники экологических отношений
|
|
||||||
федеральный экологический нормоконтроль
|
|
||||||
федеральное оперативное экологическое управление
|
|
||||||
федеральное экологическое нормирование
|
|
||||||
федеральное экологическое управление
|
|
||||||
федеральный экологический контроль
|
|
||||||
федеральный экологический мониторинг
|
|
||||||
федеральный экологический надзор
|
|
||||||
первичные нормативы благоприятной природной среды жизнедеятельности
|
|
||||||
первичные нормативы благоприятной природной среды хозяйственной деятельности
|
|
||||||
первичные нормативы опасной природной среды жизнедеятельности
|
|
||||||
первичные нормативы приемлемой природной среды жизнедеятельности
|
|
||||||
первичные нормативы сохраняемой природной среды
|
|
||||||
целевая экологическая зона
|
|
||||||
экологическая плата
|
|
||||||
экологическая экспертиза
|
|
||||||
экологический нормоконтроль
|
|
||||||
экологические нормативы воздействия хозяйственной деятельности
|
|
||||||
экологические нормативы последствий воздействия
|
|
||||||
экологические нормативы природной среды
|
|
||||||
экологическое зонирование по нормативам
|
|
||||||
экологическое зонирование по целям
|
|
||||||
экологическое нормирование
|
|
||||||
экологическое управление
|
|
||||||
экспертное экологическое заключение
|
|
||||||
Муниципальный экологический план
|
|
||||||
Ежегодный отчет о состоянии природной среды на территории Российской Федерации
|
|
||||||
Ежегодный отчет о состоянии природной среды на территории муниципальных образований
|
|
||||||
Порядок государственной аккредитации организаций, осуществляющих научное обеспечение экологических отношений
|
|
||||||
Порядок государственной аккредитации экспертных организаций и экспертов в области экологических отношений
|
|
||||||
Порядок научного обеспечения экологических отношений
|
|
||||||
Положение о Научном экологическом совете
|
|
||||||
Положение об экологическом нормоконтроле
|
|
||||||
Положение об экологической экспертизе
|
|
||||||
Методика расчета совокупного опосредованного воздействия на природную среду
|
|
||||||
Положение об Едином экологическом реестре
|
|
||||||
Методика оценки воздействия на природную среду
|
|
||||||
Положение о мерах по благоприятному изменению природной среды и снижению риска возникновения негативных экологических последствий
|
|
||||||
Перечень видов хозяйственной деятельности, для которых устанавливаются экологические нормативы природной среды хозяйственной деятельности
|
|
||||||
Порядок утверждения экологических нормативов природной среды хозяйственной деятельности
|
|
||||||
Порядок утверждения технических и технологических экологических нормативов
|
|
||||||
Порядок утверждения экологических нормативов хозяйственной деятельности
|
|
||||||
Методические рекомендации по разработке муниципальных экологических планов
|
|
||||||
Методические рекомендации по разработке производственных экологических планов
|
|
||||||
Федеральный экологический план
|
|
||||||
Порядок разработки федеральных экологических планов
|
|
||||||
Порядок проведения стратегической экологической оценки
|
|
||||||
Методика расчета воздействия по опосредованному воздействию
|
|
||||||
Порядок оформления разрешений на воздействие на природную среду
|
|
||||||
Методика определения и изменения размера экологической платы
|
|
||||||
Методика экологического мониторинга
|
|
||||||
Методика оперативного экологического управления
|
|
||||||
Методика экологического контроля
|
|
||||||
Методика оценки экологического ущерба
|
|
||||||
Правила экологического страхования
|
|
||||||
Ежегодный отчет о состоянии природной среды на территории субъекта Российской Федерации
|
|
||||||
Положение о Региональном экологическом совете субъекта Российской Федерации
|
|
||||||
Порядок создания региональных экологических советов
|
|
||||||
Порядок проведения публичных слушаний при рассмотрении проектов региональных схем экологического зонирования
|
|
||||||
Региональный экологический план
|
|
||||||
Порядок разработки региональных экологических планов
|
|
||||||
Производственный экологический план
|
|
||||||
жизнедеятельность
|
|
||||||
технология
|
|
||||||
хозяйственная деятельность
|
|
||||||
производственная инфраструктура
|
|
||||||
объект хозяйственной деятельности
|
|
||||||
природный объект
|
|
||||||
природная среда
|
|
||||||
природная среда жизнедеятельности
|
|
||||||
природная среда хозяйственной деятельности
|
|
||||||
природный объект особого значения
|
|
||||||
особо охраняемая природная территория
|
|
||||||
сохраняемая природная среда
|
|
||||||
действующий объект хозяйственной деятельности
|
|
||||||
новый объект хозяйственной деятельности
|
|
||||||
субъект хозяйственной деятельности
|
|
||||||
работник субъекта хозяйственной деятельности
|
|
||||||
отход
|
|
||||||
выброс
|
|
||||||
сброс
|
|
||||||
размещение отходов
|
|
||||||
размещенные отходы
|
|
||||||
объект размещения отходов
|
|
||||||
воздействие хозяйственной деятельности
|
|
||||||
физическое воздействие
|
|
||||||
мероприятие по благоприятному изменению природной среды
|
|
||||||
технологически невосстановимое изменение природной среды
|
|
||||||
опосредующая природная среда
|
|
||||||
опосредующие свойства природной среды
|
|
||||||
опосредованное воздействие
|
|
||||||
территория опосредованного воздействия хозяйственной деятельности
|
|
||||||
совокупное опосредованное воздействие на природную среду
|
|
||||||
экологический эффект хозяйственной деятельности
|
|
||||||
экологическое последствие
|
|
||||||
экологическое правонарушение
|
|
||||||
экологический вред
|
|
||||||
экологический ущерб
|
|
||||||
наилучшая доступная технология
|
|
||||||
негативный экологический эффект хозяйственной деятельности
|
|
||||||
негативное экологическое последствие хозяйственной деятельности
|
|
||||||
реципиент воздействия
|
|
||||||
мероприятие по снижению риска возникновения негативных экологических последствий
|
|
||||||
мера по благоприятному изменению природной среды и снижению риска возникновения негативных экологических последствий
|
|
||||||
экологическое отношение
|
|
||||||
субъекты
|
|
||||||
критерий незначительности воздействия
|
|
||||||
незначительное воздействие хозяйственной деятельности
|
|
||||||
критерий декларирования опосредованного воздействия
|
|
||||||
первичный норматив
|
|
||||||
экологический норматив
|
|
||||||
баланс интересов
|
|
||||||
экологическое зонирование
|
|
||||||
экологических зон
|
|
||||||
базовое состояние природной среды
|
|
||||||
ресурс природной среды
|
|
||||||
распределяемый ресурс природной ресурс
|
|
||||||
квота опосредованного воздействия
|
|
||||||
утраченный экологический ресурс
|
|
||||||
критерий корректировки программ мероприятий
|
|
||||||
экологическая экспертная оценка
|
|
||||||
экологический экспертный расчет
|
|
||||||
экологический нормоконтроль
|
|
||||||
управленческое решение
|
|
||||||
экологический мониторинг
|
|
||||||
оперативное экологическое управление
|
|
||||||
экологический контроль
|
|
||||||
экологический надзор
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
{
|
|
||||||
"cells": [
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"import pandas as pd\n",
|
|
||||||
"\n",
|
|
||||||
"import pymorphy2\n",
|
|
||||||
"import cctext as ct\n",
|
|
||||||
"\n",
|
|
||||||
"from nltk.stem import SnowballStemmer\n",
|
|
||||||
"stemmer = SnowballStemmer(language=\"russian\")\n",
|
|
||||||
"# https://www.langust.ru/rus_gram/rus_gr06.shtml\n",
|
|
||||||
"# stemmer.stem(\"обеспечение\")\n",
|
|
||||||
"\n",
|
|
||||||
"morpho = pymorphy2.MorphAnalyzer()\n",
|
|
||||||
"parser = ct.RuParser()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"butyavka = morpho.parse('мюсли')\n",
|
|
||||||
"butyavka[0].lexeme"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"ct.get_all_forms('танк')"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"# thesaurus = open('tests/data/thesaurus.txt', mode='r', encoding='UTF-8')\n",
|
|
||||||
"thesaurus = open('tests/data/functions.txt', mode='r', encoding='UTF-8')\n",
|
|
||||||
"content = [term.strip() for term in thesaurus.readlines()]\n",
|
|
||||||
"thesaurus.close()\n",
|
|
||||||
"\n",
|
|
||||||
"inflect_datv = [ct.inflect(term, 'datv') for term in content]\n",
|
|
||||||
"inflect_nomn1 = [ct.inflect(term, 'nomn') for term in inflect_datv]\n",
|
|
||||||
"inflect_gent = [ct.inflect(term, 'gent') for term in content]\n",
|
|
||||||
"inflect_nomn2 = [ct.inflect(term, 'nomn') for term in inflect_gent]\n",
|
|
||||||
"\n",
|
|
||||||
"data = zip(content, inflect_datv, inflect_nomn1, inflect_gent, inflect_nomn2)\n",
|
|
||||||
"df = pd.DataFrame(data, columns =['Term', 'Datv', 'Inverse1', 'Gent', 'Inverse2'])\n",
|
|
||||||
"df.to_excel('output.xlsx')"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"interpreter": {
|
|
||||||
"hash": "11938c6bc6919ae2720b4d5011047913343b08a43b18698fd82dedb0d4417594"
|
|
||||||
},
|
|
||||||
"kernelspec": {
|
|
||||||
"display_name": "Python 3.9.5 64-bit",
|
|
||||||
"language": "python",
|
|
||||||
"name": "python3"
|
|
||||||
},
|
|
||||||
"language_info": {
|
|
||||||
"codemirror_mode": {
|
|
||||||
"name": "ipython",
|
|
||||||
"version": 3
|
|
||||||
},
|
|
||||||
"file_extension": ".py",
|
|
||||||
"mimetype": "text/x-python",
|
|
||||||
"name": "python",
|
|
||||||
"nbconvert_exporter": "python",
|
|
||||||
"pygments_lexer": "ipython3",
|
|
||||||
"version": "3.9.5"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"interpreter": {
|
|
||||||
"hash": "b8488343e509b415c98a857491a9b4c90395f9a45992da0bb6102fdf120e22ce"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"orig_nbformat": 2
|
|
||||||
},
|
|
||||||
"nbformat": 4,
|
|
||||||
"nbformat_minor": 2
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,83 +0,0 @@
|
||||||
''' Unit tests: conceptapi. '''
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import cctext as cc
|
|
||||||
|
|
||||||
|
|
||||||
class TestConceptAPI(unittest.TestCase):
|
|
||||||
'''Test class for Concept API.'''
|
|
||||||
def _assert_tags(self, actual: str, expected: str):
|
|
||||||
self.assertEqual(set(cc.split_grams(actual)), set(cc.split_grams(expected)))
|
|
||||||
|
|
||||||
def test_parse(self):
|
|
||||||
''' Test parsing. '''
|
|
||||||
self._assert_tags(cc.parse(''), '')
|
|
||||||
self._assert_tags(cc.parse('1'), 'NUMB,intg')
|
|
||||||
self._assert_tags(cc.parse('слон', require_grams='masc'), 'NOUN,anim,masc,sing,nomn')
|
|
||||||
|
|
||||||
def test_normalize_word(self):
|
|
||||||
''' Test normalize for single word. '''
|
|
||||||
self.assertEqual(cc.normalize(''), '')
|
|
||||||
self.assertEqual(cc.normalize('первого'), 'первый')
|
|
||||||
self.assertEqual(cc.normalize('диких людей'), 'дикий человек')
|
|
||||||
|
|
||||||
def test_generate_lexeme(self):
|
|
||||||
''' Test all lexical forms. '''
|
|
||||||
self.assertEqual(cc.generate_lexeme(''), [])
|
|
||||||
|
|
||||||
forms = cc.generate_lexeme('наверное')
|
|
||||||
self.assertEqual(len(forms), 1)
|
|
||||||
self.assertEqual(forms[0][0], 'наверное')
|
|
||||||
self._assert_tags(forms[0][1], 'CONJ,Prnt')
|
|
||||||
|
|
||||||
forms = cc.generate_lexeme('молодой человек')
|
|
||||||
self.assertEqual(len(forms), 19)
|
|
||||||
self.assertEqual(forms[0][0], 'молодой человек')
|
|
||||||
self._assert_tags(forms[0][1], 'nomn,masc,sing,anim,NOUN')
|
|
||||||
|
|
||||||
def test_inflect(self):
|
|
||||||
''' Test inflection. '''
|
|
||||||
self.assertEqual(cc.inflect('', ''), '')
|
|
||||||
self.assertEqual(cc.inflect('слона', 'nomn'), 'слон')
|
|
||||||
self.assertEqual(cc.inflect('слона', 'ADJF'), 'слона')
|
|
||||||
self.assertEqual(cc.inflect('слона', 'nomn,plur'), 'слоны')
|
|
||||||
self.assertEqual(cc.inflect('слона', 'nomn, plur'), 'слоны')
|
|
||||||
self.assertEqual(cc.inflect('шкала оценок', 'loct,plur'), 'шкалах оценок')
|
|
||||||
|
|
||||||
def test_find_substr(self):
|
|
||||||
'''Test substring search'''
|
|
||||||
self.assertEqual(cc.find_substr('', ''), (0, 0))
|
|
||||||
self.assertEqual(cc.find_substr('сложного красивого слона', 'красивые слоном'), (9, 24))
|
|
||||||
|
|
||||||
def test_inflect_context(self):
|
|
||||||
'''Test contex inflection'''
|
|
||||||
self.assertEqual(cc.inflect_context('', '', ''), '')
|
|
||||||
self.assertEqual(cc.inflect_context('красивый', '', 'чашка'), 'красивая')
|
|
||||||
|
|
||||||
def test_inflect_substitute(self):
|
|
||||||
'''Test substitute inflection'''
|
|
||||||
self.assertEqual(cc.inflect_substitute('', ''), '')
|
|
||||||
self.assertEqual(cc.inflect_substitute('', 'слон'), '')
|
|
||||||
self.assertEqual(cc.inflect_substitute('слон', ''), 'слон')
|
|
||||||
self.assertEqual(cc.inflect_substitute('красивый бантик', 'кошкой'), 'красивым бантиком')
|
|
||||||
|
|
||||||
def test_inflect_dependant(self):
|
|
||||||
''' Test coordination inflection. '''
|
|
||||||
self.assertEqual(cc.inflect_dependant('', ''), '')
|
|
||||||
self.assertEqual(cc.inflect_dependant('', 'слон'), '')
|
|
||||||
self.assertEqual(cc.inflect_dependant('слон', ''), 'слон')
|
|
||||||
self.assertEqual(cc.inflect_dependant('общий', 'мать'), 'общая')
|
|
||||||
self.assertEqual(cc.inflect_dependant('синий', 'слонов'), 'синих')
|
|
||||||
|
|
||||||
def test_match_all_morpho(self):
|
|
||||||
''' Test extracting matching morpho. '''
|
|
||||||
self.assertEqual(cc.match_all_morpho('', ''), [])
|
|
||||||
self.assertEqual(cc.match_all_morpho('горит город', ''), [])
|
|
||||||
self.assertEqual(cc.match_all_morpho('горит город', 'NOUN'), [[6, 11]])
|
|
||||||
self.assertEqual(cc.match_all_morpho('горит город', 'VERB'), [[0, 5]])
|
|
||||||
self.assertEqual(cc.match_all_morpho('столица страны', 'NOUN'), [[0, 7], [8, 14]])
|
|
||||||
self.assertEqual(cc.match_all_morpho('столица страны', 'NOUN,sing,nomn'), [[0, 7]])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
|
@ -1,32 +0,0 @@
|
||||||
''' Unit tests: context. '''
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from cctext.context import Entity, TermContext
|
|
||||||
|
|
||||||
class TestEntity(unittest.TestCase):
|
|
||||||
'''Test Entity termform access.'''
|
|
||||||
def setUp(self):
|
|
||||||
self.alias = 'X1'
|
|
||||||
self.nominal = 'человек'
|
|
||||||
self.text1 = 'test1'
|
|
||||||
self.form1 = ['sing','datv']
|
|
||||||
self.entity = Entity(self.alias, self.nominal, [{'text': self.text1, 'grams': self.form1}])
|
|
||||||
|
|
||||||
def test_attributes(self):
|
|
||||||
self.assertEqual(self.entity.alias, self.alias)
|
|
||||||
self.assertEqual(self.entity.get_nominal(), self.nominal)
|
|
||||||
self.assertEqual(self.entity.manual, [{'text': self.text1, 'grams': self.form1}])
|
|
||||||
|
|
||||||
def test_get_form(self):
|
|
||||||
self.assertEqual(self.entity.get_form([]), self.nominal)
|
|
||||||
self.assertEqual(self.entity.get_form(self.form1), self.text1)
|
|
||||||
self.assertEqual(self.entity.get_form(['invalid tags']), '!Неизвестная граммема: invalid tags!')
|
|
||||||
self.assertEqual(self.entity.get_form(['plur']), 'люди')
|
|
||||||
|
|
||||||
def test_set_nominal(self):
|
|
||||||
new_nomial = 'TEST'
|
|
||||||
self.assertEqual(self.entity.get_form(['plur']), 'люди')
|
|
||||||
self.entity.set_nominal(new_nomial)
|
|
||||||
self.assertEqual(self.entity.get_nominal(), new_nomial)
|
|
||||||
self.assertEqual(self.entity.get_form(['plur']), new_nomial)
|
|
||||||
self.assertEqual(self.entity.manual, [])
|
|
|
@ -1,44 +0,0 @@
|
||||||
''' Unit tests: reference. '''
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from cctext import EntityReference, ReferenceType, SyntacticReference, parse_reference
|
|
||||||
|
|
||||||
class TestReferences(unittest.TestCase):
|
|
||||||
''' Test class for references. '''
|
|
||||||
|
|
||||||
def test_EntityReference(self):
|
|
||||||
''' Testing EntityRefence basics. '''
|
|
||||||
ref = EntityReference('X1', 'sing,nomn')
|
|
||||||
self.assertEqual(ref.get_type(), ReferenceType.entity)
|
|
||||||
self.assertEqual(ref.to_text(), '@{X1|sing,nomn}')
|
|
||||||
|
|
||||||
def test_SyntacticReference(self):
|
|
||||||
''' Testing SyntacticReference basics. '''
|
|
||||||
ref = SyntacticReference(-1, 'черный')
|
|
||||||
self.assertEqual(ref.get_type(), ReferenceType.syntactic)
|
|
||||||
self.assertEqual(ref.to_text(), '@{-1|черный}')
|
|
||||||
|
|
||||||
def test_parse_reference_invalid(self):
|
|
||||||
''' Testing parsing reference invalid input. '''
|
|
||||||
self.assertIsNone(parse_reference(''))
|
|
||||||
self.assertIsNone(parse_reference('X1'))
|
|
||||||
self.assertIsNone(parse_reference('invalid'))
|
|
||||||
self.assertIsNone(parse_reference(' '))
|
|
||||||
self.assertIsNone(parse_reference('@{|}'))
|
|
||||||
self.assertIsNone(parse_reference('@{|черный}'))
|
|
||||||
self.assertIsNone(parse_reference('@{ | }'))
|
|
||||||
self.assertIsNone(parse_reference('@{-1| }'))
|
|
||||||
self.assertIsNone(parse_reference('@{1| }'))
|
|
||||||
self.assertIsNone(parse_reference('@{0|черный}'))
|
|
||||||
|
|
||||||
def test_parse_reference(self):
|
|
||||||
''' Testing parsing reference text. '''
|
|
||||||
ref = parse_reference('@{1| черный }')
|
|
||||||
self.assertIsNotNone(ref)
|
|
||||||
self.assertEqual(ref.to_text(), '@{1|черный}')
|
|
||||||
self.assertEqual(ref.get_type(), ReferenceType.syntactic)
|
|
||||||
|
|
||||||
ref = parse_reference('@{X1 | VERB, past, sing}')
|
|
||||||
self.assertIsNotNone(ref)
|
|
||||||
self.assertEqual(ref.to_text(), '@{X1|VERB,past,sing}')
|
|
||||||
self.assertEqual(ref.get_type(), ReferenceType.entity)
|
|
|
@ -1,107 +0,0 @@
|
||||||
''' Unit tests: resolver. '''
|
|
||||||
import unittest
|
|
||||||
from typing import cast
|
|
||||||
|
|
||||||
from cctext import (
|
|
||||||
EntityReference, TermContext, Entity, SyntacticReference,
|
|
||||||
Resolver, ResolvedReference, Position, TermForm,
|
|
||||||
resolve_entity, resolve_syntactic, extract_entities
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(unittest.TestCase):
|
|
||||||
''' Test utilitiy methods. '''
|
|
||||||
def test_extract_entities(self):
|
|
||||||
self.assertEqual(extract_entities(''), [])
|
|
||||||
self.assertEqual(extract_entities('@{-1|черны}'), [])
|
|
||||||
self.assertEqual(extract_entities('@{X1|nomn}'), ['X1'])
|
|
||||||
self.assertEqual(extract_entities('@{X1|datv}'), ['X1'])
|
|
||||||
self.assertEqual(extract_entities('@{X1|datv} @{X1|datv} @{X2|datv}'), ['X1', 'X2'])
|
|
||||||
self.assertEqual(extract_entities('@{X1} | @{X1} | @{X2}'), [])
|
|
||||||
|
|
||||||
|
|
||||||
class TestResolver(unittest.TestCase):
|
|
||||||
'''Test reference Resolver.'''
|
|
||||||
def setUp(self):
|
|
||||||
self.context = cast(TermContext, {})
|
|
||||||
self.context['X1'] = Entity('X1', 'человек')
|
|
||||||
self.context['X2'] = Entity('X2', '')
|
|
||||||
self.resolver = Resolver(self.context)
|
|
||||||
|
|
||||||
def test_resolve_entity(self):
|
|
||||||
self.assertEqual(resolve_entity(EntityReference('X1', ''), self.context), 'человек')
|
|
||||||
self.assertEqual(resolve_entity(EntityReference('X1', 'plur'), self.context), 'люди')
|
|
||||||
self.assertEqual(resolve_entity(EntityReference('X2', ''), self.context), '!Отсутствует термин: X2!')
|
|
||||||
self.assertEqual(resolve_entity(EntityReference('X1', 'invalid'), self.context), '!Неизвестная граммема: invalid!')
|
|
||||||
self.assertEqual(resolve_entity(EntityReference('X123', 'plur'), self.context), '!Неизвестная сущность: X123!')
|
|
||||||
|
|
||||||
def test_resolve_syntactic(self):
|
|
||||||
ref = ResolvedReference(ref=EntityReference('X1', 'sing,datv'), resolved='человеку')
|
|
||||||
allrefs = [ref, ref, ref, ref]
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-1), 0, allrefs), '!Некорректное смещение: -1!')
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=1), 3, allrefs), '!Некорректное смещение: 1!')
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=1), 0, allrefs), 'умному')
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=2), 0, allrefs), 'умному')
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=3), 0, allrefs), 'умному')
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-1), 3, allrefs), 'умному')
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-2), 3, allrefs), 'умному')
|
|
||||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-3), 3, allrefs), 'умному')
|
|
||||||
|
|
||||||
def test_resolve_invalid(self):
|
|
||||||
self.assertEqual(self.resolver.resolve(''), '')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 0)
|
|
||||||
|
|
||||||
self.assertEqual(self.resolver.resolve('simple text'), 'simple text')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 0)
|
|
||||||
|
|
||||||
self.assertEqual(self.resolver.resolve('simple @{unparsable ref} text'), 'simple @{unparsable ref} text')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 0)
|
|
||||||
|
|
||||||
def test_resolve_single(self):
|
|
||||||
self.assertEqual(self.resolver.resolve('просто @{-1|умный} текст'), 'просто !Некорректное смещение: -1! текст')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 1)
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_input, Position(7, 18))
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_output, Position(7, 34))
|
|
||||||
|
|
||||||
self.assertEqual(self.resolver.resolve('просто @{X123|sing,nomn} текст'), 'просто !Неизвестная сущность: X123! текст')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 1)
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_input, Position(7, 24))
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_output, Position(7, 35))
|
|
||||||
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|sing,nomn}'), 'человек')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 1)
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_input, Position(0, 15))
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_output, Position(0, 7))
|
|
||||||
|
|
||||||
self.assertEqual(self.resolver.resolve('просто @{X1|sing,nomn} текст'), 'просто человек текст')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 1)
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_input, Position(7, 22))
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_output, Position(7, 14))
|
|
||||||
|
|
||||||
def test_resolve_multiple(self):
|
|
||||||
input = '@{X1|sing,datv} @{-1|умный} @{X1|plur} завидуют'
|
|
||||||
self.assertEqual(self.resolver.resolve(input), 'человеку умному люди завидуют')
|
|
||||||
self.assertEqual(len(self.resolver.refs), 3)
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_input, Position(0, 15))
|
|
||||||
self.assertEqual(self.resolver.refs[0].pos_output, Position(0, 8))
|
|
||||||
self.assertEqual(self.resolver.refs[1].pos_input, Position(16, 27))
|
|
||||||
self.assertEqual(self.resolver.refs[1].pos_output, Position(9, 15))
|
|
||||||
self.assertEqual(self.resolver.refs[2].pos_input, Position(28, 38))
|
|
||||||
self.assertEqual(self.resolver.refs[2].pos_output, Position(16, 20))
|
|
||||||
|
|
||||||
def test_resolve_manual_forms(self):
|
|
||||||
self.context['X1'] = Entity(
|
|
||||||
alias='X1',
|
|
||||||
nominal='человек',
|
|
||||||
manual_forms=[
|
|
||||||
TermForm(text='тест1', grams='NOUN,sing'.split(',')),
|
|
||||||
TermForm(text='тест2', grams='NOUN,datv,plur'.split(','))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|NOUN,sing,nomn}'), 'тест1', 'Match subset')
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|NOUN,sing}'), 'тест1', 'Match full')
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|NOUN,datv,plur}'), 'тест2')
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|NOUN,plur,datv}'), 'тест2', 'Match any order')
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|datv,plur}'), 'тест2', 'Match missing POS')
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|NOUN,datv,sing}'), 'тест1')
|
|
||||||
self.assertEqual(self.resolver.resolve('@{X1|VERB,datv,plur}'), 'человек')
|
|
|
@ -1,18 +0,0 @@
|
||||||
''' Unit tests: rumodel. '''
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from cctext import split_grams, combine_grams
|
|
||||||
|
|
||||||
|
|
||||||
class TestTags(unittest.TestCase):
|
|
||||||
'''Test tags manipulation.'''
|
|
||||||
|
|
||||||
def test_split_tags(self):
|
|
||||||
self.assertEqual(split_grams(''), [])
|
|
||||||
self.assertEqual(split_grams('NOUN'), ['NOUN'])
|
|
||||||
self.assertEqual(split_grams('NOUN,plur,sing'), ['NOUN','plur','sing'])
|
|
||||||
|
|
||||||
def test_combine_tags(self):
|
|
||||||
self.assertEqual(combine_grams([]), '')
|
|
||||||
self.assertEqual(combine_grams(['NOUN']), 'NOUN')
|
|
||||||
self.assertEqual(combine_grams(['NOUN','plur','sing']), 'NOUN,plur,sing')
|
|
|
@ -1,446 +0,0 @@
|
||||||
''' Unit tests: ruparser. '''
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from typing import Iterable, Optional
|
|
||||||
from cctext import PhraseParser
|
|
||||||
|
|
||||||
parser = PhraseParser()
|
|
||||||
|
|
||||||
|
|
||||||
class TestRuParser(unittest.TestCase):
|
|
||||||
''' Test class for russian parsing. '''
|
|
||||||
|
|
||||||
def _assert_parse(self, text: str, expected: Iterable[str],
|
|
||||||
require_index: int = -1,
|
|
||||||
require_grams: Optional[Iterable[str]] = None):
|
|
||||||
phrase = parser.parse(text, require_index, require_grams)
|
|
||||||
self.assertIsNotNone(phrase)
|
|
||||||
if phrase:
|
|
||||||
self.assertEqual(phrase.get_morpho().tag.grammemes, set(expected))
|
|
||||||
|
|
||||||
def _assert_inflect(self, text: str, tags: list[str], expected: str):
|
|
||||||
model = parser.parse(text)
|
|
||||||
if not model:
|
|
||||||
result = text
|
|
||||||
else:
|
|
||||||
result = model.inflect(frozenset(tags))
|
|
||||||
self.assertEqual(result, expected)
|
|
||||||
|
|
||||||
def test_parse_word(self):
|
|
||||||
''' Test parse for single word. '''
|
|
||||||
self._assert_parse('1', ['NUMB', 'intg'])
|
|
||||||
self._assert_parse('пять', ['NUMR', 'nomn'])
|
|
||||||
self._assert_parse('трёх', ['NUMR', 'gent'])
|
|
||||||
self._assert_parse('трех', ['NUMR', 'gent'])
|
|
||||||
self._assert_parse('круча', ['NOUN', 'femn', 'sing', 'nomn', 'inan'])
|
|
||||||
self._assert_parse('круть', ['NOUN', 'femn', 'sing', 'nomn', 'inan', 'Sgtm', 'Geox'])
|
|
||||||
self._assert_parse('ПВО', ['NOUN', 'femn', 'sing', 'nomn', 'inan', 'Sgtm', 'Abbr', 'Fixd'])
|
|
||||||
self._assert_parse('СМИ', ['NOUN', 'plur', 'nomn', 'inan', 'Pltm', 'Abbr', 'Fixd', 'GNdr'])
|
|
||||||
self._assert_parse('ему', ['NPRO', 'masc', 'sing', 'datv', '3per', 'Anph'])
|
|
||||||
self._assert_parse('крутит', ['VERB', 'sing', '3per', 'pres', 'impf', 'tran', 'indc'])
|
|
||||||
self._assert_parse('смеркалось', ['VERB', 'neut', 'sing', 'Impe', 'past', 'impf', 'intr', 'indc'])
|
|
||||||
self._assert_parse('крутить', ['INFN', 'impf', 'tran'])
|
|
||||||
self._assert_parse('крученый', ['ADJF', 'masc', 'sing', 'nomn'])
|
|
||||||
self._assert_parse('крут', ['ADJS', 'masc', 'sing', 'Qual'])
|
|
||||||
self._assert_parse('крутящего', ['PRTF', 'masc', 'sing', 'gent', 'pres', 'impf', 'tran', 'actv'])
|
|
||||||
self._assert_parse('откручен', ['PRTS', 'masc', 'sing', 'past', 'perf', 'pssv'])
|
|
||||||
self._assert_parse('крутя', ['GRND', 'pres', 'impf', 'tran'])
|
|
||||||
self._assert_parse('круто', ['ADVB'])
|
|
||||||
self._assert_parse('круче', ['COMP', 'Qual'])
|
|
||||||
self._assert_parse(',', ['PNCT'])
|
|
||||||
self._assert_parse('32-', ['intg', 'NUMB'])
|
|
||||||
|
|
||||||
self._assert_parse('слон', ['NOUN', 'anim', 'masc', 'sing', 'nomn'], require_index=0)
|
|
||||||
self._assert_parse('слон', ['NOUN', 'anim', 'masc', 'sing', 'nomn'], require_grams=['masc'])
|
|
||||||
self._assert_parse('прямой', ['ADJF', 'gent', 'sing', 'femn', 'Qual'], require_index=0)
|
|
||||||
self._assert_parse('прямой', ['ADJF', 'datv', 'Qual', 'sing', 'femn'], require_index=1)
|
|
||||||
self._assert_parse('прямой', ['NOUN', 'sing', 'inan', 'femn', 'gent'], require_grams=['NOUN'])
|
|
||||||
|
|
||||||
self._assert_parse('консистенции', ['NOUN', 'inan', 'femn', 'plur', 'nomn'])
|
|
||||||
self._assert_parse('тест', ['NOUN', 'sing', 'masc', 'inan', 'nomn'])
|
|
||||||
self._assert_parse('петля', ['NOUN', 'inan', 'femn', 'sing', 'nomn'])
|
|
||||||
|
|
||||||
self._assert_parse('Слон', ['NOUN', 'anim', 'masc', 'sing', 'nomn'])
|
|
||||||
self._assert_parse('СМИ', ['NOUN', 'Pltm', 'GNdr', 'Fixd', 'inan', 'Abbr', 'plur', 'nomn'])
|
|
||||||
self.assertEqual(parser.parse('КАиП'), None)
|
|
||||||
self.assertEqual(parser.parse('СЛОН'), None)
|
|
||||||
self.assertEqual(parser.parse(''), None)
|
|
||||||
self.assertEqual(parser.parse('слон', require_grams=set(['femn'])), None)
|
|
||||||
self.assertEqual(parser.parse('32', require_grams=set(['NOUN'])), None)
|
|
||||||
self.assertEqual(parser.parse('32-', require_grams=set(['NOUN'])), None)
|
|
||||||
self.assertEqual(parser.parse('слон', require_index=42), None)
|
|
||||||
|
|
||||||
def test_parse_text(self):
|
|
||||||
''' Test parse for multiword sequences. '''
|
|
||||||
self._assert_parse(', ,', ['PNCT'])
|
|
||||||
self._assert_parse('слон,', ['NOUN', 'anim', 'masc', 'sing', 'nomn'])
|
|
||||||
|
|
||||||
self._assert_parse('синий слон', ['NOUN', 'anim', 'masc', 'sing', 'nomn'])
|
|
||||||
self._assert_parse('слон синий', ['NOUN', 'anim', 'masc', 'sing', 'nomn'])
|
|
||||||
self._assert_parse('тихий Дон', ['NOUN', 'anim', 'masc', 'sing', 'nomn'])
|
|
||||||
self._assert_parse('слон, лежащий на траве', ['NOUN', 'anim', 'masc', 'sing', 'nomn'])
|
|
||||||
self._assert_parse('лежащий на траве слон', ['NOUN', 'anim', 'masc', 'sing', 'nomn'])
|
|
||||||
self._assert_parse('города улиц', ['NOUN', 'nomn', 'plur', 'masc', 'inan'])
|
|
||||||
self._assert_parse('первый дом улиц города', ['NOUN', 'inan', 'masc', 'nomn', 'sing'])
|
|
||||||
self._assert_parse('быстро едет', ['VERB', 'intr', 'impf', '3per', 'sing', 'indc', 'pres'])
|
|
||||||
self._assert_parse('летучий 1-2-пептид', ['NOUN', 'masc', 'nomn', 'sing', 'inan'])
|
|
||||||
|
|
||||||
self._assert_parse('прямой угол', ['NOUN', 'masc', 'nomn', 'inan', 'sing'])
|
|
||||||
self._assert_parse('прямого угла', ['NOUN', 'sing', 'inan', 'masc', 'gent'])
|
|
||||||
self._assert_parse('угла прямой', ['NOUN', 'sing', 'inan', 'masc', 'gent'])
|
|
||||||
self._assert_parse('бесконечной прямой', ['NOUN', 'gent', 'femn', 'sing', 'inan'])
|
|
||||||
self._assert_parse('складские операции', ['NOUN', 'femn', 'plur', 'inan', 'nomn'])
|
|
||||||
self._assert_parse('незначительному воздействию хозяйственной деятельности',
|
|
||||||
['NOUN', 'datv', 'sing', 'neut', 'inan'])
|
|
||||||
|
|
||||||
self._assert_parse('варить овсянку', ['INFN', 'tran', 'impf'])
|
|
||||||
self._assert_parse('варить рис', ['INFN', 'tran', 'impf'])
|
|
||||||
|
|
||||||
self._assert_parse('нарочито сложный', ['ADJF', 'sing', 'masc', 'nomn', 'Qual'])
|
|
||||||
self._assert_parse('части программы', ['NOUN', 'femn', 'plur', 'nomn', 'inan'])
|
|
||||||
self._assert_parse('летучий 1-2-фторметил', ['NOUN', 'nomn', 'sing', 'masc', 'inan'])
|
|
||||||
self._assert_parse('отрезок времени', ['NOUN', 'nomn', 'sing', 'masc', 'inan'])
|
|
||||||
self._assert_parse('портки сушить', ['INFN', 'impf', 'tran'])
|
|
||||||
self._assert_parse('портки вешай', ['VERB', 'tran', 'impr', 'impf', 'sing', 'excl'])
|
|
||||||
self._assert_parse('Анализирует состояние организации.',
|
|
||||||
['VERB', 'sing', 'tran', 'pres', '3per', 'impf', 'indc'])
|
|
||||||
self._assert_parse('Во взаимодействии с подразделениями Генеральной прокуратуры формирует перечень показателей',
|
|
||||||
['VERB', 'sing', 'tran', 'pres', '3per', 'impf', 'indc'])
|
|
||||||
|
|
||||||
def test_parse_coordination(self):
|
|
||||||
''' Test parse coordination info. '''
|
|
||||||
self.assertEqual(parser.parse('говорить').coordination, [-1, 1])
|
|
||||||
self.assertEqual(parser.parse('ворчливая карга').coordination, [2, -1, 0])
|
|
||||||
self.assertEqual(parser.parse('страна, объятая пожаром').coordination, [-1, -1, 2, -1, 2])
|
|
||||||
self.assertEqual(parser.parse('тихо говорил').coordination, [-1, -1, 2])
|
|
||||||
|
|
||||||
def test_normalize_word(self):
|
|
||||||
''' Test normalize for single word. '''
|
|
||||||
self.assertEqual(parser.normalize(''), '')
|
|
||||||
self.assertEqual(parser.normalize('123'), '123')
|
|
||||||
self.assertEqual(parser.normalize('test'), 'test')
|
|
||||||
self.assertEqual(parser.normalize('первого'), 'первый')
|
|
||||||
self.assertEqual(parser.normalize('слону'), 'слон')
|
|
||||||
self.assertEqual(parser.normalize('слонам'), 'слон')
|
|
||||||
self.assertEqual(parser.normalize('обеспечил'), 'обеспечить')
|
|
||||||
self.assertEqual(parser.normalize('сильную'), 'сильный')
|
|
||||||
self.assertEqual(parser.normalize('бежавший'), 'бежать')
|
|
||||||
self.assertEqual(parser.normalize('1 2 3'), '1 2 3')
|
|
||||||
|
|
||||||
def test_normalize_text(self):
|
|
||||||
''' Test normalize for multiword collation. '''
|
|
||||||
self.assertEqual(parser.normalize('синего слона'), 'синий слон')
|
|
||||||
self.assertEqual(parser.normalize('тихо говоривший'), 'тихо говорить')
|
|
||||||
self.assertEqual(parser.normalize('канавой квартала'), 'канава квартала')
|
|
||||||
|
|
||||||
def test_inflect_word(self):
|
|
||||||
''' Test inflection for single word. '''
|
|
||||||
self._assert_inflect('', [], '')
|
|
||||||
self._assert_inflect('invalid', [], 'invalid')
|
|
||||||
self._assert_inflect('invalid', ['nomn'], 'invalid')
|
|
||||||
self._assert_inflect('', ['nomn'], '')
|
|
||||||
self._assert_inflect('123', ['nomn'], '123')
|
|
||||||
self._assert_inflect('слона', [], 'слона')
|
|
||||||
self._assert_inflect('слона', ['ADJF'], 'слона')
|
|
||||||
|
|
||||||
self._assert_inflect('слона', ['nomn'], 'слон')
|
|
||||||
self._assert_inflect('объектоид', ['datv'], 'объектоиду')
|
|
||||||
self._assert_inflect('терм-функция', ['datv'], 'терм-функции')
|
|
||||||
|
|
||||||
self._assert_inflect('Слона', ['nomn'], 'Слон')
|
|
||||||
self._assert_inflect('СМИ', ['datv'], 'СМИ')
|
|
||||||
self._assert_inflect('КАНС', ['datv'], 'КАНС')
|
|
||||||
self._assert_inflect('КАиП', ['datv'], 'КАиП')
|
|
||||||
self._assert_inflect('АТ', ['datv'], 'АТ')
|
|
||||||
self._assert_inflect('А-проекция', ['datv'], 'А-проекции')
|
|
||||||
|
|
||||||
def test_inflect_noun(self):
|
|
||||||
''' Test inflection for single noun. '''
|
|
||||||
self._assert_inflect('книга', ['nomn'], 'книга')
|
|
||||||
self._assert_inflect('книга', ['gent'], 'книги')
|
|
||||||
self._assert_inflect('книга', ['datv'], 'книге')
|
|
||||||
self._assert_inflect('книга', ['accs'], 'книгу')
|
|
||||||
self._assert_inflect('книга', ['ablt'], 'книгой')
|
|
||||||
self._assert_inflect('книга', ['loct'], 'книге')
|
|
||||||
self._assert_inflect('люди', ['loct'], 'людях')
|
|
||||||
|
|
||||||
self._assert_inflect('книга', ['plur'], 'книги')
|
|
||||||
self._assert_inflect('люди', ['sing'], 'человек')
|
|
||||||
self._assert_inflect('человек', ['plur'], 'люди')
|
|
||||||
self._assert_inflect('человек', ['plur', 'loct'], 'людях')
|
|
||||||
|
|
||||||
self._assert_inflect('человеку', ['masc'], 'человеку')
|
|
||||||
self._assert_inflect('человеку', ['neut'], 'человеку')
|
|
||||||
self._assert_inflect('человека', ['femn'], 'человека')
|
|
||||||
self._assert_inflect('человека', ['past'], 'человека')
|
|
||||||
|
|
||||||
def test_inflect_npro(self):
|
|
||||||
''' Test inflection for single pronoun. '''
|
|
||||||
self._assert_inflect('меня', ['nomn'], 'я')
|
|
||||||
self._assert_inflect('я', ['gent'], 'меня')
|
|
||||||
self._assert_inflect('я', ['datv'], 'мне')
|
|
||||||
self._assert_inflect('я', ['accs'], 'меня')
|
|
||||||
self._assert_inflect('я', ['ablt'], 'мной')
|
|
||||||
self._assert_inflect('я', ['loct'], 'мне')
|
|
||||||
|
|
||||||
self._assert_inflect('я', ['ADJF'], 'я')
|
|
||||||
self._assert_inflect('я', ['NOUN'], 'я')
|
|
||||||
self._assert_inflect('я', ['2per'], 'я')
|
|
||||||
self._assert_inflect('я', ['past'], 'я')
|
|
||||||
|
|
||||||
def test_inflect_numr(self):
|
|
||||||
''' Test inflection for single numeric. '''
|
|
||||||
self._assert_inflect('трёх', ['nomn'], 'три')
|
|
||||||
self._assert_inflect('три', ['gent'], 'трёх')
|
|
||||||
self._assert_inflect('три', ['datv'], 'трём')
|
|
||||||
self._assert_inflect('три', ['accs', 'inan'], 'три')
|
|
||||||
self._assert_inflect('три', ['accs', 'anim'], 'трёх')
|
|
||||||
self._assert_inflect('три', ['ablt'], 'тремя')
|
|
||||||
self._assert_inflect('три', ['loct'], 'трёх')
|
|
||||||
|
|
||||||
def test_inflect_adjf(self):
|
|
||||||
''' Test inflection for single adjectif. '''
|
|
||||||
self._assert_inflect('хороший', ['nomn'], 'хороший')
|
|
||||||
self._assert_inflect('хороший', ['gent'], 'хорошего')
|
|
||||||
self._assert_inflect('хороший', ['datv'], 'хорошему')
|
|
||||||
self._assert_inflect('хороший', ['accs'], 'хорошего')
|
|
||||||
self._assert_inflect('хороший', ['ablt'], 'хорошим')
|
|
||||||
self._assert_inflect('хороший', ['loct'], 'хорошем')
|
|
||||||
|
|
||||||
self._assert_inflect('хороший', ['plur'], 'хорошие')
|
|
||||||
self._assert_inflect('хорошие', ['sing'], 'хороший')
|
|
||||||
self._assert_inflect('хорошие', ['sing', 'datv'], 'хорошему')
|
|
||||||
self._assert_inflect('хорошие', ['plur', 'masc', 'datv'], 'хорошим')
|
|
||||||
|
|
||||||
self._assert_inflect('хорошая', ['masc'], 'хороший')
|
|
||||||
self._assert_inflect('перепончатокрылое', ['masc'], 'перепончатокрылый')
|
|
||||||
self._assert_inflect('хороший', ['neut'], 'хорошее')
|
|
||||||
self._assert_inflect('хорошая', ['neut'], 'хорошее')
|
|
||||||
self._assert_inflect('хороший', ['femn'], 'хорошая')
|
|
||||||
self._assert_inflect('перепончатокрылое', ['femn'], 'перепончатокрылая')
|
|
||||||
|
|
||||||
self._assert_inflect('хороший', ['masc', 'femn'], 'хороший')
|
|
||||||
self._assert_inflect('хороший', ['plur', 'femn'], 'хорошие')
|
|
||||||
self._assert_inflect('хороший', ['past'], 'хороший')
|
|
||||||
|
|
||||||
def test_inflect_prtf(self):
|
|
||||||
''' Test inflection for single participle. '''
|
|
||||||
self._assert_inflect('бегущего', ['nomn'], 'бегущий')
|
|
||||||
self._assert_inflect('бегущий', ['gent'], 'бегущего')
|
|
||||||
self._assert_inflect('бегущий', ['datv'], 'бегущему')
|
|
||||||
self._assert_inflect('бегущий', ['accs'], 'бегущего')
|
|
||||||
self._assert_inflect('бегущий', ['ablt'], 'бегущим')
|
|
||||||
self._assert_inflect('бегущий', ['loct'], 'бегущем')
|
|
||||||
self._assert_inflect('бегущая', ['loct'], 'бегущей')
|
|
||||||
self._assert_inflect('бежавшая', ['loct'], 'бежавшей')
|
|
||||||
|
|
||||||
self._assert_inflect('бегущий', ['plur'], 'бегущие')
|
|
||||||
self._assert_inflect('бегущие', ['sing'], 'бегущий')
|
|
||||||
self._assert_inflect('бегущие', ['sing', 'datv'], 'бегущему')
|
|
||||||
|
|
||||||
self._assert_inflect('бегущий', ['femn'], 'бегущая')
|
|
||||||
self._assert_inflect('бегущий', ['neut'], 'бегущее')
|
|
||||||
self._assert_inflect('бегущая', ['masc'], 'бегущий')
|
|
||||||
|
|
||||||
self._assert_inflect('бегущий', ['past'], 'бежавший')
|
|
||||||
self._assert_inflect('бежавших', ['pres'], 'бегущих')
|
|
||||||
|
|
||||||
self._assert_inflect('бегущий', ['masc', 'femn'], 'бегущий')
|
|
||||||
self._assert_inflect('бегущий', ['plur', 'femn'], 'бегущие')
|
|
||||||
|
|
||||||
def test_inflect_verb(self):
|
|
||||||
''' Test inflection for single verb. '''
|
|
||||||
self._assert_inflect('говорить', ['1per'], 'говорю')
|
|
||||||
self._assert_inflect('говорить', ['2per'], 'говоришь')
|
|
||||||
self._assert_inflect('говорить', ['2per', 'plur'], 'говорите')
|
|
||||||
self._assert_inflect('говорить', ['3per'], 'говорит')
|
|
||||||
|
|
||||||
self._assert_inflect('говорите', ['1per'], 'говорим')
|
|
||||||
self._assert_inflect('говорите', ['3per'], 'говорят')
|
|
||||||
|
|
||||||
self._assert_inflect('говорит', ['plur'], 'говорят')
|
|
||||||
self._assert_inflect('говорят', ['sing'], 'говорит')
|
|
||||||
|
|
||||||
self._assert_inflect('говорит', ['past'], 'говорил')
|
|
||||||
self._assert_inflect('говорил', ['pres'], 'говорю')
|
|
||||||
|
|
||||||
self._assert_inflect('говорили', ['sing'], 'говорил')
|
|
||||||
self._assert_inflect('говорил', ['plur'], 'говорили')
|
|
||||||
|
|
||||||
self._assert_inflect('говорила', ['masc'], 'говорил')
|
|
||||||
self._assert_inflect('говорили', ['masc'], 'говорил')
|
|
||||||
self._assert_inflect('говорил', ['neut'], 'говорило')
|
|
||||||
self._assert_inflect('говорил', ['femn'], 'говорила')
|
|
||||||
|
|
||||||
self._assert_inflect('говорить', ['datv'], 'говорить')
|
|
||||||
|
|
||||||
def test_inflect_text_nominal(self):
|
|
||||||
''' Test inflection for multiword text in nominal form. '''
|
|
||||||
self._assert_inflect('синий короткий', ['accs', 'sing', 'femn'], 'синюю короткую')
|
|
||||||
self._assert_inflect('красивые слоны', ['accs', 'sing'], 'красивого слона')
|
|
||||||
self._assert_inflect('вход процесса', ['loct', 'plur'], 'входах процесса')
|
|
||||||
self._assert_inflect('нарочито сложный тест', ['datv', 'sing'], 'нарочито сложному тесту')
|
|
||||||
self._assert_inflect('первый дом улиц города', ['loct', 'plur'], 'первых домах улиц города')
|
|
||||||
self._assert_inflect('шкала оценок', ['loct', 'plur'], 'шкалах оценок')
|
|
||||||
self._assert_inflect('складские операции', ['sing', 'datv'], 'складской операции')
|
|
||||||
self._assert_inflect('стороны конфликтного перехода', ['loct', 'sing'], 'стороне конфликтного перехода')
|
|
||||||
self._assert_inflect('уникомплексные тектологические переходы', ['loct', 'sing'],
|
|
||||||
'уникомплексном тектологическом переходе')
|
|
||||||
|
|
||||||
self._assert_inflect('слабый НИР', ['datv', 'sing'], 'слабому НИР')
|
|
||||||
self._assert_inflect('слабый НИР', ['accs', 'plur'], 'слабых НИР')
|
|
||||||
self._assert_inflect('летучий 1-2-бутан', ['ablt', 'sing'], 'летучим 1-2-бутаном')
|
|
||||||
self._assert_inflect('летучий 1-2-фторметил', ['ablt', 'sing'], 'летучим 1-2-фторметилом')
|
|
||||||
|
|
||||||
self._assert_inflect('красивые процессы', ['accs', 'sing'], 'красивого процесс')
|
|
||||||
self._assert_inflect('красивые процессы', ['gent', 'sing'], 'красивого процесса')
|
|
||||||
self._assert_inflect('части программы', ['ablt', 'sing'], 'части программой')
|
|
||||||
self._assert_inflect('первые здания', ['ablt', 'sing'], 'первым зданием')
|
|
||||||
self._assert_inflect('прямой слон', ['ablt', 'sing'], 'прямым слоном')
|
|
||||||
|
|
||||||
self._assert_inflect('тихо говорить', ['past', 'masc'], 'тихо говорил')
|
|
||||||
self._assert_inflect('быть готовым', ['past', 'masc'], 'был готовым')
|
|
||||||
self._assert_inflect('уметь готовить', ['pres', '2per'], 'умеешь готовить')
|
|
||||||
self._assert_inflect('готовить рис', ['pres', '1per'], 'готовлю рис')
|
|
||||||
|
|
||||||
# self._assert_inflect('десять миллионов', ['datv'], 'десяти миллионам')
|
|
||||||
# self._assert_inflect('десять апельсинов', ['datv'], 'десяти апельсинов')
|
|
||||||
# self._assert_inflect('два миллиона', ['datv'], 'двум миллионам')
|
|
||||||
|
|
||||||
self._assert_inflect('техногенема n-го порядка', ['datv'], 'техногенеме n-го порядка')
|
|
||||||
self._assert_inflect('Положение об органе АБВ', ['datv'], 'Положению об органе АБВ')
|
|
||||||
|
|
||||||
def test_inflect_text_cross(self):
|
|
||||||
''' Test inflection for multiword text in multiple forms. '''
|
|
||||||
self._assert_inflect('слона кота', ['nomn'], 'слон кота')
|
|
||||||
self._assert_inflect('готовкой риса', ['nomn'], 'готовка риса')
|
|
||||||
|
|
||||||
# self._assert_inflect('реципиенту воздействия', ['nomn'], 'реципиент воздействия')
|
|
||||||
|
|
||||||
def test_inflect_complex_mainword(self):
|
|
||||||
''' Test inflection of mainword conmprised of multiple words. '''
|
|
||||||
# Do not parse complex main words
|
|
||||||
self._assert_inflect('слона и кота', ['nomn'], 'слон и кота')
|
|
||||||
self._assert_inflect('сказал и поехал', ['INFN'], 'сказать и поехал')
|
|
||||||
|
|
||||||
def test_inflect_word_pos(self):
|
|
||||||
''' Test inflection for word changing pars of speech. '''
|
|
||||||
self._assert_inflect('обеспечит', ['INFN'], 'обеспечить')
|
|
||||||
self._assert_inflect('обеспечить', ['VERB', '1per'], 'обеспечу')
|
|
||||||
# self._assert_inflect('обеспечить', ['NOUN', 'sing','nomn'], 'обеспечение')
|
|
||||||
# self._assert_inflect('обеспечить', ['NOUN', 'plur','datv'], 'обеспечениям')
|
|
||||||
# self._assert_inflect('синеть', ['NOUN'], 'синь')
|
|
||||||
# self._assert_inflect('готовить', ['NOUN', 'sing'], 'готовка')
|
|
||||||
# self._assert_inflect('обеспечить', ['ADJF'], '???')
|
|
||||||
# self._assert_inflect('обеспечить', ['ADJS'], '???')
|
|
||||||
# self._assert_inflect('синеть', ['ADJF'], 'синий')
|
|
||||||
# self._assert_inflect('готовить', ['ADJF', 'sing', 'femn'], 'готовая')
|
|
||||||
self._assert_inflect('обеспечить', ['PRTF', 'plur', 'past'], 'обеспечившие')
|
|
||||||
self._assert_inflect('обеспечить', ['PRTS', 'plur'], 'обеспечены')
|
|
||||||
self._assert_inflect('обеспечить', ['GRND', 'past'], 'обеспечив')
|
|
||||||
# self._assert_inflect('обеспечить', ['ADVB'], 'обеспечённо')
|
|
||||||
# self._assert_inflect('обеспечить', ['COMP'], 'обеспеченнее')
|
|
||||||
|
|
||||||
# self._assert_inflect('обеспечение', ['INFN'], 'обеспечить')
|
|
||||||
# self._assert_inflect('обеспечение', ['VERB','1per'], 'обеспечу')
|
|
||||||
self._assert_inflect('обеспечение', ['NOUN', 'plur', 'nomn'], 'обеспечения')
|
|
||||||
self._assert_inflect('обеспечение', ['NOUN', 'plur', 'datv'], 'обеспечениям')
|
|
||||||
# self._assert_inflect('синь', ['ADJF'], 'синий')
|
|
||||||
# self._assert_inflect('обеспечение', ['PRTF', 'plur', 'past'], 'обеспечившие')
|
|
||||||
# self._assert_inflect('обеспечение', ['PRTS', 'plur'], 'обеспечены')
|
|
||||||
# self._assert_inflect('обеспечение', ['GRND', 'past'], 'обеспечив')
|
|
||||||
# self._assert_inflect('обеспечение', ['ADVB'], 'обеспечённо')
|
|
||||||
# self._assert_inflect('обеспечение', ['COMP'], 'обеспеченнее')
|
|
||||||
|
|
||||||
# self._assert_inflect('синий', ['INFN'], 'синеть')
|
|
||||||
# self._assert_inflect('синий', ['VERB','1per'], 'синею')
|
|
||||||
# self._assert_inflect('синий', ['NOUN', 'plur','nomn'], 'синьки')
|
|
||||||
# self._assert_inflect('синий', ['NOUN', 'plur','datv'], 'синькам')
|
|
||||||
self._assert_inflect('синий', ['ADJS'], 'синь')
|
|
||||||
self._assert_inflect('хороший', ['ADJS'], 'хорош')
|
|
||||||
# self._assert_inflect('синий', ['PRTF', 'plur', 'past'], 'синевшие')
|
|
||||||
# self._assert_inflect('синий', ['PRTS', 'plur'], '??')
|
|
||||||
# self._assert_inflect('синий', ['GRND', 'past'], 'синев')
|
|
||||||
# self._assert_inflect('хороший', ['ADVB'], 'хорошо')
|
|
||||||
self._assert_inflect('синий', ['COMP'], 'синее')
|
|
||||||
|
|
||||||
self._assert_inflect('обеспечащий', ['INFN'], 'обеспечить')
|
|
||||||
self._assert_inflect('обеспечивающий', ['INFN'], 'обеспечивать')
|
|
||||||
self._assert_inflect('бегущий', ['INFN'], 'бежать')
|
|
||||||
self._assert_inflect('бегущий', ['VERB'], 'бегу')
|
|
||||||
self._assert_inflect('бежавшего', ['VERB'], 'бежал')
|
|
||||||
# self._assert_inflect('обеспечащий', ['NOUN', 'plur','datv'], 'обеспечениям')
|
|
||||||
# self._assert_inflect('синеющий', ['NOUN'], 'синь')
|
|
||||||
# self._assert_inflect('готовящий', ['NOUN', 'sing'], 'готовка')
|
|
||||||
# self._assert_inflect('синеющий', ['ADJF'], 'синий')
|
|
||||||
self._assert_inflect('обеспечащий', ['PRTF', 'plur', 'past'], 'обеспечившие')
|
|
||||||
self._assert_inflect('обеспечащий', ['PRTS', 'plur'], 'обеспечимы')
|
|
||||||
self._assert_inflect('обеспечащий', ['GRND', 'past'], 'обеспечив')
|
|
||||||
# self._assert_inflect('обеспечащий', ['ADVB'], 'обеспечённо')
|
|
||||||
# self._assert_inflect('обеспечащий', ['COMP'], 'обеспеченнее')
|
|
||||||
|
|
||||||
def test_inflect_text_pos(self):
|
|
||||||
''' Test inflection for multiword text changing parts of speech. '''
|
|
||||||
# self._assert_inflect('готовить еду', ['NOUN', 'sing'], 'готовка еды')
|
|
||||||
# self._assert_inflect('обеспечение безопасности', ['INFN'], 'обеспечить безопасность')
|
|
||||||
# self._assert_inflect('сильный удар по мячу', ['INFN'], 'сильно ударить по мячу')
|
|
||||||
self._assert_inflect('сильно обиженный', ['INFN'], 'сильно обидеть')
|
|
||||||
# self._assert_inflect('сильно обиженный', ['NOUN'], 'сильная обида')
|
|
||||||
# self._assert_inflect('надежно обеспечить', ['NOUN'], 'надежное обеспечение')
|
|
||||||
|
|
||||||
def test_inflect_invalid_text(self):
|
|
||||||
''' Test inflection for multiword not coordinated text. '''
|
|
||||||
self._assert_inflect('синими слоны', ['nomn', 'sing'], 'синими слон')
|
|
||||||
|
|
||||||
def test_inflect_context(self):
|
|
||||||
''' Test content inflection. '''
|
|
||||||
self.assertEqual(parser.inflect_context('', '', ''), '')
|
|
||||||
self.assertEqual(parser.inflect_context('', 'красивый', ''), '')
|
|
||||||
self.assertEqual(parser.inflect_context('', '', 'в'), '')
|
|
||||||
self.assertEqual(parser.inflect_context('слон', '', ''), 'слон')
|
|
||||||
|
|
||||||
self.assertEqual(parser.inflect_context('красивый', '', 'чашка'), 'красивая')
|
|
||||||
self.assertEqual(parser.inflect_context('красивый', '', 'черного'), 'красивого')
|
|
||||||
self.assertEqual(parser.inflect_context('слон', '', 'черного'), 'слона')
|
|
||||||
self.assertEqual(parser.inflect_context('слоны', 'сильный', 'черную'), 'слон')
|
|
||||||
self.assertEqual(parser.inflect_context('город', 'огня', ''), 'города')
|
|
||||||
# self.assertEqual(parser.inflect_context('улица', 'дом', ''), 'улицы')
|
|
||||||
|
|
||||||
self.assertEqual(parser.inflect_context('большой город', 'стильного', 'необъятной страны'), 'большого города')
|
|
||||||
self.assertEqual(parser.inflect_context('город', '', ', расположенного неподалеку'), 'города')
|
|
||||||
|
|
||||||
def test_inflect_substitute(self):
|
|
||||||
''' Test substitute inflection. '''
|
|
||||||
self.assertEqual(parser.inflect_substitute('', ''), '')
|
|
||||||
self.assertEqual(parser.inflect_substitute('123', '123'), '123')
|
|
||||||
self.assertEqual(parser.inflect_substitute('', 'слон'), '')
|
|
||||||
self.assertEqual(parser.inflect_substitute('слон', ''), 'слон')
|
|
||||||
self.assertEqual(parser.inflect_substitute('слон', 'слон'), 'слон')
|
|
||||||
self.assertEqual(parser.inflect_substitute('слон', 'слоны'), 'слоны')
|
|
||||||
self.assertEqual(parser.inflect_substitute('слон', 'кошкой'), 'слоном')
|
|
||||||
self.assertEqual(parser.inflect_substitute('синий слон', 'стильного чайника'), 'синего слона')
|
|
||||||
self.assertEqual(parser.inflect_substitute('варить клюкву', 'осуществляет'), 'варит клюкву')
|
|
||||||
|
|
||||||
def test_inflect_dependant(self):
|
|
||||||
''' Test coordination inflection. '''
|
|
||||||
self.assertEqual(parser.inflect_dependant('', ''), '')
|
|
||||||
self.assertEqual(parser.inflect_dependant('', 'слон'), '')
|
|
||||||
self.assertEqual(parser.inflect_dependant('слон', ''), 'слон')
|
|
||||||
self.assertEqual(parser.inflect_dependant('общий', 'мать'), 'общая')
|
|
||||||
self.assertEqual(parser.inflect_dependant('синий', 'слонов'), 'синих')
|
|
||||||
self.assertEqual(parser.inflect_dependant('белый длинный', 'столами'), 'белыми длинными')
|
|
||||||
|
|
||||||
def test_find_substr(self):
|
|
||||||
''' Test substring search. '''
|
|
||||||
self.assertEqual(parser.find_substr('', ''), (0, 0))
|
|
||||||
self.assertEqual(parser.find_substr('слон', ''), (0, 0))
|
|
||||||
self.assertEqual(parser.find_substr('', 'слон'), (0, 0))
|
|
||||||
self.assertEqual(parser.find_substr('слон', 'слон'), (0, 4))
|
|
||||||
self.assertEqual(parser.find_substr('сложного слона', 'слон'), (9, 14))
|
|
||||||
self.assertEqual(parser.find_substr('сложного слона', 'слоном'), (9, 14))
|
|
||||||
self.assertEqual(parser.find_substr('сложного красивого слона', 'красивые слоном'), (9, 24))
|
|
||||||
self.assertEqual(parser.find_substr('человек', 'люди'), (0, 7))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
|
@ -1,62 +0,0 @@
|
||||||
''' Unit tests: syntax. '''
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from cctext import RuSyntax, Capitalization
|
|
||||||
|
|
||||||
|
|
||||||
class TestRusSyntax(unittest.TestCase):
|
|
||||||
''' Test class for russian syntax. '''
|
|
||||||
|
|
||||||
def test_capitalization(self):
|
|
||||||
''' Testing capitalization. '''
|
|
||||||
self.assertEqual(Capitalization.from_text(''), Capitalization.unknwn)
|
|
||||||
self.assertEqual(Capitalization.from_text('Альфа'), Capitalization.first_capital)
|
|
||||||
self.assertEqual(Capitalization.from_text('АЛЬФА'), Capitalization.upper_case)
|
|
||||||
self.assertEqual(Capitalization.from_text('альфа'), Capitalization.lower_case)
|
|
||||||
self.assertEqual(Capitalization.from_text('альФа'), Capitalization.mixed)
|
|
||||||
self.assertEqual(Capitalization.from_text('альфА'), Capitalization.mixed)
|
|
||||||
self.assertEqual(Capitalization.from_text('КАиП'), Capitalization.mixed)
|
|
||||||
|
|
||||||
self.assertEqual(Capitalization.upper_case.apply_to('альфа'), 'АЛЬФА')
|
|
||||||
self.assertEqual(Capitalization.lower_case.apply_to('АльФа'), 'альфа')
|
|
||||||
self.assertEqual(Capitalization.first_capital.apply_to('альфа'), 'Альфа')
|
|
||||||
self.assertEqual(Capitalization.first_capital.apply_to('АльФа'), 'АльФа')
|
|
||||||
self.assertEqual(Capitalization.unknwn.apply_to('АльФа'), 'АльФа')
|
|
||||||
self.assertEqual(Capitalization.mixed.apply_to('АльФа'), 'АльФа')
|
|
||||||
|
|
||||||
def test_is_single_word(self):
|
|
||||||
''' Testing single word identification. '''
|
|
||||||
self.assertTrue(RuSyntax.is_single_word(''))
|
|
||||||
self.assertTrue(RuSyntax.is_single_word('word'))
|
|
||||||
self.assertTrue(RuSyntax.is_single_word('слово'))
|
|
||||||
self.assertTrue(RuSyntax.is_single_word(' word '), 'Whitespace doesnt count')
|
|
||||||
self.assertTrue(RuSyntax.is_single_word('1001'), 'Numbers are words')
|
|
||||||
self.assertTrue(RuSyntax.is_single_word('кое-как'), 'Hyphen doesnt break work')
|
|
||||||
self.assertTrue(RuSyntax.is_single_word('1-2-метилбутан'), 'Complex words')
|
|
||||||
self.assertFalse(RuSyntax.is_single_word('one two'))
|
|
||||||
self.assertFalse(RuSyntax.is_single_word('синий слон'))
|
|
||||||
|
|
||||||
def test_tokenize(self):
|
|
||||||
''' Testing tokenization. '''
|
|
||||||
self.assertEqual(list(RuSyntax.tokenize('')), [])
|
|
||||||
self.assertEqual(list(RuSyntax.tokenize(' ')), [])
|
|
||||||
self.assertEqual(self._list_tokenize('test'), [(0, 4, 'test')])
|
|
||||||
self.assertEqual(self._list_tokenize(' test '), [(1, 5, 'test')])
|
|
||||||
self.assertEqual(self._list_tokenize('синий слон'), [(0, 5, 'синий'), (6, 10, 'слон')])
|
|
||||||
|
|
||||||
def test_split_words(self):
|
|
||||||
''' Testing splitting text into words. '''
|
|
||||||
self.assertEqual([], list(RuSyntax.split_words('')))
|
|
||||||
self.assertEqual([], list(RuSyntax.split_words(' ')))
|
|
||||||
self.assertEqual(RuSyntax.split_words('test'), ['test'])
|
|
||||||
self.assertEqual(RuSyntax.split_words(' test '), ['test'])
|
|
||||||
self.assertEqual(RuSyntax.split_words('синий слон'), ['синий', 'слон'])
|
|
||||||
self.assertEqual(RuSyntax.split_words('синий, большой слон'), ['синий', ',', 'большой', 'слон'])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _list_tokenize(text: str):
|
|
||||||
return [(token.start, token.stop, token.text) for token in RuSyntax.tokenize(text)]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
|
@ -1,13 +1,12 @@
|
||||||
tzdata==2024.1
|
tzdata==2024.1
|
||||||
Django==5.0.3
|
Django==5.0.4
|
||||||
djangorestframework==3.15.1
|
djangorestframework==3.15.1
|
||||||
django-cors-headers==4.3.1
|
django-cors-headers==4.3.1
|
||||||
django-filter==24.2
|
django-filter==24.2
|
||||||
drf-spectacular==0.27.1
|
drf-spectacular==0.27.2
|
||||||
coreapi==2.3.3
|
coreapi==2.3.3
|
||||||
pymorphy3==2.0.1
|
|
||||||
razdel==0.5.0
|
|
||||||
django-rest-passwordreset==1.4.0
|
django-rest-passwordreset==1.4.0
|
||||||
|
cctext==0.1.1
|
||||||
|
|
||||||
psycopg2-binary==2.9.9
|
psycopg2-binary==2.9.9
|
||||||
gunicorn==21.2.0
|
gunicorn==21.2.0
|
|
@ -5,8 +5,7 @@ django-cors-headers
|
||||||
django-filter
|
django-filter
|
||||||
drf-spectacular
|
drf-spectacular
|
||||||
coreapi
|
coreapi
|
||||||
pymorphy3
|
cctext
|
||||||
razdel
|
|
||||||
|
|
||||||
mypy
|
mypy
|
||||||
pylint
|
pylint
|
||||||
|
|
79
rsconcept/frontend/package-lock.json
generated
79
rsconcept/frontend/package-lock.json
generated
|
@ -35,8 +35,8 @@
|
||||||
"@lezer/generator": "^1.7.0",
|
"@lezer/generator": "^1.7.0",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.12.7",
|
||||||
"@types/react": "^18.2.75",
|
"@types/react": "^18.2.77",
|
||||||
"@types/react-dom": "^18.2.24",
|
"@types/react-dom": "^18.2.25",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"typescript": "^5.4.4",
|
"typescript": "^5.4.5",
|
||||||
"vite": "^4.5.3"
|
"vite": "^4.5.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -614,9 +614,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/autocomplete": {
|
"node_modules/@codemirror/autocomplete": {
|
||||||
"version": "6.15.0",
|
"version": "6.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz",
|
||||||
"integrity": "sha512-G2Zm0mXznxz97JhaaOdoEG2cVupn4JjPaS4AcNvZzhOsnnG9YVN68VzfoUw6dYTsIxT6a/cmoFEN47KAWhXaOg==",
|
"integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
|
@ -691,9 +691,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.26.2",
|
"version": "6.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.2.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
|
||||||
"integrity": "sha512-j6V48PlFC/O7ERAR5vRW5QKDdchzmyyfojDdt+zPsB0YXoWgcjlC1IWjmlYfx08aQZ3HN5BtALcgGgtSKGMe7A==",
|
"integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.4.0",
|
"@codemirror/state": "^6.4.0",
|
||||||
"style-mod": "^4.1.0",
|
"style-mod": "^4.1.0",
|
||||||
|
@ -2828,18 +2828,18 @@
|
||||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
|
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.2.75",
|
"version": "18.2.77",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.75.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.77.tgz",
|
||||||
"integrity": "sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==",
|
"integrity": "sha512-CUT9KUUF+HytDM7WiXKLF9qUSg4tGImwy4FXTlfEDPEkkNUzJ7rVFolYweJ9fS1ljoIaP7M7Rdjc5eUm/Yu5AA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/react-dom": {
|
"node_modules/@types/react-dom": {
|
||||||
"version": "18.2.24",
|
"version": "18.2.25",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.24.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.25.tgz",
|
||||||
"integrity": "sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg==",
|
"integrity": "sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
|
@ -3779,9 +3779,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001608",
|
"version": "1.0.30001609",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001608.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz",
|
||||||
"integrity": "sha512-cjUJTQkk9fQlJR2s4HMuPMvTiRggl0rAVMtthQuyOlDWuqHXqN8azLq+pi8B2TjwKJ32diHjUqRIKeFX4z1FoA==",
|
"integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
@ -4369,9 +4369,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dedent": {
|
"node_modules/dedent": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
|
||||||
"integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==",
|
"integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"babel-plugin-macros": "^3.1.0"
|
"babel-plugin-macros": "^3.1.0"
|
||||||
|
@ -4511,9 +4511,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.731",
|
"version": "1.4.735",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.731.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz",
|
||||||
"integrity": "sha512-+TqVfZjpRz2V/5SPpmJxq9qK620SC5SqCnxQIOi7i/U08ZDcTpKbT7Xjj9FU5CbXTMUb4fywbIr8C7cGv4hcjw=="
|
"integrity": "sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A=="
|
||||||
},
|
},
|
||||||
"node_modules/ellipsize": {
|
"node_modules/ellipsize": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
|
@ -5929,15 +5929,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/its-fine": {
|
"node_modules/its-fine": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.2.5.tgz",
|
||||||
"integrity": "sha512-518vLgHK/dgGxyZj4MdLrDRryziqR1M+JbVtjw1tmdgvZQYsJvB2Leoe2qFOHPalZ5KiAOK18wTmIC0XszYc0w==",
|
"integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react": "*",
|
"@types/react-reconciler": "^0.28.0"
|
||||||
"@types/react-reconciler": "*"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=16.8"
|
"react": ">=18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/its-fine/node_modules/@types/react-reconciler": {
|
||||||
|
"version": "0.28.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.8.tgz",
|
||||||
|
"integrity": "sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jackspeak": {
|
"node_modules/jackspeak": {
|
||||||
|
@ -9816,9 +9823,9 @@
|
||||||
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
|
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
|
||||||
},
|
},
|
||||||
"node_modules/three-mesh-bvh": {
|
"node_modules/three-mesh-bvh": {
|
||||||
"version": "0.7.3",
|
"version": "0.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.4.tgz",
|
||||||
"integrity": "sha512-3W6KjzmupjfE89GuHPT31kxKWZ4YGZPEZJNysJpiOZfQRsBQQgmK7v/VJPpjG6syhAvTnY+5Fr77EvIkTLpGSw==",
|
"integrity": "sha512-flxe0A4uflTPR6elgq/Y8VrLoljDNS899i422SxQcU3EtMj6o8z4kZRyqZqGWzR0qMf1InTZzY1/0xZl/rnvVw==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"three": ">= 0.151.0"
|
"three": ">= 0.151.0"
|
||||||
}
|
}
|
||||||
|
@ -10016,9 +10023,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.4.4",
|
"version": "5.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||||
"integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
|
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
|
@ -39,8 +39,8 @@
|
||||||
"@lezer/generator": "^1.7.0",
|
"@lezer/generator": "^1.7.0",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.12.7",
|
||||||
"@types/react": "^18.2.75",
|
"@types/react": "^18.2.77",
|
||||||
"@types/react-dom": "^18.2.24",
|
"@types/react-dom": "^18.2.25",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"typescript": "^5.4.4",
|
"typescript": "^5.4.5",
|
||||||
"vite": "^4.5.3"
|
"vite": "^4.5.3"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
|
|
@ -11,8 +11,8 @@ function BackendLint() {
|
||||||
|
|
||||||
Set-Location $backend
|
Set-Location $backend
|
||||||
$env:DJANGO_SETTINGS_MODULE = "project.settings"
|
$env:DJANGO_SETTINGS_MODULE = "project.settings"
|
||||||
& $pylint cctext project apps
|
& $pylint project apps
|
||||||
& $mypy cctext project apps
|
& $mypy project apps
|
||||||
}
|
}
|
||||||
|
|
||||||
RunLinters
|
RunLinters
|
Loading…
Reference in New Issue
Block a user