@ -1,4 +1,13 @@
#!/usr/bin/env python3
#!/usr/bin/env python3
'''
Purpose : View and cache issues in the EnlivenMinetest repo .
Author : 2020 - 2021 Jake Gustafson
License : See license file at https : / / github . com / poikilos / EnlivenMinetest
This script caches issues ( To
~ / . cache / enissue / poikilos / EnlivenMinetest / issues
by default ) .
'''
from __future__ import print_function
from __future__ import print_function
import sys
import sys
import json
import json
@ -78,6 +87,7 @@ modes = {
" examples " : [ " labels " ]
" examples " : [ " labels " ]
} ,
} ,
" find " : {
" find " : {
" parent " : " list " ,
" help " : ( " To search using a keyword, say \" find \" or \" AND \" "
" help " : ( " To search using a keyword, say \" find \" or \" AND \" "
" before each term. " ) ,
" before each term. " ) ,
" examples " : [
" examples " : [
@ -96,7 +106,8 @@ modes = {
] ,
] ,
" AND_EXAMPLES " : [ 6 , 7 ] , # indices in ['find']['examples'] list
" AND_EXAMPLES " : [ 6 , 7 ] , # indices in ['find']['examples'] list
} ,
} ,
" closed or open " : {
" open " : {
" parent " : " list " ,
" help " : ( " Only show a closed issue, or show closed & open "
" help " : ( " Only show a closed issue, or show closed & open "
" (The default is open issues only) " ) ,
" (The default is open issues only) " ) ,
" examples " : [
" examples " : [
@ -107,6 +118,7 @@ modes = {
]
]
} ,
} ,
" page " : {
" page " : {
" parent " : " list " ,
" help " : ( " GitHub only lists 30 issues at a time. Type page "
" help " : ( " GitHub only lists 30 issues at a time. Type page "
" followed by a number to see additional pages. " ) ,
" followed by a number to see additional pages. " ) ,
" examples " : [ " page 2 " , " page 2 --closed " ]
" examples " : [ " page 2 " , " page 2 --closed " ]
@ -116,6 +128,10 @@ modes = {
" examples " : [ " 1 " ]
" examples " : [ " 1 " ]
} ,
} ,
}
}
# ^ The parent mode is the actual operating mode whereas the submodes
# are only for documenting the keywords that apply to the parent mode.
modes [ " closed " ] = modes [ " open " ]
match_all_labels = [ ]
match_all_labels = [ ]
@ -250,11 +266,16 @@ class Repo:
This method is used internally by load_issues and the result is
This method is used internally by load_issues and the result is
placed in self . issues .
placed in self . issues .
The consumer of this method must manually filter by label ! The
page is cached regardless of label ( s ) because the page is the
same unless there is a query .
Sequential arguments :
Sequential arguments :
options - - This dictionary where all keys are optional may have :
options - - This dictionary where all keys are optional may have :
- ' refresh ' : Set to True to refresh the cache ( load the data
- ' refresh ' : Set to True to refresh the cache ( load the data
from the internet and re - save the cached data ) .
from the internet and re - save the cached data ) .
Keyword arguments :
Keyword arguments :
query - - Place keys & values in this dictionary directly into
query - - Place keys & values in this dictionary directly into
the query part of the URL .
the query part of the URL .
@ -264,10 +285,16 @@ class Repo:
None when using issue_no or a ValueError is raised .
None when using issue_no or a ValueError is raised .
search_terms - - Search for each of these terms .
search_terms - - Search for each of these terms .
Raises :
Raises :
ValueError if query is not None and issue_no is not None .
ValueError if query is not None and issue_no is not None .
'''
'''
debug ( " get_issues... " )
debug ( " options= {} " . format ( options ) )
debug ( " query= {} " . format ( query ) )
debug ( " issue_no= {} " . format ( issue_no ) )
debug ( " search_terms= {} " . format ( search_terms ) )
p = self . log_prefix
p = self . log_prefix
searchTermStr = " "
searchTermStr = " "
if search_terms is None :
if search_terms is None :
@ -297,11 +324,11 @@ class Repo:
#
#
# <https://stackoverflow.com/a/5607708/4541104>:
# <https://stackoverflow.com/a/5607708/4541104>:
'''
'''
query_part = urlencode ( query )
query_part = urlencode ( query ) # urlencode takes a dict
and_query_part = " & " + query_part
and_query_part = " & " + query_part
and_query_part + = searchTermStr
and_query_part + = searchTermStr
elif len ( searchTermStr ) > 0 :
elif len ( searchTermStr ) > 0 :
debug ( p + " searchTermStr= \" {} \" " . format ( searchTermStr ) )
debug ( " searchTermStr=\" {} \" " . format ( searchTermStr ) )
and_query_part = " & " + " q= " + searchTermStr
and_query_part = " & " + " q= " + searchTermStr
# NOTE: using urlencode(searchTermStr) causes
# NOTE: using urlencode(searchTermStr) causes
# "TypeError: not a valid non-string sequence or mapping
# "TypeError: not a valid non-string sequence or mapping
@ -309,6 +336,7 @@ class Repo:
# - It should already be formed correctly by
# - It should already be formed correctly by
# toSubQueryValue anyway, so don't filter it here.
# toSubQueryValue anyway, so don't filter it here.
debug ( " and_query_part: {} " . format ( and_query_part ) )
if self . page is not None :
if self . page is not None :
this_page = self . page
this_page = self . page
c_issues_name = self . c_issues_name_fmt . format ( p = this_page ,
c_issues_name = self . c_issues_name_fmt . format ( p = this_page ,
@ -343,8 +371,24 @@ class Repo:
query_s + = " ?q= " + searchTermStr
query_s + = " ?q= " + searchTermStr
else :
else :
query_s + = searchTermStr
query_s + = searchTermStr
debug ( " C hanging query_s from {} to {} "
debug ( " c hanging query_s from {} to {} "
" " . format ( prev_query_s , query_s ) )
" " . format ( prev_query_s , query_s ) )
debug ( " (issues will be set to the content of the {} "
" element) " . format ( results_key ) )
# Labels are NOT in the URL, but filtered later (See docstring).
if self . page is not None :
query_s = self . issues_url + " ?page= " + str ( self . page )
query_s + = and_query_part
debug ( " appended page query_part= {} (c_path= {} ) "
" " . format ( query_part , c_path ) )
elif len ( query_part ) > 0 :
query_s + = " ? " + query_part
debug ( " appended custom query_part= {} (c_path= {} ) "
" " . format ( query_part , c_path ) )
else :
debug ( " There was no custom query. " )
if os . path . isfile ( c_path ) :
if os . path . isfile ( c_path ) :
@ -357,7 +401,8 @@ class Repo:
if ( refresh is not True ) and ( filetime > cache_delta ) :
if ( refresh is not True ) and ( filetime > cache_delta ) :
print ( p + " Loading cache: \" {} \" " . format ( c_path ) )
print ( p + " Loading cache: \" {} \" " . format ( c_path ) )
# print(p+"Cache time limit: {}".format(max_cache_delta)
debug ( p + " Cache time limit: {} " . format ( max_cache_delta ) )
debug ( p + " for URL: {} " . format ( query_s ) )
print ( p + " Cache expires: {} " . format ( filetime
print ( p + " Cache expires: {} " . format ( filetime
+ max_cache_delta ) )
+ max_cache_delta ) )
with open ( c_path ) as json_file :
with open ( c_path ) as json_file :
@ -366,17 +411,27 @@ class Repo:
# " {} issue(s).".format(len(results)))
# " {} issue(s).".format(len(results)))
max_issue = None
max_issue = None
results = result
results = result
if results_key is not None :
if hasattr ( results , results_key ) :
debug ( " loaded result[ {} ] "
" " . format ( results_key ) )
results = results [ results_key ]
else :
error ( " WARNING: expected {} in dict "
" " . format ( results_key ) )
if hasattr ( results , ' keys ' ) :
if hasattr ( results , ' keys ' ) :
# It is an issue not a page, so convert to list:
debug ( " issue not page: converting to list " )
results = [ result ]
results = [ result ]
for issue in results :
for issue in results :
issue_n = issue . get ( " number " )
issue_n = issue . get ( " number " )
debug ( " issue_n: {} " . format ( issue_n ) )
if issue_n is not None :
if issue_n is not None :
if ( max_issue is None ) or ( issue_n > max_issue ) :
if ( max_issue is None ) or ( issue_n > max_issue ) :
max_issue = issue_n
max_issue = issue_n
print ( p + " The highest cached issue# is {} . " . format (
print ( " The highest cached issue# is {} . " . format (
max_issue
max_issue
) )
) )
debug ( " returning {} issue(s) " . format ( len ( results ) ) )
return results
return results
else :
else :
if refresh is True :
if refresh is True :
@ -391,11 +446,6 @@ class Repo:
c_path
c_path
) )
) )
if self . page is not None :
query_s = self . issues_url + " ?page= " + str ( self . page )
query_s + = and_query_part
elif len ( query_part ) > 0 :
query_s + = " ? " + query_part
try :
try :
debug ( p + " Query URL (query_s): {} " . format ( query_s ) )
debug ( p + " Query URL (query_s): {} " . format ( query_s ) )
response = request . urlopen ( query_s )
response = request . urlopen ( query_s )
@ -790,6 +840,7 @@ class Repo:
'''
'''
See _get_issues for documentation .
See _get_issues for documentation .
'''
'''
debug ( " load_issues... " )
if issue_no is not None :
if issue_no is not None :
if query is not None :
if query is not None :
raise ValueError ( " You cannot do a query when getting "
raise ValueError ( " You cannot do a query when getting "
@ -825,6 +876,16 @@ class Repo:
# if mode == "list":
# if mode == "list":
for issue in self . issues :
for issue in self . issues :
this_issue_labels_lower = [ ]
this_issue_labels_lower = [ ]
try :
labels = issue [ " labels " ]
except KeyError as ex :
dumpPath = os . path . join ( Repo . profile ,
" dump-issues.json " )
with open ( dumpPath , ' w ' ) as outs :
json . dump ( self . issues , outs , indent = 2 )
print ( " Error: dumped self.issues as {} "
" " . format ( dumpPath ) )
raise ex
for label in issue [ " labels " ] :
for label in issue [ " labels " ] :
self . label_ids . append ( label [ " id " ] )
self . label_ids . append ( label [ " id " ] )
if label [ " url " ] . startswith ( self . labels_url ) :
if label [ " url " ] . startswith ( self . labels_url ) :
@ -890,8 +951,9 @@ def main():
# don't set the command to list unless the enclosing case is
# don't set the command to list unless the enclosing case is
# true. If a label was specified, paging is handled in the
# true. If a label was specified, paging is handled in the
# other case.
# other case.
if arg == " page " :
parent = modes [ arg ] . get ( ' parent ' )
mode = " list "
if parent is not None :
mode = parent
else :
else :
mode = arg
mode = arg
else :
else :
@ -1005,9 +1067,11 @@ def main():
}
}
repo . load_issues ( options , query = query ,
repo . load_issues ( options , query = query ,
search_terms = search_terms )
search_terms = search_terms )
debug ( " * done load_issues for list " )
else :
else :
repo . load_issues ( options , issue_no = issue_no ,
repo . load_issues ( options , issue_no = issue_no ,
search_terms = search_terms )
search_terms = search_terms )
debug ( " * done load_issues for single issue " )
if repo . issues is None :
if repo . issues is None :
print ( " There were no issues. " )
print ( " There were no issues. " )
@ -1118,6 +1182,8 @@ def main():
print ( " To view details, type " )
print ( " To view details, type " )
print ( " ./ " + me )
print ( " ./ " + me )
print ( " followed by a number. " )
print ( " followed by a number. " )
else :
debug ( " There is no summary output due to mode= {} " . format ( mode ) )
print ( " " )
print ( " " )