potodo/potodo/potodo.py

485 lines
15 KiB
Python
Raw Normal View History

2018-12-13 22:57:35 +00:00
import argparse
2019-12-16 22:17:27 +00:00
import json
import logging
2020-08-25 06:59:46 +00:00
import webbrowser
from pathlib import Path
2020-09-15 08:46:06 +00:00
from typing import Any
from typing import Dict
from typing import List
from typing import Sequence
2020-09-15 09:32:15 +00:00
from typing import Tuple
from gitignore_parser import parse_gitignore
2018-12-13 22:57:35 +00:00
2019-12-10 14:58:00 +00:00
from potodo import __version__
2020-10-13 16:44:11 +00:00
from potodo.arguments_handling import check_args
2020-10-13 15:08:35 +00:00
from potodo.github import get_issue_reservations
from potodo.interactive import _confirmation_menu
from potodo.interactive import _directory_list_menu
from potodo.interactive import _file_list_menu
from potodo.interactive import get_dir_list
from potodo.interactive import get_files_from_dir
from potodo.json import json_dateconv
from potodo.logging import setup_logging
2020-10-13 16:44:11 +00:00
from potodo.po_file import get_po_stats_from_repo_or_cache
from potodo.po_file import PoFileStats
2018-12-13 22:57:35 +00:00
def print_dir_stats(
2020-08-25 06:59:46 +00:00
directory_name: str,
buffer: Sequence[str],
2020-10-14 20:32:34 +00:00
folder_stats: Dict[str, int],
2020-08-25 06:59:46 +00:00
printed_list: Sequence[bool],
2019-12-13 14:12:35 +00:00
) -> None:
"""This function prints the directory name, its stats and the buffer"""
2019-12-10 20:04:36 +00:00
if True in printed_list:
2020-08-27 08:18:24 +00:00
logging.debug("Printing directory %s", directory_name)
2019-12-11 23:36:35 +00:00
# If at least one of the files isn't done then print the
# folder stats and file(s) Each time a file is went over True
# or False is placed in the printed_list list. If False is
# placed it means it doesnt need to be printed
2020-10-14 20:32:34 +00:00
folder_completion = 100 * folder_stats["translated"] / folder_stats["total"]
print(f"\n\n# {directory_name} ({folder_completion:.2f}% done)\n")
2019-12-10 20:04:36 +00:00
print("\n".join(buffer))
2020-08-27 08:18:24 +00:00
logging.debug("Not printing directory %s", directory_name)
2019-12-10 20:04:36 +00:00
2019-12-16 22:17:27 +00:00
def add_dir_stats(
2020-08-25 06:59:46 +00:00
directory_name: str,
buffer: List[Dict[str, str]],
2020-10-14 20:32:34 +00:00
folder_stats: Dict[str, int],
2020-08-25 06:59:46 +00:00
printed_list: Sequence[bool],
all_stats: List[Dict[str, Any]],
2019-12-16 22:57:17 +00:00
) -> None:
"""Appends directory name, its stats and the buffer to stats"""
2019-12-16 22:17:27 +00:00
if any(printed_list):
2020-10-14 20:32:34 +00:00
folder_completion = 100 * folder_stats["translated"] / folder_stats["total"]
2019-12-16 22:57:17 +00:00
all_stats.append(
dict(
name=f"{directory_name}/",
2020-10-14 20:32:34 +00:00
percent_translated=float(f"{folder_completion:.2f}"),
2019-12-16 22:57:17 +00:00
files=buffer,
)
)
2018-12-13 22:57:35 +00:00
2019-12-10 20:04:36 +00:00
2019-12-11 21:19:19 +00:00
def exec_potodo(
path: Path,
exclude: List[Path],
2019-12-11 21:19:19 +00:00
above: int,
below: int,
2020-09-15 08:44:49 +00:00
only_fuzzy: bool,
2019-12-11 21:19:19 +00:00
offline: bool,
hide_reserved: bool,
counts: bool,
2019-12-16 22:17:27 +00:00
json_format: bool,
2020-09-15 08:44:49 +00:00
exclude_fuzzy: bool,
2020-09-15 08:58:49 +00:00
exclude_reserved: bool,
2020-09-15 09:05:47 +00:00
only_reserved: bool,
2020-09-15 09:32:15 +00:00
show_reservation_dates: bool,
2020-09-15 11:49:52 +00:00
no_cache: bool,
is_interactive: bool,
2020-10-13 10:56:02 +00:00
matching_files: bool,
2019-12-16 22:57:17 +00:00
) -> None:
2019-12-11 12:05:42 +00:00
"""
Will run everything based on the given parameters
2019-12-10 14:38:53 +00:00
2019-12-10 20:04:36 +00:00
:param path: The path to search into
2020-06-04 07:37:15 +00:00
:param exclude: folders or files to be ignored
2019-12-10 20:04:36 +00:00
:param above: The above threshold
:param below: The below threshold
2020-09-15 08:44:49 +00:00
:param only_fuzzy: Should only fuzzies be printed
2019-12-10 20:04:36 +00:00
:param offline: Will not connect to internet
:param hide_reserved: Will not show the reserved files
2019-12-11 21:58:31 +00:00
:param counts: Render list with counts not percentage
2019-12-16 22:17:27 +00:00
:param json_format: Format output as JSON.
2020-09-15 08:44:49 +00:00
:param exclude_fuzzy: Will exclude files with fuzzies in output.
2020-09-15 08:58:49 +00:00
:param exclude_reserved: Will print out only files that aren't reserved
2020-10-12 12:29:59 +00:00
:param only_reserved: Will print only reserved files
2020-09-15 09:32:15 +00:00
:param show_reservation_dates: Will show the reservation dates
:param no_cache: Disables cache (Cache is disabled when files are modified)
2020-08-24 13:20:22 +00:00
:param is_interactive: Switches output to an interactive CLI menu
2020-10-13 10:56:02 +00:00
:param matching_files: Should the file paths be printed instead of normal output
2019-12-10 20:04:36 +00:00
"""
2020-08-25 06:59:46 +00:00
cache_args = {
"path": path,
"exclude": exclude,
"above": above,
"below": below,
"only_fuzzy": only_fuzzy,
"offline": offline,
"hide_reserved": hide_reserved,
"counts": counts,
"json_format": json_format,
"exclude_fuzzy": exclude_fuzzy,
"exclude_reserved": exclude_reserved,
"only_reserved": only_reserved,
"show_reservation_dates": show_reservation_dates,
"no_cache": no_cache,
"is_interactive": is_interactive,
}
2020-10-14 07:54:37 +00:00
try:
2020-10-15 08:31:48 +00:00
ignore_matches = parse_gitignore(".potodoignore", base_dir=path)
2020-10-14 07:54:37 +00:00
except FileNotFoundError:
ignore_matches = parse_gitignore("/dev/null")
2020-10-13 16:43:04 +00:00
2019-12-13 13:40:09 +00:00
# Initialize the arguments
2020-10-14 12:59:39 +00:00
issue_reservations = get_issue_reservations(offline, hide_reserved, path)
2019-12-10 14:38:53 +00:00
2019-12-17 08:34:50 +00:00
dir_stats: List[Any] = []
2020-08-24 19:05:49 +00:00
if is_interactive:
directory_options = get_dir_list(
repo_path=path, exclude=exclude, ignore_matches=ignore_matches
)
while True:
selected_dir = _directory_list_menu(directory_options)
2020-09-14 08:54:40 +00:00
if selected_dir == (len(directory_options) - 1):
exit(0)
directory = directory_options[selected_dir]
file_options = get_files_from_dir(
directory=directory, repo_path=path, exclude=exclude
)
# TODO: Add stats on files and also add reservations
2020-09-14 08:54:40 +00:00
selected_file = _file_list_menu(directory, file_options)
if selected_file == (len(file_options) + 1):
exit(0)
2020-09-14 08:54:40 +00:00
elif selected_file == len(file_options):
continue
file = file_options[selected_file]
final_choice = _confirmation_menu(file, directory)
if final_choice == 3:
exit(0)
elif final_choice == 2:
continue
else:
break
if final_choice == 0:
2020-08-25 06:59:46 +00:00
webbrowser.open(
f"https://github.com/python/python-docs-fr/issues/new?title=Je%20travaille%20sur%20"
f"{directory}/{file}"
f"&body=%0A%0A%0A---%0AThis+issue+was+created+using+potodo+interactive+mode."
)
else:
exit()
2020-08-24 19:05:49 +00:00
else:
po_files_and_dirs = get_po_stats_from_repo_or_cache(
path, exclude, cache_args, ignore_matches, no_cache
)
2020-08-24 19:05:49 +00:00
for directory_name, po_files in sorted(po_files_and_dirs.items()):
# For each directory and files in this directory
buffer: List[Any] = []
2020-10-14 20:32:34 +00:00
folder_stats: Dict[str, int] = {"translated": 0, "total": 0}
2020-08-24 19:05:49 +00:00
printed_list: List[bool] = []
2020-10-12 12:52:58 +00:00
2020-10-12 12:29:59 +00:00
for po_file in sorted(po_files):
# For each file in those files from that directory
if not only_fuzzy or po_file.fuzzy_entries:
if exclude_fuzzy and po_file.fuzzy_entries:
continue
buffer_add(
buffer,
folder_stats,
printed_list,
po_file,
issue_reservations,
above,
below,
counts,
json_format,
exclude_reserved,
only_reserved,
show_reservation_dates,
2020-10-13 10:56:02 +00:00
matching_files,
2020-08-24 19:05:49 +00:00
)
2020-10-12 12:52:58 +00:00
2020-10-12 12:29:59 +00:00
# Once all files have been processed, print the dir and the files
# or store them into a dict to print them once all directories have
# been processed.
if json_format:
2020-10-12 12:52:58 +00:00
add_dir_stats(
directory_name, buffer, folder_stats, printed_list, dir_stats
)
2020-08-24 19:05:49 +00:00
else:
print_dir_stats(directory_name, buffer, folder_stats, printed_list)
2020-10-12 12:52:58 +00:00
2020-10-12 12:29:59 +00:00
if json_format:
2020-10-12 12:52:58 +00:00
print(
json.dumps(
dir_stats,
indent=4,
separators=(",", ": "),
sort_keys=False,
default=json_dateconv,
)
)
2019-12-16 22:17:27 +00:00
def buffer_add(
2019-12-17 08:34:50 +00:00
buffer: List[Any],
2020-10-14 20:32:34 +00:00
folder_stats: Dict[str, int],
2019-12-16 22:17:27 +00:00
printed_list: List[bool],
po_file_stats: PoFileStats,
2020-09-15 09:32:15 +00:00
issue_reservations: Dict[str, Tuple[Any, Any]],
2019-12-16 22:17:27 +00:00
above: int,
below: int,
counts: bool,
json_format: bool,
2020-09-15 08:58:49 +00:00
exclude_reserved: bool,
2020-09-15 09:05:47 +00:00
only_reserved: bool,
2020-09-15 09:32:15 +00:00
show_reservation_dates: bool,
2020-10-13 10:56:02 +00:00
matching_files: bool,
2019-12-16 22:17:27 +00:00
) -> None:
"""Will add to the buffer the information to print about the file is
the file isn't translated entirely or above or below requested
values.
"""
# If the file is completely translated,
# or is translated below what's requested
# or is translated above what's requested
2019-12-16 22:57:17 +00:00
if (
2020-08-25 06:59:46 +00:00
po_file_stats.percent_translated == 100
or po_file_stats.percent_translated < above
or po_file_stats.percent_translated > below
2019-12-16 22:57:17 +00:00
):
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
# add the percentage of the file to the stats of the folder
2020-10-14 20:32:34 +00:00
folder_stats["translated"] += po_file_stats.translated_nb
folder_stats["total"] += po_file_stats.entries_count
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
if not json_format:
# don't print that file
printed_list.append(False)
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
# return without adding anything to the buffer
return
2020-08-25 06:59:46 +00:00
2019-12-17 08:34:50 +00:00
fuzzy_entries = po_file_stats.fuzzy_entries
untranslated_entries = po_file_stats.untranslated_entries
2019-12-16 22:17:27 +00:00
# nb of fuzzies in the file IF there are some fuzzies in the file
2019-12-17 08:34:50 +00:00
fuzzy_nb = po_file_stats.fuzzy_nb if fuzzy_entries else 0
2019-12-16 22:17:27 +00:00
# number of entries translated
translated_nb = po_file_stats.translated_nb
# file size
po_file_size = po_file_stats.po_file_size
# percentage of the file already translated
percent_translated = po_file_stats.percent_translated
2020-09-16 07:12:35 +00:00
2019-12-16 22:57:17 +00:00
# `reserved by` if the file is reserved
2020-09-15 09:32:15 +00:00
reserved_by, reservation_date = issue_reservations.get(
po_file_stats.filename_dir.lower(), (None, None)
)
2019-12-16 22:57:17 +00:00
# unless the offline/hide_reservation are enabled
2020-09-15 08:58:49 +00:00
if exclude_reserved and reserved_by:
return
2020-09-15 09:05:47 +00:00
if only_reserved and not reserved_by:
return
2019-12-16 22:17:27 +00:00
2019-12-16 22:57:17 +00:00
directory = po_file_stats.directory
filename = po_file_stats.filename
path = po_file_stats.path
2020-08-25 06:59:46 +00:00
2020-10-13 10:56:02 +00:00
if matching_files:
print(path)
return
elif json_format:
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
# the order of the keys is the display order
2019-12-17 08:34:50 +00:00
d = dict(
2019-12-17 23:03:46 +00:00
name=f"{directory}/{filename.replace('.po', '')}",
2019-12-16 22:57:17 +00:00
path=str(path),
entries=po_file_size,
fuzzies=fuzzy_nb,
translated=translated_nb,
percent_translated=percent_translated,
2019-12-16 22:57:17 +00:00
reserved_by=reserved_by,
2020-09-15 09:32:15 +00:00
reservation_date=reservation_date,
2019-12-16 22:57:17 +00:00
)
2020-08-25 06:59:46 +00:00
2019-12-17 08:34:50 +00:00
buffer.append(d)
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
else:
2019-12-17 08:34:50 +00:00
s = f"- {filename:<30} " # The filename
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
if counts:
2019-12-17 08:34:50 +00:00
missing = len(fuzzy_entries) + len(untranslated_entries)
s += f"{missing:3d} to do"
s += f", including {fuzzy_nb} fuzzies." if fuzzy_nb else ""
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
else:
2019-12-17 08:34:50 +00:00
s += f"{translated_nb:3d} / {po_file_size:3d} "
s += f"({percent_translated:5.1f}% translated)"
s += f", {fuzzy_nb} fuzzy" if fuzzy_nb else ""
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
if reserved_by is not None:
2019-12-17 08:34:50 +00:00
s += f", réservé par {reserved_by}"
2020-09-15 09:32:15 +00:00
if show_reservation_dates:
s += f" ({reservation_date})"
2019-12-16 22:17:27 +00:00
2019-12-17 08:34:50 +00:00
buffer.append(s)
2020-08-25 06:59:46 +00:00
2019-12-16 22:17:27 +00:00
# Add the percent translated to the folder statistics
2020-10-14 20:32:34 +00:00
folder_stats["translated"] += po_file_stats.translated_nb
folder_stats["total"] += po_file_stats.entries_count
2019-12-16 22:17:27 +00:00
# Indicate to print the file
printed_list.append(True)
2019-12-10 14:38:53 +00:00
2019-12-13 14:12:35 +00:00
def main() -> None:
parser = argparse.ArgumentParser(
2020-10-13 16:44:47 +00:00
prog="potodo",
description="List and prettify the po files left to translate.",
)
2020-08-25 06:59:46 +00:00
2020-06-03 12:02:03 +00:00
parser.add_argument(
2020-10-13 16:44:47 +00:00
"-p",
"--path",
help="execute Potodo in path",
metavar="path",
2020-06-03 12:02:03 +00:00
)
2020-08-25 06:59:46 +00:00
2020-06-03 07:57:09 +00:00
parser.add_argument(
2020-06-03 12:02:03 +00:00
"-e",
"--exclude",
nargs="+",
default=[],
help="exclude from search",
metavar="path",
2020-06-03 07:57:09 +00:00
)
2020-08-25 06:59:46 +00:00
2019-12-11 21:58:31 +00:00
parser.add_argument(
"-a",
"--above",
default=0,
2020-01-13 22:12:15 +00:00
metavar="X",
2019-12-11 21:58:31 +00:00
type=int,
2020-01-13 22:12:15 +00:00
help="list all TODOs above given X%% completion",
2019-12-11 21:58:31 +00:00
)
2020-08-25 06:59:46 +00:00
2019-12-11 21:58:31 +00:00
parser.add_argument(
"-b",
"--below",
default=100,
2020-01-13 22:12:15 +00:00
metavar="X",
2019-12-11 21:58:31 +00:00
type=int,
2020-01-13 22:12:15 +00:00
help="list all TODOs below given X%% completion",
)
2020-08-25 06:59:46 +00:00
parser.add_argument(
2020-09-15 08:46:06 +00:00
"-f",
"--only-fuzzy",
dest="only_fuzzy",
action="store_true",
help="print only files marked as fuzzys",
)
2020-08-25 06:59:46 +00:00
parser.add_argument(
"-o",
"--offline",
action="store_true",
2020-01-13 22:12:15 +00:00
help="don't perform any fetching to GitHub/online",
)
2020-08-25 06:59:46 +00:00
parser.add_argument(
"-n",
"--no-reserved",
2020-01-13 22:12:15 +00:00
dest="hide_reserved",
action="store_true",
2020-01-13 22:12:15 +00:00
help="don't print info about reserved files",
)
2020-08-25 06:59:46 +00:00
2019-12-11 21:19:19 +00:00
parser.add_argument(
"-c",
"--counts",
action="store_true",
2020-01-13 22:12:15 +00:00
help="render list with the count of remaining entries "
2020-08-25 06:59:46 +00:00
"(translate or review) rather than percentage done",
2019-12-11 21:19:19 +00:00
)
2020-08-25 06:59:46 +00:00
2020-06-03 12:02:03 +00:00
parser.add_argument(
2020-06-03 20:18:36 +00:00
"-j",
"--json",
action="store_true",
dest="json_format",
2020-09-15 11:59:43 +00:00
help="format output as JSON",
2020-06-03 12:02:03 +00:00
)
2020-08-25 06:59:46 +00:00
2020-09-15 08:44:49 +00:00
parser.add_argument(
"--exclude-fuzzy",
action="store_true",
dest="exclude_fuzzy",
2020-09-15 11:59:43 +00:00
help="select only files without fuzzy entries",
2020-09-15 08:44:49 +00:00
)
2020-09-15 08:58:49 +00:00
parser.add_argument(
"--exclude-reserved",
action="store_true",
dest="exclude_reserved",
help="select only files that aren't reserved",
2020-09-15 08:58:49 +00:00
)
2020-09-15 09:05:47 +00:00
parser.add_argument(
"--only-reserved",
action="store_true",
dest="only_reserved",
help="select only only reserved files",
2020-09-15 09:05:47 +00:00
)
2020-09-15 09:32:15 +00:00
parser.add_argument(
"--show-reservation-dates",
action="store_true",
dest="show_reservation_dates",
help="show issue creation dates",
2020-09-15 09:32:15 +00:00
)
2020-09-15 11:49:52 +00:00
parser.add_argument(
"--no-cache",
action="store_true",
dest="no_cache",
help="Disables cache (Cache is disabled when files are modified)",
2020-09-15 11:49:52 +00:00
)
parser.add_argument(
"-i",
"--interactive",
action="store_true",
dest="is_interactive",
help="Activates the interactive menu",
)
2020-08-25 06:59:46 +00:00
2020-10-13 10:56:02 +00:00
parser.add_argument(
2020-10-14 13:06:24 +00:00
"-l",
2020-10-13 10:56:02 +00:00
"--matching-files",
action="store_true",
dest="matching_files",
help="Suppress normal output; instead print the name of each matching po file from which output would normally "
"have been printed.",
)
2019-12-11 23:36:35 +00:00
parser.add_argument(
"--version", action="version", version="%(prog)s " + __version__
)
2019-12-10 14:58:00 +00:00
parser.add_argument(
"-v", "--verbose", action="count", default=0, help="Increases output verbosity"
)
# Initialize args and check consistency
args = vars(parser.parse_args())
args.update(check_args(**args))
2020-08-25 06:59:46 +00:00
if args["logging_level"]:
setup_logging(args["logging_level"])
2020-08-27 08:18:24 +00:00
logging.info("Logging activated.")
logging.debug("Executing potodo with args %s", args)
# Removing useless args before running the process
del args["verbose"]
del args["logging_level"]
# Launch the processing itself
exec_potodo(**args)