Skip to content

Commit

Permalink
Merge pull request #4 from CoderChen01/cjj_dev
Browse files Browse the repository at this point in the history
Complete the config command and json data export
  • Loading branch information
CoderChen01 authored Aug 31, 2023
2 parents 8012abf + 37d6523 commit 58f9692
Show file tree
Hide file tree
Showing 36 changed files with 546 additions and 126 deletions.
4 changes: 0 additions & 4 deletions opendigger_pycli/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@
from .commands.config_cmd import config
from .commands.display_cmd import display
from .commands.export_cmd import export
from .commands.monitor_cmd import monitor
from .commands.report_cmd import report

opendigger.add_command(config)

query.add_command(display)
query.add_command(export)
query.add_command(report)
query.add_command(monitor)
107 changes: 103 additions & 4 deletions opendigger_pycli/cli/commands/config_cmd.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,107 @@
import typing as t # noqa: F401
import typing as t
from dataclasses import fields

import click
from click.shell_completion import CompletionItem

from opendigger_pycli.console import CONSOLE

@click.command("config")
def config():
pass
from ..base import pass_environment
from ..config import ALL_CONFIGS

if t.TYPE_CHECKING:
from click.core import Context, Parameter

from opendigger_pycli.datatypes.config import BaseConfig

from ..base import Environment


def config_shell_completion(
ctx: t.Optional["Context"], param: t.Optional["Parameter"], incomplete: str
) -> t.List[CompletionItem]:
incomplete_splited = incomplete.rsplit(".", 1)[0]
if incomplete_splited == "":
return [
CompletionItem(config_dataclass_key) for config_dataclass_key in ALL_CONFIGS
]
is_key = False
last_config_dataclass: t.Optional[BaseConfig] = None
for config_dataclass_key in ALL_CONFIGS:
if incomplete_splited.startswith(config_dataclass_key):
if incomplete_splited != config_dataclass_key:
return [CompletionItem(config_dataclass_key + ".")]
else:
is_key = True
last_config_dataclass = ALL_CONFIGS[config_dataclass_key] # type: ignore
break
else:
continue

if not is_key and last_config_dataclass is None:
return []

if incomplete[:-1] == incomplete_splited:
return [CompletionItem(f"{incomplete_splited}.{field.name}") for field in fields(last_config_dataclass)] # type: ignore

for field in fields(last_config_dataclass): # type: ignore
if incomplete in f"{incomplete_splited}.{field.name}":
return [CompletionItem(f"{incomplete_splited}.{field.name}")]

return []


def parse_config_key(key: str) -> t.Tuple[str, str]:
section_name, config_key = key.rsplit(".", 1)
return section_name, config_key


def check_config_setting(
ctx: "Context", param: "Parameter", values: t.List[t.Tuple[str, str]]
) -> t.List[t.Tuple[str, str]]:
for value in values:
try:
section_name, config_key = parse_config_key(value[0])
except Exception:
raise click.BadParameter(f"{value[0]} is not a valid config key")
else:
if section_name not in ALL_CONFIGS or config_key not in [
field.name for field in fields(ALL_CONFIGS[section_name])
]:
raise click.BadParameter(f"{value[0]} is not a valid config key")
return values


@click.command("config") # type: ignore
@click.option(
"--set",
"-s",
"config_settings",
type=(str, str),
multiple=True,
callback=check_config_setting,
shell_complete=config_shell_completion,
help="Set config value",
required=True,
)
@click.option(
"--persist/--no-persist",
"-p/-n",
is_flag=True,
default=True,
help="Set config value persistently",
)
@pass_environment
def config(
env: "Environment", config_settings: t.List[t.Tuple[str, str]], persist: bool
) -> None:
"""Set config value"""
for config_setting in config_settings:
key, value = config_setting
section_name, key = parse_config_key(key)
env.dlog(f"set config: {section_name}.{key}={value}")
env.set_config(section_name, key, value, is_persist=persist)
env.dlog(f"finished to set config: {section_name}.{key}={value}")

CONSOLE.print("[green]Config set successfully[/]")
CONSOLE.print(env.cli_config)
2 changes: 1 addition & 1 deletion opendigger_pycli/cli/commands/display_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from opendigger_pycli.console.print_indicator import SURPPORTED_DISPLAY_FORMAT_TYPE
from opendigger_pycli.results.query import QueryResults

from .base import Environment
from ..base import Environment


@click.command("display", help="Display metrics")
Expand Down
36 changes: 30 additions & 6 deletions opendigger_pycli/cli/commands/export_cmd.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
import typing as t
from pathlib import Path

import click

from opendigger_pycli.exporters import (
SURPPORTED_EXPORT_FORMAT_TYPE,
SURPPORTED_EXPORT_FORMATS,
)
from opendigger_pycli.results.export import ExportResult
from opendigger_pycli.utils.decorators import processor

from ..base import pass_environment

if t.TYPE_CHECKING:
from opendigger_pycli.results.query import QueryResults

from ..base import Environment


@click.command("export", help="Export metrics")
@click.option(
"--format",
"-f",
"format_name",
type=click.Choice(["csv", "json", "mhtml"]),
type=click.Choice(SURPPORTED_EXPORT_FORMATS),
required=True,
help="Format to export",
)
@click.option(
"--save-dir",
"-s",
"save_dir",
type=click.Path(file_okay=False, resolve_path=True, path_type=Path),
required=True,
help="Directory to save indicators",
)
@click.option(
"--split/--no-split",
"is_split",
default=True,
help="Save indicators in separate files",
)
@click.option("--filename", "-o", "filename", type=str, required=True)
@processor
@pass_environment
def export(
env: "Environment",
format_name: t.Literal["csv", "json", "mhtml"],
filename: click.Path,
results: "QueryResults",
format: SURPPORTED_EXPORT_FORMAT_TYPE,
save_dir: Path,
is_split: bool,
):
pass
ExportResult(results, format, save_dir, is_split).export()
yield from results
8 changes: 0 additions & 8 deletions opendigger_pycli/cli/commands/monitor_cmd.py

This file was deleted.

8 changes: 0 additions & 8 deletions opendigger_pycli/cli/commands/report_cmd.py

This file was deleted.

14 changes: 11 additions & 3 deletions opendigger_pycli/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ def __init__(self):
def user_config_file_path(self) -> str:
config_dir_str = click.get_app_dir("opendigger-pycli")
config_dir = Path(config_dir_str)
config_dir.mkdir(parents=True, exist_ok=True)
user_config = config_dir / "config.ini"
if not user_config.exists():
user_config.touch()
return str(user_config)

@property
Expand All @@ -40,7 +43,9 @@ def __load_config(self):
parser = configparser.RawConfigParser()
parser.read(self.config_file_paths)

for config_dataclass in ALL_CONFIGS:
for config_dataclass_key in ALL_CONFIGS:
config_dataclass = ALL_CONFIGS[config_dataclass_key]
config_dataclass = ALL_CONFIGS[config_dataclass_key]
if not is_dataclass(config_dataclass):
raise TypeError(f"{config_dataclass} is not a dataclass")

Expand All @@ -54,8 +59,10 @@ def __load_config(self):

def update_config(self):
parser = configparser.RawConfigParser()
parser.read(self.config_file_paths)

for config_dataclass in ALL_CONFIGS:
for config_dataclass_key in ALL_CONFIGS:
config_dataclass = ALL_CONFIGS[config_dataclass_key]
if not is_dataclass(config_dataclass):
raise TypeError(f"{config_dataclass} is not a dataclass")

Expand All @@ -75,7 +82,8 @@ def __rich_console__(
self, console: "Console", options: "ConsoleOptions"
) -> "RenderResult":
yield "[b]OpenDigger Python CLI Configs:[/b]"
for config_dataclass in ALL_CONFIGS:
for config_dataclass_key in ALL_CONFIGS:
config_dataclass = ALL_CONFIGS[config_dataclass_key]
if not is_dataclass(config_dataclass):
raise TypeError(f"{config_dataclass} is not a dataclass")

Expand Down
2 changes: 1 addition & 1 deletion opendigger_pycli/cli/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import click
from click.shell_completion import CompletionItem

from opendigger_pycli.dataloader import (
from opendigger_pycli.dataloaders import (
DeveloperNetworkRepoDataloader,
ProjectOpenRankNetworkRepoDataloader,
RepoNetworkRepoDataloader,
Expand Down
11 changes: 11 additions & 0 deletions opendigger_pycli/cli/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,14 @@ def set_log_level(
self.log("[bold green]verbose mode enabled")

self.load_configs()

def set_config(
self, section_name: str, key: str, value: str, *, is_persist: bool = True
) -> None:
config = getattr(self.cli_config, section_name.replace(".", "_"))
setattr(config, key, value)
if is_persist:
self.cli_config.update_config()
self.dlog(
f"[bold green]set config {section_name}.{key} to {value} successfully"
)
2 changes: 1 addition & 1 deletion opendigger_pycli/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import click

from opendigger_pycli.dataloader import filter_dataloader
from opendigger_pycli.dataloaders import filter_dataloader

if t.TYPE_CHECKING:
from click import Context
Expand Down
2 changes: 1 addition & 1 deletion opendigger_pycli/console/print_base_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from rich import box
from rich.table import Table

from opendigger_pycli.dataloader import filter_dataloader
from opendigger_pycli.dataloaders import filter_dataloader
from opendigger_pycli.utils import THREAD_POOL
from opendigger_pycli.utils.gtihub_api import (
RepoInfoType,
Expand Down
24 changes: 1 addition & 23 deletions opendigger_pycli/console/print_indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .print_indicator_graph import print_base_data_graph, print_base_network_data_graph
from .print_indicator_json import print_base_data_json, print_base_network_data_json
from .print_indicator_table import print_base_data_table, print_base_network_data_table
from .utils import print_failed_query

if t.TYPE_CHECKING:
from opendigger_pycli.datatypes import (
Expand All @@ -24,29 +25,6 @@ def format_indicator_name(indicator_name: str) -> str:
return indicator_name.replace("_", " ").title()


def print_failed_query(
indicator_name: str, failed_query: t.Optional["IndicatorQuery"]
) -> None:
if failed_query is None:
return

if failed_query.years:
CONSOLE.print(
f"[red]No {indicator_name} Indicator Data in years: "
f"{list(failed_query.years)}"
)
if failed_query.months:
CONSOLE.print(
f"[red]No {indicator_name} Indicator Data in months: "
f"{list(failed_query.months)}"
)
if failed_query.year_months:
CONSOLE.print(
f"[red]No {indicator_name} Indicator Data in year_months: "
f"{[f'{year_month[0]}-{year_month[1]:02}' for year_month in failed_query.year_months]}"
)


def print_trivial_indicator(
indicator_name: str,
indicator_data: "TrivialIndicatorData",
Expand Down
26 changes: 26 additions & 0 deletions opendigger_pycli/console/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import typing as t

from . import CONSOLE

if t.TYPE_CHECKING:
from opendigger_pycli.datatypes import NameAndValue, NameNameAndValue
from opendigger_pycli.datatypes.query import IndicatorQuery


def if_prettey(value: t.Any) -> bool:
Expand All @@ -13,3 +16,26 @@ def if_prettey(value: t.Any) -> bool:
value = t.cast(t.List["NameNameAndValue"], value)
return True
return False


def print_failed_query(
indicator_name: str, failed_query: t.Optional["IndicatorQuery"]
) -> None:
if failed_query is None:
return

if failed_query.years:
CONSOLE.print(
f"[red]No {indicator_name} Indicator Data in years: "
f"{list(failed_query.years)}"
)
if failed_query.months:
CONSOLE.print(
f"[red]No {indicator_name} Indicator Data in months: "
f"{list(failed_query.months)}"
)
if failed_query.year_months:
CONSOLE.print(
f"[red]No {indicator_name} Indicator Data in year_months: "
f"{[f'{year_month[0]}-{year_month[1]:02}' for year_month in failed_query.year_months]}"
)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from opendigger_pycli.dataloader import (
from opendigger_pycli.dataloaders import (
OpenRankRepoDataloader,
OpenRankUserDataLoader,
ProjectOpenRankNetworkRepoDataloader,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from opendigger_pycli.dataloader.indices import (
from opendigger_pycli.dataloaders.indices import (
ActivityRepoDataloader,
ActivityUserDataLoader,
AttentionRepoDataloader,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from opendigger_pycli.dataloader.metrics import (
from opendigger_pycli.dataloaders.metrics import (
AcceptedChangeRequestRepoDataloader,
ActiveDateAndTimeRepoDataloader,
AddedCodeChangeLineRepoDataloader,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from opendigger_pycli.dataloader.networks import (
from opendigger_pycli.dataloaders.networks import (
DeveloperNetworkRepoDataloader,
DeveloperNetworkUserDataloader,
ProjectOpenRankNetworkRepoDataloader,
Expand Down
File renamed without changes.
Loading

0 comments on commit 58f9692

Please sign in to comment.