2018-12-13 22:57:35 +00:00
import argparse
2019-12-16 22:17:27 +00:00
import json
2020-08-26 08:32:26 +00:00
import logging
2020-06-14 16:33:25 +00:00
from pathlib import Path
2020-09-15 08:46:06 +00:00
from typing import Any
2020-10-24 20:44:11 +00:00
from typing import Callable
2020-09-15 08:46:06 +00:00
from typing import Dict
from typing import List
from typing import Sequence
2020-09-15 09:32:15 +00:00
from typing import Tuple
2020-11-26 14:05:45 +00:00
2021-02-11 21:56:53 +00:00
from gitignore_parser import rule_from_pattern
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
2023-02-16 17:04:20 +00:00
from potodo . forge_api import get_issue_reservations
2020-10-13 15:08:35 +00:00
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
2019-12-11 12:17:10 +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 :
2020-10-12 08:00:08 +00:00
""" 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 :
2020-10-12 08:00:08 +00:00
""" 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
2020-10-24 20:44:11 +00:00
def non_interactive_output (
path : Path ,
2021-01-04 16:29:56 +00:00
exclude : List [ str ] ,
2020-10-24 20:44:11 +00:00
above : int ,
below : int ,
only_fuzzy : bool ,
hide_reserved : bool ,
counts : bool ,
json_format : bool ,
exclude_fuzzy : bool ,
exclude_reserved : bool ,
only_reserved : bool ,
show_reservation_dates : bool ,
no_cache : bool ,
is_interactive : bool ,
matching_files : bool ,
ignore_matches : Callable [ [ str ] , bool ] ,
2023-02-16 17:04:20 +00:00
api_url : str ,
2020-10-24 20:44:11 +00:00
) - > None :
dir_stats : List [ Any ] = [ ]
# Initialize the arguments
2023-02-16 17:04:20 +00:00
if api_url :
2023-02-22 10:50:13 +00:00
issue_reservations = get_issue_reservations ( hide_reserved , api_url )
2023-02-16 17:04:20 +00:00
else :
issue_reservations = { }
2020-10-24 20:44:11 +00:00
total_translated : int = 0
total_entries : int = 0
2021-02-11 21:56:53 +00:00
po_files_and_dirs = get_po_stats_from_repo_or_cache ( path , ignore_matches , no_cache )
2020-10-24 20:44:11 +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 ] = [ ]
folder_stats : Dict [ str , int ] = { " translated " : 0 , " total " : 0 }
printed_list : List [ bool ] = [ ]
2020-11-26 14:05:45 +00:00
2020-10-24 20:44:11 +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 ,
matching_files ,
)
2020-11-26 14:05:45 +00:00
2020-10-24 20:44:11 +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-11-26 14:05:45 +00:00
add_dir_stats ( directory_name , buffer , folder_stats , printed_list , dir_stats )
2020-10-24 20:44:11 +00:00
else :
print_dir_stats ( directory_name , buffer , folder_stats , printed_list )
2020-11-26 14:05:45 +00:00
2020-10-24 20:44:11 +00:00
total_translated + = folder_stats [ " translated " ]
total_entries + = folder_stats [ " total " ]
if json_format :
print (
json . dumps (
dir_stats ,
indent = 4 ,
separators = ( " , " , " : " ) ,
sort_keys = False ,
default = json_dateconv ,
)
)
else :
2020-11-26 14:19:35 +00:00
if total_entries != 0 :
total_completion = 100 * total_translated / total_entries
print ( f " \n \n # TOTAL ( { total_completion : .2f } % done) \n " )
2020-10-24 20:44:11 +00:00
2021-02-11 21:56:53 +00:00
def build_ignore_matcher ( path : Path , exclude : List [ str ] ) - > Callable [ [ str ] , bool ] :
path = path . resolve ( )
potodo_ignore = path / " .potodoignore "
rules = [ ]
if potodo_ignore . exists ( ) :
for line in potodo_ignore . read_text ( ) . splitlines ( ) :
rule = rule_from_pattern ( line , path )
if rule :
rules . append ( rule )
rules . append ( rule_from_pattern ( " .git/ " , path ) )
for rule in exclude :
rules . append ( rule_from_pattern ( rule , path ) )
return lambda file_path : any ( r . match ( file_path ) for r in rules )
2019-12-11 21:19:19 +00:00
def exec_potodo (
2020-06-14 16:33:25 +00:00
path : Path ,
2021-01-04 16:29:56 +00:00
exclude : List [ str ] ,
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
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 ,
2020-08-24 13:09:05 +00:00
is_interactive : bool ,
2020-10-13 10:56:02 +00:00
matching_files : bool ,
2023-02-16 17:04:20 +00:00
api_url : str ,
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 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
2020-10-12 12:06:56 +00:00
: 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
2023-02-16 17:04:20 +00:00
: param api_url : API URL for reservation tickets on Gitea or GitHub
2019-12-10 20:04:36 +00:00
"""
2020-08-25 06:59:46 +00:00
2021-02-11 21:56:53 +00:00
ignore_matches = build_ignore_matcher ( path , exclude )
2020-08-24 19:05:49 +00:00
if is_interactive :
2020-10-24 20:44:11 +00:00
from potodo . interactive import interactive_output
2020-11-26 14:05:45 +00:00
2021-02-11 21:56:53 +00:00
interactive_output ( path , ignore_matches )
2020-08-24 19:05:49 +00:00
else :
2020-10-24 20:44:11 +00:00
non_interactive_output (
path ,
exclude ,
above ,
below ,
only_fuzzy ,
hide_reserved ,
counts ,
json_format ,
exclude_fuzzy ,
exclude_reserved ,
only_reserved ,
show_reservation_dates ,
no_cache ,
is_interactive ,
matching_files ,
ignore_matches ,
2023-02-16 17:04:20 +00:00
api_url ,
2020-10-14 07:46:50 +00:00
)
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
) :
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 :
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 ,
2019-12-17 08:40:58 +00:00
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 :
2019-12-11 12:17:10 +00:00
parser = argparse . ArgumentParser (
2020-11-26 14:19:35 +00:00
prog = " potodo " ,
description = " List and prettify the po files left to translate. " ,
2019-12-11 12:17:10 +00:00
)
2020-08-25 06:59:46 +00:00
2020-06-03 12:02:03 +00:00
parser . add_argument (
2020-11-26 14:19:35 +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 = [ ] ,
2021-02-11 21:56:53 +00:00
help = " gitignore-style patterns to exclude from search. " ,
2020-06-03 12:02:03 +00:00
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 " ,
2020-06-14 16:33:25 +00:00
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 " ,
2020-06-14 16:33:25 +00:00
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 " ,
2019-12-11 12:17:10 +00:00
)
2020-08-25 06:59:46 +00:00
2019-12-11 12:17:10 +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 " ,
2019-12-11 12:17:10 +00:00
)
2020-08-25 06:59:46 +00:00
2023-02-16 17:04:20 +00:00
parser . add_argument (
" -u " ,
" --api-url " ,
help = (
" API URL to retrieve reservation tickets (https://api.github.com/repos/ORGANISATION/REPOSITORY/issues?state=open or https://git.afpy.org/api/v1/repos/ORGANISATION/REPOSITORY/issues?state=open&type=issues) "
2023-02-22 10:50:13 +00:00
) ,
2019-12-11 12:17:10 +00:00
)
2020-08-25 06:59:46 +00:00
2019-12-11 12:17:10 +00:00
parser . add_argument (
" -n " ,
" --no-reserved " ,
2020-01-13 22:12:15 +00:00
dest = " hide_reserved " ,
2019-12-11 12:17:10 +00:00
action = " store_true " ,
2020-01-13 22:12:15 +00:00
help = " don ' t print info about reserved files " ,
2019-12-11 12:17:10 +00:00
)
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 " ,
2020-09-15 11:05:07 +00:00
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 " ,
2020-09-15 12:04:35 +00:00
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 " ,
2020-09-15 11:01:37 +00:00
help = " show issue creation dates " ,
2020-09-15 09:32:15 +00:00
)
2020-09-15 11:49:52 +00:00
parser . add_argument (
2020-09-16 07:18:15 +00:00
" --no-cache " ,
action = " store_true " ,
dest = " no_cache " ,
2020-10-12 12:06:56 +00:00
help = " Disables cache (Cache is disabled when files are modified) " ,
2020-09-15 11:49:52 +00:00
)
2020-08-24 13:09:05 +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
2020-08-26 08:32:26 +00:00
parser . add_argument (
" -v " , " --verbose " , action = " count " , default = 0 , help = " Increases output verbosity "
)
2020-06-14 16:33:25 +00:00
# Initialize args and check consistency
args = vars ( parser . parse_args ( ) )
args . update ( check_args ( * * args ) )
2020-08-25 06:59:46 +00:00
2020-08-26 08:32:26 +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
2020-08-26 08:32:26 +00:00
del args [ " verbose " ]
del args [ " logging_level " ]
2020-06-14 16:33:25 +00:00
# Launch the processing itself
exec_potodo ( * * args )