Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Argument | Description
**‑e**, **‑‑end-line** | the end line to be analyzed. The default value is `None`, which meant to handle file by the end.
**‑‑new-format** | the argument determines whether the tool should use the _new format_. _New format_ means separating the result by the files to allow getting quality and observed issues for each file separately. The default value is `False`.
**‑‑history** | JSON string with a list of issues for each language. For each issue its class and quantity are specified. Example: `--history "{\"python\": [{\"origin_class\": \"SC200\", \"number\": 20}, {\"origin_class\": \"WPS314\", \"number\": 3}]}"`
**‑‑with‑all‑categories** | Without this flag, all issues will be categorized into 5 main categories: `CODE_STYLE`, `BEST_PRACTICES`, `ERROR_PRONE`, `COMPLEXITY`, `INFO`.

The output examples:

Expand Down
4 changes: 4 additions & 0 deletions src/python/common/tool_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class RunToolArgument(Enum):
'Json string, which contains lists of issues in the previous submissions '
'for other tasks for one user.')

WITH_ALL_CATEGORIES = ArgumentsInfo(None, '--with-all-categories',
'Without this flag, all issues will be categorized into 5 main categories: '
'CODE_STYLE, BEST_PRACTICES, ERROR_PRONE, COMPLEXITY, INFO.')

SOLUTIONS_FILE_PATH = ArgumentsInfo(None, 'solutions_file_path',
'Local XLSX-file or CSV-file path. '
'Your file must include column-names: '
Expand Down
1 change: 1 addition & 0 deletions src/python/review/application_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class ApplicationConfig:
allow_duplicates: bool
n_cpu: int
inspectors_config: dict
with_all_categories: bool
start_line: int = 1
end_line: Optional[int] = None
new_format: bool = False
Expand Down
32 changes: 26 additions & 6 deletions src/python/review/inspectors/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,47 @@

@unique
class IssueType(Enum):
# Code style issues
CODE_STYLE = 'CODE_STYLE'
LINE_LEN = 'LINE_LEN'

# Best practice issues
BEST_PRACTICES = 'BEST_PRACTICES'
ERROR_PRONE = 'ERROR_PRONE'
FUNC_LEN = 'FUNC_LEN'
LINE_LEN = 'LINE_LEN'
CYCLOMATIC_COMPLEXITY = 'CYCLOMATIC_COMPLEXITY'
BOOL_EXPR_LEN = 'BOOL_EXPR_LEN'
CLASS_RESPONSE = 'CLASS_RESPONSE'
METHOD_NUMBER = 'METHOD_NUMBER'

# Error-prone issues
ERROR_PRONE = 'ERROR_PRONE'

# Code complexity issues
COMPLEXITY = 'COMPLEXITY'
ARCHITECTURE = 'ARCHITECTURE'
CYCLOMATIC_COMPLEXITY = 'CYCLOMATIC_COMPLEXITY'
INHERITANCE_DEPTH = 'INHERITANCE_DEPTH'
CHILDREN_NUMBER = 'CHILDREN_NUMBER'
WEIGHTED_METHOD = 'WEIGHTED_METHOD'
COUPLING = 'COUPLING'
COHESION = 'COHESION'
CLASS_RESPONSE = 'CLASS_RESPONSE'
METHOD_NUMBER = 'METHOD_NUMBER'
MAINTAINABILITY = 'MAINTAINABILITY'

# Info issues
INFO = 'INFO'

# Others
UNDEFINED = 'UNDEFINED'
ARCHITECTURE = 'ARCHITECTURE' # TODO: Distribute into one of the main types

def __str__(self) -> str:
return ' '.join(self.value.lower().split('_'))

def to_main_type(self) -> 'IssueType':
"""
Converts the issue type to main issue type.
Main issue types: CODE_STYLE, BEST_PRACTICES, ERROR_PRONE, COMPLEXITY, INFO.
"""
return get_main_category_by_issue_type(self)


ISSUE_TYPE_TO_MAIN_CATEGORY = {
# CODE_STYLE
Expand All @@ -60,6 +77,9 @@ def __str__(self) -> str:
IssueType.CHILDREN_NUMBER: IssueType.COMPLEXITY,
IssueType.INHERITANCE_DEPTH: IssueType.COMPLEXITY,
IssueType.ARCHITECTURE: IssueType.COMPLEXITY,

# INFO
IssueType.INFO: IssueType.INFO,
}


Expand Down
2 changes: 2 additions & 0 deletions src/python/review/quality/penalty.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
IssueType.MAINTAINABILITY: 0.3,
IssueType.METHOD_NUMBER: 0.2,
IssueType.WEIGHTED_METHOD: 0.2,
IssueType.UNDEFINED: 0,
IssueType.INFO: 0,
}


Expand Down
6 changes: 3 additions & 3 deletions src/python/review/reviewers/perform_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ def perform_and_print_review(path: Path,

if OutputFormat.JSON == output_format:
if config.new_format:
print_review_result_as_multi_file_json(review_result)
print_review_result_as_multi_file_json(review_result, config)
else:
print_review_result_as_json(review_result)
print_review_result_as_json(review_result, config)
else:
print_review_result_as_text(review_result, path)
print_review_result_as_text(review_result, path, config)

# Don't count INFO issues too
return len(list(filter(lambda issue: issue.type != IssueType.INFO, review_result.all_issues)))
Expand Down
26 changes: 18 additions & 8 deletions src/python/review/reviewers/utils/print_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
from typing import Any, Dict, List

from src.python.evaluation.inspectors.common.statistics import PenaltyIssue
from src.python.review.application_config import ApplicationConfig
from src.python.review.common.file_system import get_file_line
from src.python.review.inspectors.inspector_type import InspectorType
from src.python.review.inspectors.issue import BaseIssue, IssueType
from src.python.review.reviewers.review_result import ReviewResult


def print_review_result_as_text(review_result: ReviewResult,
path: Path) -> None:
path: Path,
config: ApplicationConfig) -> None:
heading = f'\nReview of {str(path)} ({len(review_result.all_issues)} violations)'
print(heading)

Expand All @@ -32,9 +34,13 @@ def print_review_result_as_text(review_result: ReviewResult,
issue.line_no,
).strip()

issue_type = issue.type
if not config.with_all_categories:
issue_type = issue_type.to_main_type()

print(f'{issue.line_no} : '
f'{issue.column_no} : '
f'{issue.type.value} : '
f'{issue_type.value} : '
f'{issue.inspector_type.value} : '
f'{issue.origin_class} : '
f'{issue.description} : '
Expand All @@ -48,7 +54,7 @@ def print_review_result_as_text(review_result: ReviewResult,
print(review_result.general_quality, end='')


def print_review_result_as_json(review_result: ReviewResult) -> None:
def print_review_result_as_json(review_result: ReviewResult, config: ApplicationConfig) -> None:
issues = review_result.all_issues

issues.sort(key=lambda issue: issue.line_no)
Expand All @@ -65,12 +71,12 @@ def print_review_result_as_json(review_result: ReviewResult) -> None:
if quality_with_penalty != quality_without_penalty:
influence_on_penalty = review_result.general_punisher.get_issue_influence_on_penalty(issue.origin_class)

output_json['issues'].append(convert_issue_to_json(issue, influence_on_penalty))
output_json['issues'].append(convert_issue_to_json(issue, config, influence_on_penalty))

print(json.dumps(output_json))


def print_review_result_as_multi_file_json(review_result: ReviewResult) -> None:
def print_review_result_as_multi_file_json(review_result: ReviewResult, config: ApplicationConfig) -> None:
file_review_result_jsons = []

review_result.file_review_results.sort(key=lambda result: result.file_path)
Expand All @@ -94,7 +100,7 @@ def print_review_result_as_multi_file_json(review_result: ReviewResult) -> None:
if quality_with_penalty != quality_without_penalty:
influence_on_penalty = file_review_result.punisher.get_issue_influence_on_penalty(issue.origin_class)

file_review_result_json['issues'].append(convert_issue_to_json(issue, influence_on_penalty))
file_review_result_json['issues'].append(convert_issue_to_json(issue, config, influence_on_penalty))

quality_without_penalty = review_result.general_quality.quality_type
quality_with_penalty = review_result.general_punisher.get_quality_with_penalty(quality_without_penalty)
Expand All @@ -121,16 +127,20 @@ class IssueJsonFields(Enum):
INFLUENCE_ON_PENALTY = 'influence_on_penalty'


def convert_issue_to_json(issue: BaseIssue, influence_on_penalty: int = 0) -> Dict[str, Any]:
def convert_issue_to_json(issue: BaseIssue, config: ApplicationConfig, influence_on_penalty: int = 0) -> Dict[str, Any]:
line_text = get_file_line(issue.file_path, issue.line_no)

issue_type = issue.type
if not config.with_all_categories:
issue_type = issue_type.to_main_type()

return {
IssueJsonFields.CODE.value: issue.origin_class,
IssueJsonFields.TEXT.value: issue.description,
IssueJsonFields.LINE.value: line_text,
IssueJsonFields.LINE_NUMBER.value: issue.line_no,
IssueJsonFields.COLUMN_NUMBER.value: issue.column_no,
IssueJsonFields.CATEGORY.value: issue.type.value,
IssueJsonFields.CATEGORY.value: issue_type.value,
IssueJsonFields.INFLUENCE_ON_PENALTY.value: influence_on_penalty,
}

Expand Down
5 changes: 5 additions & 0 deletions src/python/review/run_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ def configure_arguments(parser: argparse.ArgumentParser) -> None:
help=RunToolArgument.HISTORY.value.description,
type=str)

parser.add_argument(RunToolArgument.WITH_ALL_CATEGORIES.value.long_name,
help=RunToolArgument.WITH_ALL_CATEGORIES.value.description,
action='store_true')


def configure_logging(verbosity: VerbosityLevel) -> None:
if verbosity is VerbosityLevel.ERROR:
Expand Down Expand Up @@ -150,6 +154,7 @@ def main() -> int:
end_line=args.end_line,
new_format=args.new_format,
history=args.history,
with_all_categories=args.with_all_categories,
)

n_issues = perform_and_print_review(args.path, OutputFormat(args.format), config)
Expand Down
1 change: 1 addition & 0 deletions test/python/inspectors/test_local_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def config() -> ApplicationConfig:
allow_duplicates=False,
n_cpu=1,
inspectors_config={"n_cpu": 1},
with_all_categories=False,
)


Expand Down