mirror of
https://github.com/IRBorisov/cctext.git
synced 2025-06-25 21:20:36 +03:00
Transfering project
This commit is contained in:
parent
5831bf4c9e
commit
cc94f805b8
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
~$*
|
||||
.vs
|
||||
.pytest_cache
|
||||
__pycache__
|
||||
|
||||
build/
|
||||
whl/
|
||||
venv/
|
||||
output/
|
||||
|
||||
*egg-info
|
||||
|
||||
nosetests.xml
|
635
.pylintrc
Normal file
635
.pylintrc
Normal file
|
@ -0,0 +1,635 @@
|
|||
[MAIN]
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Clear in-memory caches upon conclusion of linting. Useful if running pylint
|
||||
# in a server-like mode.
|
||||
clear-cache-post-run=no
|
||||
|
||||
# Load and enable all available extensions. Use --list-extensions to see a list
|
||||
# all available extensions.
|
||||
#enable-all-extensions=
|
||||
|
||||
# In error mode, messages with a category besides ERROR or FATAL are
|
||||
# suppressed, and no reports are done by default. Error mode is compatible with
|
||||
# disabling specific errors.
|
||||
#errors-only=
|
||||
|
||||
# Always return a 0 (non-error) status code, even if lint errors are found.
|
||||
# This is primarily useful in continuous integration scripts.
|
||||
#exit-zero=
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code.
|
||||
extension-pkg-allow-list=pyconcept
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
|
||||
# for backward compatibility.)
|
||||
extension-pkg-whitelist=
|
||||
|
||||
# Return non-zero exit code if any of these messages/categories are detected,
|
||||
# even if score is above --fail-under value. Syntax same as enable. Messages
|
||||
# specified are enabled, while categories only check already-enabled messages.
|
||||
fail-on=
|
||||
|
||||
# Specify a score threshold under which the program will exit with error.
|
||||
fail-under=10
|
||||
|
||||
# Interpret the stdin as a python script, whose filename needs to be passed as
|
||||
# the module_or_package argument.
|
||||
#from-stdin=
|
||||
|
||||
# Files or directories to be skipped. They should be base names, not paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regular expressions patterns to the
|
||||
# ignore-list. The regex matches against paths and can be in Posix or Windows
|
||||
# format. Because '\\' represents the directory delimiter on Windows systems,
|
||||
# it can't be used as an escape character.
|
||||
ignore-paths=t_.*,.*migrations.*
|
||||
|
||||
# Files or directories matching the regular expression patterns are skipped.
|
||||
# The regex matches against base names, not paths. The default value ignores
|
||||
# Emacs file locks
|
||||
ignore-patterns=t_.*?py
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis). It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use, and will cap the count on Windows to
|
||||
# avoid hangs.
|
||||
jobs=1
|
||||
|
||||
# Control the amount of potential inferred values when inferring a single
|
||||
# object. This can help the performance when dealing with large functions or
|
||||
# complex, nested conditions.
|
||||
limit-inference-results=100
|
||||
|
||||
# List of plugins (as comma separated values of python module names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Minimum Python version to use for version dependent checks. Will default to
|
||||
# the version used to run pylint.
|
||||
py-version=3.9
|
||||
|
||||
# Discover python modules and packages in the file system subtree.
|
||||
recursive=no
|
||||
|
||||
# Add paths to the list of the source roots. Supports globbing patterns. The
|
||||
# source root is an absolute path or a path relative to the current working
|
||||
# directory used to determine a package namespace for modules located under the
|
||||
# source root.
|
||||
source-roots=
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
# In verbose mode, extra non-checker-related info will be displayed.
|
||||
#verbose=
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style. If left empty, argument names will be checked with the set
|
||||
# naming style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style. If left empty, attribute names will be checked with the set naming
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Bad variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be refused
|
||||
bad-names-rgxs=
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style. If left empty, class attribute names will be checked
|
||||
# with the set naming style.
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class constant names.
|
||||
class-const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct class constant names. Overrides class-
|
||||
# const-naming-style. If left empty, class constant names will be checked with
|
||||
# the set naming style.
|
||||
#class-const-rgx=
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style. If left empty, class names will be checked with the set naming style.
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style. If left empty, constant names will be checked with the set naming
|
||||
# style.
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style. If left empty, function names will be checked with the set
|
||||
# naming style.
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
ex,
|
||||
Run,
|
||||
_
|
||||
|
||||
# Good variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be accepted
|
||||
good-names-rgxs=
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style. If left empty, inline iteration names will be checked
|
||||
# with the set naming style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style. If left empty, method names will be checked with the set naming style.
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style. If left empty, module names will be checked with the set naming style.
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
# These decorators are taken in consideration only for invalid-name.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Regular expression matching correct type alias names. If left empty, type
|
||||
# alias names will be checked with the set naming style.
|
||||
#typealias-rgx=
|
||||
|
||||
# Regular expression matching correct type variable names. If left empty, type
|
||||
# variable names will be checked with the set naming style.
|
||||
#typevar-rgx=
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style. If left empty, variable names will be checked with the set
|
||||
# naming style.
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# Warn about protected attribute access inside special methods
|
||||
check-protected-access-in-special-methods=no
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp,
|
||||
asyncSetUp,
|
||||
__post_init__
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=mcs
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# List of regular expressions of class ancestor names to ignore when counting
|
||||
# public methods (see R0903)
|
||||
exclude-too-few-public-methods=
|
||||
|
||||
# List of qualified class names to ignore when counting class parents (see
|
||||
# R0901)
|
||||
ignored-parents=
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in an if statement (see R0916).
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body.
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body.
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body.
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body.
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when caught.
|
||||
overgeneral-exceptions=builtins.BaseException,builtins.Exception
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=100
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# List of modules that can be imported at any level, not just the top level
|
||||
# one.
|
||||
allow-any-import-level=
|
||||
|
||||
# Allow explicit reexports by alias from a package __init__.
|
||||
allow-reexport-from-package=no
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma.
|
||||
deprecated-modules=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of external dependencies
|
||||
# to the given file (report RP0402 must not be disabled).
|
||||
ext-import-graph=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of all (i.e. internal and
|
||||
# external) dependencies to the given file (report RP0402 must not be
|
||||
# disabled).
|
||||
import-graph=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of internal dependencies
|
||||
# to the given file (report RP0402 must not be disabled).
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
# Couples of modules and preferred modules, separated by a comma.
|
||||
preferred-modules=
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# The type of string formatting that logging methods do. `old` means using %
|
||||
# formatting, `new` is for `{}` formatting.
|
||||
logging-format-style=old
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format.
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
|
||||
# UNDEFINED.
|
||||
confidence=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once). You can also use "--disable=all" to
|
||||
# disable everything first and then re-enable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=too-many-public-methods,
|
||||
invalid-name,
|
||||
no-else-break,
|
||||
no-else-continue,
|
||||
no-else-return,
|
||||
no-member,
|
||||
too-many-ancestors,
|
||||
too-many-return-statements,
|
||||
too-many-locals,
|
||||
too-many-instance-attributes,
|
||||
too-few-public-methods,
|
||||
unused-argument,
|
||||
missing-function-docstring,
|
||||
attribute-defined-outside-init,
|
||||
ungrouped-imports,
|
||||
abstract-method
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[METHOD_ARGS]
|
||||
|
||||
# List of qualified names (i.e., library.method) which require a timeout
|
||||
# parameter e.g. 'requests.api.get,requests.api.post'
|
||||
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
|
||||
max-line-length=120
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
|
||||
# Regular expression of note tags to take in consideration.
|
||||
notes-rgx=
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=sys.exit,argparse.parse_error
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a score less than or equal to 10. You
|
||||
# have access to the variables 'fatal', 'error', 'warning', 'refactor',
|
||||
# 'convention', and 'info' which contain the number of messages in each
|
||||
# category, as well as 'statement' which is the total number of statements
|
||||
# analyzed. This score is used by the global evaluation report (RP0004).
|
||||
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
#output-format=
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Comments are removed from the similarity computation
|
||||
ignore-comments=yes
|
||||
|
||||
# Docstrings are removed from the similarity computation
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Imports are removed from the similarity computation
|
||||
ignore-imports=yes
|
||||
|
||||
# Signatures are removed from the similarity computation
|
||||
ignore-signatures=yes
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes.
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. No available dictionaries : You need to install
|
||||
# both the python package and the system dependency for enchant to work..
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should be considered directives if they
|
||||
# appear at the beginning of a comment and should not be checked.
|
||||
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains the private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to the private dictionary (see the
|
||||
# --spelling-private-dict-file option) instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[STRING]
|
||||
|
||||
# This flag controls whether inconsistent-quotes generates a warning when the
|
||||
# character used as a quote delimiter is used inconsistently within a module.
|
||||
check-quote-consistency=no
|
||||
|
||||
# This flag controls whether the implicit-str-concat should generate a warning
|
||||
# on implicit string concatenation in sequences defined over several lines.
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# Tells whether to warn about missing members when the owner of the attribute
|
||||
# is inferred to be None.
|
||||
ignore-none=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of symbolic message names to ignore for Mixin members.
|
||||
ignored-checks-for-mixins=no-member,
|
||||
not-async-context-manager,
|
||||
not-context-manager,
|
||||
attribute-defined-outside-init
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
# Regex pattern to define which classes are considered mixins.
|
||||
mixin-class-rgx=.*[Mm]ixin
|
||||
|
||||
# List of decorators that change the signature of a decorated function.
|
||||
signature-mutators=
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of names allowed to shadow builtins
|
||||
allowed-redefined-builtins=
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||
# not be used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored.
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
22
.vscode/launch.json
vendored
Normal file
22
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Build",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
"script": "${workspaceFolder}/scripts/Build.ps1",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "Lint",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
"script": "${workspaceFolder}/scripts/RunLint.ps1",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
40
.vscode/settings.json
vendored
Normal file
40
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"search.exclude": {
|
||||
".mypy_cache/": true,
|
||||
".pytest_cache/": true
|
||||
},
|
||||
"python.testing.unittestArgs": ["-v", "-s", "./tests", "-p", "test*.py"],
|
||||
"python.testing.pytestEnabled": false,
|
||||
"python.testing.unittestEnabled": true,
|
||||
"eslint.workingDirectories": [
|
||||
{
|
||||
"mode": "auto"
|
||||
}
|
||||
],
|
||||
"python.analysis.typeCheckingMode": "off",
|
||||
"python.analysis.ignore": ["**/tests/**", "**/node_modules/**", "**/venv/**"],
|
||||
"cSpell.words": [
|
||||
"ADJF",
|
||||
"ADJS",
|
||||
"ADVB",
|
||||
"Grammemes",
|
||||
"GRND",
|
||||
"INFN",
|
||||
"INTJ",
|
||||
"multiword",
|
||||
"NPRO",
|
||||
"NUMR",
|
||||
"Opencorpora",
|
||||
"PNCT",
|
||||
"PRCL",
|
||||
"PRTF",
|
||||
"PRTS",
|
||||
"pymorphy",
|
||||
"razdel",
|
||||
"rumodel",
|
||||
"tagset",
|
||||
"unknwn"
|
||||
],
|
||||
"cSpell.language": "en,ru",
|
||||
"cSpell.ignorePaths": ["node_modules/**", "*.json"]
|
||||
}
|
16
cctext/__init__.py
Normal file
16
cctext/__init__.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
''' 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
|
90
cctext/conceptapi.py
Normal file
90
cctext/conceptapi.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
'''
|
||||
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)
|
84
cctext/context.py
Normal file
84
cctext/context.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
''' 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]
|
60
cctext/reference.py
Normal file
60
cctext/reference.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
''' 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)
|
140
cctext/resolver.py
Normal file
140
cctext/resolver.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
''' 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)]
|
118
cctext/rumodel.py
Normal file
118
cctext/rumodel.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
''' 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)
|
486
cctext/ruparser.py
Normal file
486
cctext/ruparser.py
Normal file
|
@ -0,0 +1,486 @@
|
|||
''' Parsing russian language using pymorphy3 library. '''
|
||||
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])
|
87
cctext/syntax.py
Normal file
87
cctext/syntax.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
''' 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)]
|
12
mypy.ini
Normal file
12
mypy.ini
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Global options:
|
||||
|
||||
[mypy]
|
||||
warn_return_any = True
|
||||
warn_unused_configs = True
|
||||
|
||||
# Per-module options:
|
||||
[mypy-razdel.*]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-pymorphy3.*]
|
||||
ignore_missing_imports = True
|
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
BIN
requirements-build.txt
Normal file
BIN
requirements-build.txt
Normal file
Binary file not shown.
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
pymorphy3
|
||||
pymorphy3-dicts-ru
|
||||
razdel
|
27
scripts/Build.ps1
Normal file
27
scripts/Build.ps1
Normal file
|
@ -0,0 +1,27 @@
|
|||
Set-Location $PSScriptRoot\..
|
||||
|
||||
$packageName = 'cctext'
|
||||
$output = 'output'
|
||||
$python = '.\venv\Scripts\python.exe'
|
||||
if (-not (Test-Path -Path $python -PathType Leaf)) {
|
||||
& 'python' -m venv .\venv
|
||||
& $python -m pip install -r requirements.txt
|
||||
& $python -m pip install -r requirements-build.txt
|
||||
}
|
||||
|
||||
if (Test-Path -Path $output\$packageName) {
|
||||
Remove-Item $output\$packageName -Recurse -Force
|
||||
}
|
||||
|
||||
& $python -m build --outdir=$output\$packageName
|
||||
$wheel = Get-Childitem -Path $output\$packageName\*.whl -Name
|
||||
if (-not $wheel) {
|
||||
Write-Error "No wheel generated for $packageName"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
& $python -m pip install -I $output\$packageName\$wheel
|
||||
& $python -m unittest
|
||||
$exitcode = $LASTEXITCODE
|
||||
& $python -m pip uninstall -y $packageName
|
||||
Exit $exitcode
|
10
scripts/RunLint.ps1
Normal file
10
scripts/RunLint.ps1
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Run lint
|
||||
function RunLinters() {
|
||||
$pylint = "$PSScriptRoot\..\venv\Scripts\pylint.exe"
|
||||
$mypy = "$PSScriptRoot\..\venv\Scripts\mypy.exe"
|
||||
|
||||
& $pylint cctext
|
||||
& $mypy cctext
|
||||
}
|
||||
|
||||
RunLinters
|
10
scripts/Test.ps1
Normal file
10
scripts/Test.ps1
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Run lint
|
||||
function RunLinters() {
|
||||
$pylint = "$PSScriptRoot\..\venv\Scripts\pylint.exe"
|
||||
$mypy = "$PSScriptRoot\..\venv\Scripts\mypy.exe"
|
||||
|
||||
& $pylint cctext
|
||||
& $mypy cctext
|
||||
}
|
||||
|
||||
RunLinters
|
18
setup.cfg
Normal file
18
setup.cfg
Normal file
|
@ -0,0 +1,18 @@
|
|||
[metadata]
|
||||
name = cctext
|
||||
version = file: VERSION
|
||||
author = CIHT CONCEPT, IRBorisov
|
||||
author_email = iborisov@acconcept.ru
|
||||
description = Concept text processing library
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
license = MIT
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3
|
||||
|
||||
[options]
|
||||
packages = find:
|
||||
install_requires =
|
||||
razdel
|
||||
pymorphy3
|
||||
pymorphy3-dicts-ru
|
5
setup.py
Normal file
5
setup.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
''' CCText package installer '''
|
||||
from setuptools import setup
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup()
|
8
tests/__init__.py
Normal file
8
tests/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
''' 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 *
|
1819
tests/data/functions.txt
Normal file
1819
tests/data/functions.txt
Normal file
File diff suppressed because it is too large
Load Diff
866
tests/data/thesaurus.txt
Normal file
866
tests/data/thesaurus.txt
Normal file
|
@ -0,0 +1,866 @@
|
|||
тектологические состояния
|
||||
первые здания
|
||||
доска
|
||||
структуры
|
||||
тектологические состояния
|
||||
тектологические переходы
|
||||
уникомплексные тектологические переходы
|
||||
поликомплексные переходы
|
||||
процессы
|
||||
разрушаемые оружием структуры
|
||||
создаваемые оружием структуры
|
||||
неразрушаемые оружием структуры
|
||||
несоздаваемые оружием структуры
|
||||
формы движения
|
||||
объекты
|
||||
систенты
|
||||
субъекты
|
||||
системные классы
|
||||
системные субъекты
|
||||
системное сознание
|
||||
системно познаные объекты
|
||||
обсубъект
|
||||
системно непознанные объекты
|
||||
оценки
|
||||
шкала оценок
|
||||
альтернативы
|
||||
критерии
|
||||
сеть альтернатив
|
||||
предъявление
|
||||
субъект актуализации
|
||||
основание
|
||||
актуализированная альтернатива
|
||||
цепи актуализаций
|
||||
Сечения актуализации
|
||||
комплексы
|
||||
квазиконфликтанты
|
||||
консистенции
|
||||
квазиконфликтантные комплексы
|
||||
коструктурный слой
|
||||
коструктурное окружение
|
||||
квазиконфликты
|
||||
шкалы оценок квазиконфликтанта
|
||||
ущемленная сторона конфликтного перехода
|
||||
кооперативный переход
|
||||
конфликты
|
||||
консистенты
|
||||
цепи нагнетания напряженности
|
||||
состояние напряженности
|
||||
конфликтное состояние
|
||||
структурный дифференциал
|
||||
конфликтоген
|
||||
основание конфликта
|
||||
конфликтанты
|
||||
Уникомплексный переход
|
||||
структурный дифференциал перехода
|
||||
рабочий процесс оружия
|
||||
приемы
|
||||
натуральные числа
|
||||
складские операции
|
||||
группа организационных единиц
|
||||
полные описания издений
|
||||
разбиения
|
||||
номера договорных поставок
|
||||
номера приходных ордеров поставок
|
||||
дни приходных ордеров поставок
|
||||
дни
|
||||
выбранное число
|
||||
организационные единицы
|
||||
изделия
|
||||
операции
|
||||
группа организаций
|
||||
предприятия
|
||||
организация
|
||||
склад
|
||||
отрезки времени
|
||||
прошлые состояния склада
|
||||
подразделения
|
||||
поступления партий изделий
|
||||
отпуск партий изделий
|
||||
типы изделий
|
||||
виды изделий
|
||||
договорные поставки
|
||||
поставки
|
||||
начальные отрезки натуральных чисел
|
||||
абсолютные нумерации разбиений
|
||||
относительные нумерации разбиений
|
||||
календари
|
||||
солнечные календари
|
||||
григорианские календари
|
||||
квартальные григорианские календари
|
||||
календарные годы
|
||||
календарные кварталы
|
||||
отрезки нумерации складских операций
|
||||
поступление
|
||||
отпуск
|
||||
классы изделий
|
||||
текущее состояние склада
|
||||
спецификация
|
||||
единицы учета, заданные спецификациями изделий
|
||||
Единицы учета, заданные множествами изделий
|
||||
единицы учета
|
||||
прием партий изделий
|
||||
отпуск изделий
|
||||
характеристики
|
||||
единицы измерения
|
||||
числовые индексы
|
||||
полные описания изделий
|
||||
полные описания
|
||||
самое мелкое разбиение
|
||||
самое крупное разбиение
|
||||
виды операций
|
||||
Складские операции, выраженные в единицах учета
|
||||
Поступления изделий, выраженные в единицах учета
|
||||
Отпуск изделий, выраженный в единицах учета
|
||||
високосные годы
|
||||
невисокосные годы
|
||||
годы
|
||||
месяцы
|
||||
кварталы
|
||||
номера фондовых извещений
|
||||
номера договоров
|
||||
фондовые извещения
|
||||
Нумерация складских операций
|
||||
фонды
|
||||
договора
|
||||
дата
|
||||
номер приходного ордера
|
||||
поставки по техпомощи
|
||||
Тезаурус кафедры КАиП:
|
||||
Н-арное отношение
|
||||
Н-местное отношение
|
||||
R-интерпретация
|
||||
Абельность
|
||||
Абсолютное дополнение
|
||||
Абстрагирование
|
||||
Абстрактная система
|
||||
Абстрактный процесс
|
||||
Абстракция
|
||||
Автоматизированная система проектирования систем организационного управления
|
||||
Адаптивная система
|
||||
Адекватность
|
||||
Аксиома
|
||||
Аксиома связности
|
||||
Аксиома структуры
|
||||
Аксиоматизация
|
||||
Аксиоматика теории
|
||||
Аксиоматическая теория
|
||||
Аксиомы группы
|
||||
Аксиомы синтеза
|
||||
Актуальность
|
||||
Алгебраическая структура
|
||||
Алгебра конструктов
|
||||
Альтернатива
|
||||
Альтернативные методы
|
||||
Анализ
|
||||
Аналитическая теория
|
||||
Аналогия
|
||||
Аспект
|
||||
Аспектная теория
|
||||
АСП СОУ
|
||||
АТ
|
||||
Атрибут
|
||||
Атрибутивная форма
|
||||
Атрибутивное мышление
|
||||
База данных
|
||||
Базис множеств
|
||||
Базисное множество
|
||||
Библиотека моделей
|
||||
Бинарная операция
|
||||
Бинарное отношение
|
||||
Богданов
|
||||
Большая проекция
|
||||
Боулдинг
|
||||
Булеан
|
||||
Бурбаки
|
||||
Вершина
|
||||
Взаимно дополнительные теории
|
||||
Видообразование
|
||||
Включение
|
||||
Власть
|
||||
Внешность предметной области
|
||||
Внутренние термы ядра теории
|
||||
Возможность
|
||||
Восхождение
|
||||
Вспомогательные базисные множества
|
||||
Вторичная функция
|
||||
Вторичный метод
|
||||
Вход
|
||||
Вход процесса
|
||||
Выбор
|
||||
Выразимость
|
||||
Выразительная сила
|
||||
Выразительные средства теории
|
||||
Высказывание
|
||||
Выход
|
||||
Выход процесса
|
||||
ГДМ
|
||||
Генезис
|
||||
Гипертеория
|
||||
Гипотеза
|
||||
Гипотетико-дедуктивные требования
|
||||
Гипотетико-дедуктивный метод
|
||||
Главная функция
|
||||
Глубина проникновения
|
||||
Гносеология
|
||||
Гомоморфизм
|
||||
ГП РС
|
||||
Границы сети процессов
|
||||
Граф
|
||||
Граф термов
|
||||
Группа
|
||||
Двухместное отношение
|
||||
Дебуленизация
|
||||
Дедуктивная теория
|
||||
Декартово произведение
|
||||
Декомпозиция
|
||||
Дескриптивное использование теории
|
||||
Дескриптивный подход
|
||||
Диаграмма Венна
|
||||
Динамическая система
|
||||
Дихотомия
|
||||
Доказательство
|
||||
Дуга
|
||||
Желаемая система
|
||||
Жизненный цикл
|
||||
Жизненный цикл интереса
|
||||
Жизненный цикл угрозы
|
||||
Зависимое множество аксиом
|
||||
Задача
|
||||
Закон композиции
|
||||
Знаковая система
|
||||
Идентификация
|
||||
Идеолог
|
||||
Идея
|
||||
Идея абстрактного выбора
|
||||
Иерархическая структура
|
||||
Измельчение графа термов
|
||||
Изменение
|
||||
Изоморфизм
|
||||
Изофункциональные методы
|
||||
Ильковский
|
||||
Имплицитная форма
|
||||
Интегрированные объекты
|
||||
Интенсионализация
|
||||
Интересы
|
||||
Интерпретант
|
||||
Интерпретатор
|
||||
Интерпретация
|
||||
Интерпретированная теория
|
||||
Информационная система
|
||||
Инцесс
|
||||
Инцессант
|
||||
Искусственная система
|
||||
Исследовательский подход
|
||||
Истинное подмножество
|
||||
Исток графа термов
|
||||
Исходная сущность
|
||||
Исходное отношение
|
||||
Исходное понятие
|
||||
Исходное утверждение
|
||||
КА
|
||||
КАК-ориентированная система
|
||||
КАНС
|
||||
Карта понятий
|
||||
Картезиан
|
||||
Каталог методов
|
||||
Категорическая теория
|
||||
Качественная проблема
|
||||
Качественные характеристики теории
|
||||
Квазиаксиома
|
||||
Классификация
|
||||
Классификация множества объектов
|
||||
Классы концептуальных схем
|
||||
Классы систем
|
||||
Класс эквивалентности
|
||||
Количественная проблема
|
||||
Комплексное управление развитием систем
|
||||
Композиция
|
||||
Композиция-2
|
||||
Конвенция
|
||||
Конкрест
|
||||
Конкретант
|
||||
Конкретизация
|
||||
Конкретор
|
||||
Конкреция
|
||||
Конституента
|
||||
Конструкт
|
||||
Конструкт-конструктные конструкции
|
||||
Конструкт-содержантные конструкции
|
||||
Конструкция
|
||||
Концепт
|
||||
Концептуализация
|
||||
Концептуализм
|
||||
Концептуальная конструкция
|
||||
Концептуальная модель
|
||||
Концептуальная схема
|
||||
Концептуальное мышление
|
||||
Концептуальное проектирование
|
||||
Концептуальный анализ
|
||||
Концептуальный анализ и синтез
|
||||
Концептуальный аппарат
|
||||
Корректировка целей
|
||||
Кортеж
|
||||
КП
|
||||
Критерий
|
||||
Критический элемент
|
||||
Круги Эйлера
|
||||
КС
|
||||
КТО-ориентированная система
|
||||
КУРС
|
||||
Лингвистическая интерпретация
|
||||
Логика
|
||||
Логические средства
|
||||
Логическое мышление
|
||||
ЛПР
|
||||
Максимальное измельчение
|
||||
МАКС система
|
||||
Малая проекция
|
||||
Математическая структура
|
||||
Математическая теория
|
||||
Математические средства КП СОУ
|
||||
Материальная интерпретация
|
||||
Машинные средства КП СОУ
|
||||
М-граф
|
||||
Мегатеория
|
||||
Мезотеория
|
||||
Меновая власть
|
||||
Мероприятия по достижению частных целей
|
||||
Метатеорема
|
||||
Метатеория
|
||||
Метаязык
|
||||
Метод
|
||||
Метод КП СОУ
|
||||
Методология
|
||||
Механизм
|
||||
Механизм парирования угрозы
|
||||
Микротеория
|
||||
Минимальный род структуры
|
||||
Множество
|
||||
Множество сечений
|
||||
Множество-степень
|
||||
Модель
|
||||
Модель желаемого выхода
|
||||
Морфологическое отношение
|
||||
Мощность множества
|
||||
Мультиграф
|
||||
Мышление
|
||||
Нагрузка
|
||||
Надпроцесс
|
||||
Надсистема
|
||||
Направления развёртывания
|
||||
Н-арное отношение
|
||||
Независимое множество аксиом
|
||||
Непересекающиеся множества
|
||||
Непротиворечивая теория
|
||||
Нереализованные функции
|
||||
Несовместная теория
|
||||
Неформальная аксиоматическая теория
|
||||
Нижнее замыкание вершины графа
|
||||
Н-местное отношение
|
||||
Нормативная методология
|
||||
Нормативная теория
|
||||
Нормативное мышление
|
||||
Нормативный подход
|
||||
Область значений
|
||||
Область определения
|
||||
Область отправления
|
||||
Область прибытия
|
||||
Обратная связь
|
||||
Обстоятельства
|
||||
Общая теория систем
|
||||
Общезначимая формула
|
||||
Объединение
|
||||
Объект
|
||||
Объектная регламентация
|
||||
Объектные теории
|
||||
Объект управления
|
||||
Объём теории
|
||||
Объяснительная теория
|
||||
Объясняемая теория
|
||||
Ограничение
|
||||
Онтологизация
|
||||
Онтология
|
||||
Оператор
|
||||
Операции КАНС
|
||||
Операциональная схема синтеза
|
||||
Операциональное понятие
|
||||
Операция
|
||||
Операция малая проекция
|
||||
Операция повышения размерности
|
||||
Определяющее свойство множества
|
||||
Оптимизация
|
||||
Организационное управление
|
||||
Организация
|
||||
Ориентированный путь
|
||||
Основание классификации
|
||||
Отвлечённые теории
|
||||
Открытая система
|
||||
Относительное дополнение
|
||||
Отношение
|
||||
Отношение порядка
|
||||
Отношение эквивалентности
|
||||
Отображение
|
||||
Отождествление
|
||||
Отрицание множества
|
||||
ОТСУ
|
||||
ПВР
|
||||
Первичная функция
|
||||
Первичные термины теории
|
||||
Перепостулирование
|
||||
Пересекающиеся множества
|
||||
Пересечение множеств
|
||||
Петля
|
||||
ПО
|
||||
Поддержание
|
||||
Подмножество
|
||||
Подсистема
|
||||
Подтеория
|
||||
Подфункция
|
||||
Политика
|
||||
Полифункциональные методы
|
||||
Полная согласованная интерпретация
|
||||
Полная теория
|
||||
Полный вход
|
||||
Полный выход
|
||||
Понятие
|
||||
Порождающие структуры
|
||||
Постулирование
|
||||
Потенциальная связь
|
||||
Потенциально связанные процессы
|
||||
Потенциальный критический элемент системы
|
||||
Предмет
|
||||
Предметная область
|
||||
Предметный язык
|
||||
Предположение
|
||||
Преобразование
|
||||
Препятствие
|
||||
Прескриптивное использование теории
|
||||
Прескриптивный подход
|
||||
Приложение теории
|
||||
Принадлежность
|
||||
Принцип дополнительности
|
||||
ПРО
|
||||
Проблема
|
||||
Прогнозный сценарий
|
||||
Проект
|
||||
Проектирование СОУ
|
||||
Проектный подход
|
||||
Проект СОУ
|
||||
Проекция родовой структуры
|
||||
Произведение множеств
|
||||
Производное понятие
|
||||
Производные понятия n-го ранга
|
||||
Производные термы
|
||||
Противоречивая теория
|
||||
Процесс
|
||||
Процесс КП конкретной СОУ
|
||||
Процесс ограничения
|
||||
Процессор
|
||||
Процесс с необеспеченными связями
|
||||
Процесс с ролевыми отношениями
|
||||
ПРС
|
||||
ПТ
|
||||
Пустое множество
|
||||
Разбиение множества объектов
|
||||
Разбиение множества объектов по явному основанию
|
||||
Развёрнутое графовое представление
|
||||
Развёрнутый род структуры
|
||||
Развёртывание теории
|
||||
Развивающаяся система
|
||||
Развитие
|
||||
Разнообразие
|
||||
Ранг
|
||||
Ранжирование
|
||||
Расчленённые множества
|
||||
Редукционизм
|
||||
Редукция
|
||||
Реконструкция
|
||||
Ресурс
|
||||
Рефлексивное отношение
|
||||
Решение
|
||||
Р-интерпретация
|
||||
Родовая структура
|
||||
Родовое отношение
|
||||
Родоструктурная экспликация
|
||||
Род структуры
|
||||
Рост
|
||||
Рутинная операция
|
||||
СА
|
||||
Самоорганизующаяся система
|
||||
САПР
|
||||
Свободное мышление
|
||||
Свойство
|
||||
Свойство икса
|
||||
Связь
|
||||
Семантическая сеть
|
||||
Сеть
|
||||
Сеть ПРО
|
||||
Сеть процессов
|
||||
Сеть процессов с ролевыми отношениями
|
||||
Сеть частных целей
|
||||
Сечение
|
||||
Сильные и слабые формы концептуализации
|
||||
Симметричное отношение
|
||||
Синтаксический язык
|
||||
Синтез
|
||||
Синтез родов структур
|
||||
С-интерпретация
|
||||
Система
|
||||
Система автоматизированного проектирования
|
||||
Система восстановления
|
||||
Система организационного управления
|
||||
Система поддержания
|
||||
Система процессов
|
||||
Система развития
|
||||
Система стратегического планирования
|
||||
Система стратегического управления
|
||||
Система управления базами данных
|
||||
Система функционирования
|
||||
Системные классы
|
||||
Системные объекты
|
||||
Системный анализ
|
||||
Слабоструктурированная проблема
|
||||
Словесная форма
|
||||
Сложная система
|
||||
Сложная структура
|
||||
Событие
|
||||
Совершенствование
|
||||
Совместимая теория
|
||||
Содержание
|
||||
Содержант
|
||||
Содержательно-дедуктивное развёртывание
|
||||
Соединение
|
||||
Соединение множеств
|
||||
Соискатель ресурсов
|
||||
Соответствие
|
||||
Состояние организации
|
||||
СОУ
|
||||
Сравнение
|
||||
Среда
|
||||
Средства КП СОУ
|
||||
Срез графа термов
|
||||
Срез подграфа термов
|
||||
Сток графа термов
|
||||
Стратегические цели
|
||||
Стратегическое планирование
|
||||
Стратегическое состояние
|
||||
Стратегия
|
||||
Стратификация
|
||||
Строгое включение
|
||||
Структ
|
||||
Структура
|
||||
Структура, определённая отношением порядка
|
||||
Ступень
|
||||
СУБД
|
||||
Субъект
|
||||
Субъектная регламентация
|
||||
Субъект целеполагания
|
||||
Сумма
|
||||
Сущность
|
||||
Схема
|
||||
Схематизация
|
||||
Творческая операция
|
||||
Тектология
|
||||
Тело теории
|
||||
Теорема
|
||||
Теоретико-множественная интерпретация
|
||||
Теоретико-множественная экспликация
|
||||
Теоретико-множественные операции
|
||||
Теоретико-системная экспликация
|
||||
Теоретико-системное мышление
|
||||
Теоретическое описание объекта
|
||||
Теория
|
||||
Теория множеств
|
||||
Теория систем
|
||||
Терм
|
||||
Терминальная теория
|
||||
Терм-функция
|
||||
Термы n-го ранга
|
||||
Техническая система
|
||||
Техногенема n-го порядка
|
||||
Технология КП СОУ
|
||||
Типизация
|
||||
Типология
|
||||
Типы мышления
|
||||
ТМИ
|
||||
ТМФ
|
||||
Толчок Ильковского
|
||||
Топология
|
||||
Точка зрения
|
||||
Транзитивное отношение
|
||||
Требование проблемы
|
||||
Требования к проекту СОУ
|
||||
Треугольник Фреге
|
||||
ТСК
|
||||
Угроза
|
||||
Универсальное множество
|
||||
Универсум
|
||||
Упорядоченная n-ка
|
||||
Упорядоченная пара
|
||||
Управление
|
||||
Управление процессом достижения цели
|
||||
Урманцев
|
||||
Уровень эксплицитности
|
||||
Условие проблемы
|
||||
Фактормножество
|
||||
Фактор-структура
|
||||
Фактор-структура n-го ранга
|
||||
Феноменология
|
||||
Формализация
|
||||
Формальная система
|
||||
Формальная теория
|
||||
Формально-дедуктивное развёртывание
|
||||
Формальное мышление
|
||||
Форма от x
|
||||
Формы концептуализации
|
||||
Формы мышления
|
||||
Функционально-методное отношение
|
||||
Функциональные свойства
|
||||
Функционирование
|
||||
Функция
|
||||
Целевой выход
|
||||
Целевой терм
|
||||
Целенаправленная система
|
||||
Целенаправленная система с ОС
|
||||
Целеустремлённая система
|
||||
Цель
|
||||
Цикл
|
||||
ЦНС
|
||||
Частная цель
|
||||
Член множества
|
||||
Членство
|
||||
Шкала множеств
|
||||
Шкала ступеней
|
||||
Эквивалентность
|
||||
Экспликация
|
||||
Эксплицитное мышление
|
||||
Экстенсионализация
|
||||
Элемент
|
||||
Элементарная теория
|
||||
Элементарный объект
|
||||
Элемент входа
|
||||
Элемент выхода
|
||||
Эмпирический материал
|
||||
Эпистемология
|
||||
Этапы разработки концептуальной схемы
|
||||
Этиология
|
||||
Ядро
|
||||
Язык-объект
|
||||
Ярус графа
|
||||
Тезаурус по экологии:
|
||||
жизнедеятельность человека
|
||||
технологический процесс
|
||||
пространственный объем
|
||||
естественная экологическая система
|
||||
ценность природного объекта
|
||||
ценность природной среды
|
||||
субъект сохраняемой природной среды
|
||||
экологически значимый отход
|
||||
естественный состав природной среды
|
||||
искусственное изменение природной среды
|
||||
повреждение природного объекта
|
||||
восстановление природного объекта
|
||||
восстановление природной среды
|
||||
восстановимое изменение природной среды
|
||||
физически необратимое изменение природной среды
|
||||
источник воздействия хозяйственной деятельности
|
||||
благоприятный экологический эффект хозяйственной деятельности
|
||||
резерв ресурса целевой экологической зоны под декларации
|
||||
установленный запас ресурса целевой экологической зоны
|
||||
заявленное воздействие
|
||||
экологический аудит
|
||||
благоприятная природная среда жизнедеятельности
|
||||
благоприятная природная среда хозяйственной деятельности
|
||||
благоприятная экологическая зона
|
||||
виды экологической экспертизы
|
||||
государственное оперативное экологическое управление
|
||||
государственное экологическое управление
|
||||
границы применимости методики
|
||||
деклараций о выбросах, сбросах и физических воздействиях
|
||||
декларируемые воздействия
|
||||
декларируемые опосредованные воздействия
|
||||
документы методического обеспечения экологических отношений
|
||||
документы оценки воздействия
|
||||
документы экологического планирования
|
||||
Единый экологический реестр
|
||||
законодательство в области экологических отношений
|
||||
заключение экологического нормоконтроля
|
||||
зона экологического бедствия
|
||||
информационное обеспечение ЭО
|
||||
карта экологического зонирования
|
||||
муниципальное оперативное экологическое управление
|
||||
муниципальное экологическое управление
|
||||
муниципальный экологический контроль
|
||||
муниципальный экологический мониторинг
|
||||
научное обеспечение экологических отношений
|
||||
научный экологический совет
|
||||
неблагоприятная экологическая зона
|
||||
неопределенная экологическая зона
|
||||
неприемлемая экологическая зона
|
||||
общественное экологическое управление
|
||||
общественные экологические объединения
|
||||
общественный экологический контроль
|
||||
общественный экологический мониторинг
|
||||
объект оценки
|
||||
объект экологического нормоконтроля
|
||||
объект, подлежащий обязательному экологическому страхованию
|
||||
обязательное экологическое страхование
|
||||
опасная экологическая зона
|
||||
организации, осуществляющие научное обеспечение экологических отношений
|
||||
основания для ответственности в области экологических отношений
|
||||
приемлемая экологическая зона
|
||||
программа мероприятий по благоприятному изменению природной среды и снижению риска возникновения негативных экологических последствий
|
||||
производственное оперативное экологическое управление
|
||||
производственное экологическое управление
|
||||
производственный экологический контроль
|
||||
производственный экологический мониторинг
|
||||
прокурорский экологический надзор
|
||||
разрешение на воздействие на природную среду
|
||||
разрешение на выбросы, сбросы и физические воздействия
|
||||
разрешенные воздействия
|
||||
разрешенные опосредованные воздействия
|
||||
региональный экологический нормоконтроль
|
||||
региональное оперативное экологическое управление
|
||||
региональное экологическое нормирование
|
||||
региональное экологическое управление
|
||||
региональные экологические советы
|
||||
региональный экологический контроль
|
||||
региональный экологический мониторинг
|
||||
региональный экологический надзор
|
||||
способы возмещения экологического вреда
|
||||
стратегическая экологическая оценка
|
||||
субъект обязательного экологического страхования
|
||||
схема экологического зонирования
|
||||
технические и технологические экологические нормативы
|
||||
территория зонирования
|
||||
условия применимости норматива
|
||||
участники экологических отношений
|
||||
федеральный экологический нормоконтроль
|
||||
федеральное оперативное экологическое управление
|
||||
федеральное экологическое нормирование
|
||||
федеральное экологическое управление
|
||||
федеральный экологический контроль
|
||||
федеральный экологический мониторинг
|
||||
федеральный экологический надзор
|
||||
первичные нормативы благоприятной природной среды жизнедеятельности
|
||||
первичные нормативы благоприятной природной среды хозяйственной деятельности
|
||||
первичные нормативы опасной природной среды жизнедеятельности
|
||||
первичные нормативы приемлемой природной среды жизнедеятельности
|
||||
первичные нормативы сохраняемой природной среды
|
||||
целевая экологическая зона
|
||||
экологическая плата
|
||||
экологическая экспертиза
|
||||
экологический нормоконтроль
|
||||
экологические нормативы воздействия хозяйственной деятельности
|
||||
экологические нормативы последствий воздействия
|
||||
экологические нормативы природной среды
|
||||
экологическое зонирование по нормативам
|
||||
экологическое зонирование по целям
|
||||
экологическое нормирование
|
||||
экологическое управление
|
||||
экспертное экологическое заключение
|
||||
Муниципальный экологический план
|
||||
Ежегодный отчет о состоянии природной среды на территории Российской Федерации
|
||||
Ежегодный отчет о состоянии природной среды на территории муниципальных образований
|
||||
Порядок государственной аккредитации организаций, осуществляющих научное обеспечение экологических отношений
|
||||
Порядок государственной аккредитации экспертных организаций и экспертов в области экологических отношений
|
||||
Порядок научного обеспечения экологических отношений
|
||||
Положение о Научном экологическом совете
|
||||
Положение об экологическом нормоконтроле
|
||||
Положение об экологической экспертизе
|
||||
Методика расчета совокупного опосредованного воздействия на природную среду
|
||||
Положение об Едином экологическом реестре
|
||||
Методика оценки воздействия на природную среду
|
||||
Положение о мерах по благоприятному изменению природной среды и снижению риска возникновения негативных экологических последствий
|
||||
Перечень видов хозяйственной деятельности, для которых устанавливаются экологические нормативы природной среды хозяйственной деятельности
|
||||
Порядок утверждения экологических нормативов природной среды хозяйственной деятельности
|
||||
Порядок утверждения технических и технологических экологических нормативов
|
||||
Порядок утверждения экологических нормативов хозяйственной деятельности
|
||||
Методические рекомендации по разработке муниципальных экологических планов
|
||||
Методические рекомендации по разработке производственных экологических планов
|
||||
Федеральный экологический план
|
||||
Порядок разработки федеральных экологических планов
|
||||
Порядок проведения стратегической экологической оценки
|
||||
Методика расчета воздействия по опосредованному воздействию
|
||||
Порядок оформления разрешений на воздействие на природную среду
|
||||
Методика определения и изменения размера экологической платы
|
||||
Методика экологического мониторинга
|
||||
Методика оперативного экологического управления
|
||||
Методика экологического контроля
|
||||
Методика оценки экологического ущерба
|
||||
Правила экологического страхования
|
||||
Ежегодный отчет о состоянии природной среды на территории субъекта Российской Федерации
|
||||
Положение о Региональном экологическом совете субъекта Российской Федерации
|
||||
Порядок создания региональных экологических советов
|
||||
Порядок проведения публичных слушаний при рассмотрении проектов региональных схем экологического зонирования
|
||||
Региональный экологический план
|
||||
Порядок разработки региональных экологических планов
|
||||
Производственный экологический план
|
||||
жизнедеятельность
|
||||
технология
|
||||
хозяйственная деятельность
|
||||
производственная инфраструктура
|
||||
объект хозяйственной деятельности
|
||||
природный объект
|
||||
природная среда
|
||||
природная среда жизнедеятельности
|
||||
природная среда хозяйственной деятельности
|
||||
природный объект особого значения
|
||||
особо охраняемая природная территория
|
||||
сохраняемая природная среда
|
||||
действующий объект хозяйственной деятельности
|
||||
новый объект хозяйственной деятельности
|
||||
субъект хозяйственной деятельности
|
||||
работник субъекта хозяйственной деятельности
|
||||
отход
|
||||
выброс
|
||||
сброс
|
||||
размещение отходов
|
||||
размещенные отходы
|
||||
объект размещения отходов
|
||||
воздействие хозяйственной деятельности
|
||||
физическое воздействие
|
||||
мероприятие по благоприятному изменению природной среды
|
||||
технологически невосстановимое изменение природной среды
|
||||
опосредующая природная среда
|
||||
опосредующие свойства природной среды
|
||||
опосредованное воздействие
|
||||
территория опосредованного воздействия хозяйственной деятельности
|
||||
совокупное опосредованное воздействие на природную среду
|
||||
экологический эффект хозяйственной деятельности
|
||||
экологическое последствие
|
||||
экологическое правонарушение
|
||||
экологический вред
|
||||
экологический ущерб
|
||||
наилучшая доступная технология
|
||||
негативный экологический эффект хозяйственной деятельности
|
||||
негативное экологическое последствие хозяйственной деятельности
|
||||
реципиент воздействия
|
||||
мероприятие по снижению риска возникновения негативных экологических последствий
|
||||
мера по благоприятному изменению природной среды и снижению риска возникновения негативных экологических последствий
|
||||
экологическое отношение
|
||||
субъекты
|
||||
критерий незначительности воздействия
|
||||
незначительное воздействие хозяйственной деятельности
|
||||
критерий декларирования опосредованного воздействия
|
||||
первичный норматив
|
||||
экологический норматив
|
||||
баланс интересов
|
||||
экологическое зонирование
|
||||
экологических зон
|
||||
базовое состояние природной среды
|
||||
ресурс природной среды
|
||||
распределяемый ресурс природной ресурс
|
||||
квота опосредованного воздействия
|
||||
утраченный экологический ресурс
|
||||
критерий корректировки программ мероприятий
|
||||
экологическая экспертная оценка
|
||||
экологический экспертный расчет
|
||||
экологический нормоконтроль
|
||||
управленческое решение
|
||||
экологический мониторинг
|
||||
оперативное экологическое управление
|
||||
экологический контроль
|
||||
экологический надзор
|
||||
|
83
tests/t_conceptapi.py
Normal file
83
tests/t_conceptapi.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
''' 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()
|
32
tests/t_context.py
Normal file
32
tests/t_context.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
''' 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, [])
|
44
tests/t_reference.py
Normal file
44
tests/t_reference.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
''' 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)
|
107
tests/t_resolver.py
Normal file
107
tests/t_resolver.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
''' 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 utility 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='человеку')
|
||||
refs_list = [ref, ref, ref, ref]
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-1), 0, refs_list), '!Некорректное смещение: -1!')
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=1), 3, refs_list), '!Некорректное смещение: 1!')
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=1), 0, refs_list), 'умному')
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=2), 0, refs_list), 'умному')
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=3), 0, refs_list), 'умному')
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-1), 3, refs_list), 'умному')
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-2), 3, refs_list), 'умному')
|
||||
self.assertEqual(resolve_syntactic(SyntacticReference(text='умный', referral_offset=-3), 3, refs_list), 'умному')
|
||||
|
||||
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}'), 'человек')
|
18
tests/t_rumodel.py
Normal file
18
tests/t_rumodel.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
''' 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')
|
446
tests/t_ruparser.py
Normal file
446
tests/t_ruparser.py
Normal file
|
@ -0,0 +1,446 @@
|
|||
''' 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()
|
62
tests/t_syntax.py
Normal file
62
tests/t_syntax.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
''' 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()
|
Loading…
Reference in New Issue
Block a user