mirror of
https://abf.rosa.ru/djam/urpm-tools.git
synced 2025-02-23 17:32:46 +00:00
repodiff: some code cleaning
This commit is contained in:
parent
35a3dde9f5
commit
caedf23923
1 changed files with 232 additions and 232 deletions
464
urpm-repodiff.py
464
urpm-repodiff.py
|
@ -2,16 +2,16 @@
|
||||||
'''
|
'''
|
||||||
" Repodiff utility for finding differences between different repositories
|
" Repodiff utility for finding differences between different repositories
|
||||||
"
|
"
|
||||||
" The tool downloads, unpacks and parses synthesis.hdlist.cz and
|
" The tool downloads, unpacks and parses synthesis.hdlist.cz and
|
||||||
" changelog.xml.lzma to genererate lists of newly added packages,
|
" changelog.xml.lzma to genererate lists of newly added packages,
|
||||||
" removed from new repository packages and updated packages.
|
" removed from new repository packages and updated packages.
|
||||||
" The tool outputs data to standart output or to file.
|
" The tool outputs data to standart output or to file.
|
||||||
" It can show if a removed packages is obsoleted by some package
|
" It can show if a removed packages is obsoleted by some package
|
||||||
" in new repositories. Also the tool can output data in format of
|
" in new repositories. Also the tool can output data in format of
|
||||||
" HTML table.
|
" HTML table.
|
||||||
"
|
"
|
||||||
" REQUIREMENTS
|
" REQUIREMENTS
|
||||||
" ============
|
" ============
|
||||||
" - urpmi
|
" - urpmi
|
||||||
" - python-2.7
|
" - python-2.7
|
||||||
" - lzma
|
" - lzma
|
||||||
|
@ -82,7 +82,7 @@ def ParseCommandLine():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description=_("Tool for comparing sets of repositories."))
|
description=_("Tool for comparing sets of repositories."))
|
||||||
group = parser.add_argument_group(_('global parameters'),
|
group = parser.add_argument_group(_('global parameters'),
|
||||||
description=_("Parameters used in all cases."))
|
description=_("Parameters used in all cases."))
|
||||||
group.add_argument("--old", "-o", action="append", nargs='+', required="True",
|
group.add_argument("--old", "-o", action="append", nargs='+', required="True",
|
||||||
metavar="OLD_REPO", help=_("URL or PATH to old repositories"))
|
metavar="OLD_REPO", help=_("URL or PATH to old repositories"))
|
||||||
group.add_argument("--new", "-n", action="append", nargs='+', required="True",
|
group.add_argument("--new", "-n", action="append", nargs='+', required="True",
|
||||||
|
@ -90,43 +90,43 @@ def ParseCommandLine():
|
||||||
group.add_argument("--quiet", "-q", action="store_false",
|
group.add_argument("--quiet", "-q", action="store_false",
|
||||||
help=_("Hide service messages."))
|
help=_("Hide service messages."))
|
||||||
group.add_argument("--no-release", "-r", action="store_true",
|
group.add_argument("--no-release", "-r", action="store_true",
|
||||||
help=_("Ignore release during package compare."))
|
help=_("Ignore release during package compare."))
|
||||||
group.add_argument("--show-summary", action="store_true",
|
group.add_argument("--show-summary", action="store_true",
|
||||||
help=_("Output summary."))
|
help=_("Output summary."))
|
||||||
group.add_argument("--output", "-out", action="store", default=default_output,
|
group.add_argument("--output", "-out", action="store", default=default_output,
|
||||||
metavar="OUTPUT_FILE", help=_("Change standart output to \"OUTPUT_FILE\"."))
|
metavar="OUTPUT_FILE", help=_("Change standart output to \"OUTPUT_FILE\"."))
|
||||||
group.add_argument("--ignore", "-i", action="store", default='',
|
group.add_argument("--ignore", "-i", action="store", default='',
|
||||||
metavar="IGNORELIST", help=_("File with list of ignored packages"))
|
metavar="IGNORELIST", help=_("File with list of ignored packages"))
|
||||||
group = parser.add_argument_group(_('text mode parameters'),
|
group = parser.add_argument_group(_('text mode parameters'),
|
||||||
description=_("Parameters used only in text mode. (--html not present)"))
|
description=_("Parameters used only in text mode. (--html not present)"))
|
||||||
group.add_argument("--size", "-s", action="store_true",
|
group.add_argument("--size", "-s", action="store_true",
|
||||||
help=_("Show differences in package sizes."))
|
help=_("Show differences in package sizes."))
|
||||||
group.add_argument("--simple", action="store_false",
|
group.add_argument("--simple", action="store_false",
|
||||||
help=_("Simple output format."))
|
help=_("Simple output format."))
|
||||||
group.add_argument("--changelog", "-c", action="store_true",
|
group.add_argument("--changelog", "-c", action="store_true",
|
||||||
help=_("Show changelog difference."))
|
help=_("Show changelog difference."))
|
||||||
group = parser.add_argument_group(_('HTML mode parameters'),
|
group = parser.add_argument_group(_('HTML mode parameters'),
|
||||||
description=_("Parameters used only in HTML mode. (--html is present)"))
|
description=_("Parameters used only in HTML mode. (--html is present)"))
|
||||||
group.add_argument("--html", action="store_true",
|
group.add_argument("--html", action="store_true",
|
||||||
help=_("Output in HTML format, if --output is not present\
|
help=_("Output in HTML format, if --output is not present\
|
||||||
\"%s\" will be created in current directory. \
|
\"%s\" will be created in current directory. \
|
||||||
--size, --simple and --changelog options are ignored.") % htmlname)
|
--size, --simple and --changelog options are ignored.") % htmlname)
|
||||||
group.add_argument("--reponames", action="store", nargs='+', default='',
|
group.add_argument("--reponames", action="store", nargs='+', default='',
|
||||||
metavar="REPONAME", help=_("Repository names for output."))
|
metavar="REPONAME", help=_("Repository names for output."))
|
||||||
group.add_argument("--title", "-t", action="store",
|
group.add_argument("--title", "-t", action="store",
|
||||||
default="Difference between repositories.",
|
default="Difference between repositories.",
|
||||||
help=_("Set title."))
|
help=_("Set title."))
|
||||||
group = parser.add_argument_group(_('Filters'),
|
group = parser.add_argument_group(_('Filters'),
|
||||||
description=_("Filters for output. If none selected then every type will\
|
description=_("Filters for output. If none selected then every type will\
|
||||||
be shown"))
|
be shown"))
|
||||||
group.add_argument("--show-new", "-N", action="store_true",
|
group.add_argument("--show-new", "-N", action="store_true",
|
||||||
help=_("Show new packages"))
|
help=_("Show new packages"))
|
||||||
group.add_argument("--show-removed", "-R", action="store_true",
|
group.add_argument("--show-removed", "-R", action="store_true",
|
||||||
help=_("Show removed packages"))
|
help=_("Show removed packages"))
|
||||||
group.add_argument("--show-updated", "-U", action="store_true",
|
group.add_argument("--show-updated", "-U", action="store_true",
|
||||||
help=_("Show updated packages"))
|
help=_("Show updated packages"))
|
||||||
group.add_argument("--show-downgraded", "-D", action="store_true",
|
group.add_argument("--show-downgraded", "-D", action="store_true",
|
||||||
help=_("Show downgraded packages"))
|
help=_("Show downgraded packages"))
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
def exit_proc(arg):
|
def exit_proc(arg):
|
||||||
|
@ -182,7 +182,7 @@ def CheckArgs(urlpath, arg):
|
||||||
urlpath = urltmp + "media_info/"
|
urlpath = urltmp + "media_info/"
|
||||||
CheckURL(urlpath, arg)
|
CheckURL(urlpath, arg)
|
||||||
else:
|
else:
|
||||||
print _("Error: \"%s\" is not correct url, path or name of repository") % urlpath
|
print _("Error: \"%s\" is not correct url, path or name of repository") % urlpath
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
return urlpath
|
return urlpath
|
||||||
|
|
||||||
|
@ -201,15 +201,15 @@ def CheckOutput(arg):
|
||||||
except:
|
except:
|
||||||
print _("Error: Cannot open %s for writing.") % htmlname
|
print _("Error: Cannot open %s for writing.") % htmlname
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
arg.output = sys.stdout
|
arg.output = sys.stdout
|
||||||
return
|
return
|
||||||
|
|
||||||
if(file_output != ''):
|
if(file_output != ''):
|
||||||
if(os.path.isfile(file_output)):
|
if(os.path.isfile(file_output)):
|
||||||
print _("Error: File %s already exists") % file_output
|
print _("Error: File %s already exists") % file_output
|
||||||
arg.output = None
|
arg.output = None
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
else:
|
else:
|
||||||
dirname = os.path.dirname(file_output)
|
dirname = os.path.dirname(file_output)
|
||||||
|
@ -218,11 +218,11 @@ def CheckOutput(arg):
|
||||||
arg.output = open(file_output, "w")
|
arg.output = open(file_output, "w")
|
||||||
except IOError:
|
except IOError:
|
||||||
print _("Error: File %s cannot be created") % file_output
|
print _("Error: File %s cannot be created") % file_output
|
||||||
arg.output = None
|
arg.output = None
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
else:
|
else:
|
||||||
print _("Error: Path %s does not exist.") % dirname
|
print _("Error: Path %s does not exist.") % dirname
|
||||||
arg.output = None
|
arg.output = None
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
|
|
||||||
def CheckParam(arg):
|
def CheckParam(arg):
|
||||||
|
@ -235,12 +235,12 @@ def CheckParam(arg):
|
||||||
arg.simple = 0
|
arg.simple = 0
|
||||||
arg.changelog = 0
|
arg.changelog = 0
|
||||||
if (arg.reponames != '') and (len(arg.old) + len(arg.new) != len(arg.reponames)):
|
if (arg.reponames != '') and (len(arg.old) + len(arg.new) != len(arg.reponames)):
|
||||||
print _("Error: number of REPONAME's(%s) are not equal to number of groups(%s)") % \
|
print _("Error: number of REPONAME's(%s) are not equal to number of groups(%s)") % \
|
||||||
(str(len(arg.reponames)), str(len(arg.old) + len(arg.new)))
|
(str(len(arg.reponames)), str(len(arg.old) + len(arg.new)))
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
else:
|
else:
|
||||||
arg.repnames = ''
|
arg.repnames = ''
|
||||||
arg.title = ''
|
arg.title = ''
|
||||||
|
|
||||||
def GetFile(urlpath, filename, localdir, arg):
|
def GetFile(urlpath, filename, localdir, arg):
|
||||||
"""Donwload archive.
|
"""Donwload archive.
|
||||||
|
@ -276,47 +276,47 @@ def GetFiles(arg):
|
||||||
file_name = []
|
file_name = []
|
||||||
file_path = []
|
file_path = []
|
||||||
for i in range(len(arg.old)):
|
for i in range(len(arg.old)):
|
||||||
for j in range(len(arg.old[i])):
|
for j in range(len(arg.old[i])):
|
||||||
file_name.append(synthesis_arch)
|
file_name.append(synthesis_arch)
|
||||||
file_dir.append(arg.temp_old[i][j])
|
file_dir.append(arg.temp_old[i][j])
|
||||||
file_path.append(arg.old[i][j] + "media_info/")
|
file_path.append(arg.old[i][j] + "media_info/")
|
||||||
if ifchangelog:
|
if ifchangelog:
|
||||||
file_name.append(changelog_arch)
|
file_name.append(changelog_arch)
|
||||||
file_dir.append(arg.temp_old[i][j])
|
file_dir.append(arg.temp_old[i][j])
|
||||||
file_path.append(arg.old[i][j] + "media_info/")
|
file_path.append(arg.old[i][j] + "media_info/")
|
||||||
|
|
||||||
for i in range(len(arg.new)):
|
for i in range(len(arg.new)):
|
||||||
for j in range(len(arg.new[i])):
|
for j in range(len(arg.new[i])):
|
||||||
file_name.append(synthesis_arch)
|
file_name.append(synthesis_arch)
|
||||||
file_dir.append(arg.temp_new[i][j])
|
file_dir.append(arg.temp_new[i][j])
|
||||||
file_path.append(arg.new[i][j] + "media_info/")
|
file_path.append(arg.new[i][j] + "media_info/")
|
||||||
if ifchangelog:
|
if ifchangelog:
|
||||||
file_name.append(changelog_arch)
|
file_name.append(changelog_arch)
|
||||||
file_dir.append(arg.temp_new[i][j])
|
file_dir.append(arg.temp_new[i][j])
|
||||||
file_path.append(arg.new[i][j] + "media_info/")
|
file_path.append(arg.new[i][j] + "media_info/")
|
||||||
|
|
||||||
for i in range(len(file_name)):
|
for i in range(len(file_name)):
|
||||||
GetFile(file_path[i], file_name[i], file_dir[i], arg)
|
GetFile(file_path[i], file_name[i], file_dir[i], arg)
|
||||||
|
|
||||||
def ReadIgnoreList(arg):
|
def ReadIgnoreList(arg):
|
||||||
ignorefile = arg.ignore
|
ignorefile = arg.ignore
|
||||||
ignorelist = []
|
ignorelist = []
|
||||||
if not os.path.isfile(ignorefile):
|
if not os.path.isfile(ignorefile):
|
||||||
print _("Error: file %s does not exist.") % ignorefile
|
print _("Error: file %s does not exist.") % ignorefile
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
try:
|
try:
|
||||||
ifile = open(ignorefile)
|
ifile = open(ignorefile)
|
||||||
for string in ifile:
|
for string in ifile:
|
||||||
if string == '\n':
|
if string == '\n':
|
||||||
continue
|
continue
|
||||||
if string.endswith('\n'):
|
if string.endswith('\n'):
|
||||||
string = string[:-1]
|
string = string[:-1]
|
||||||
ignorelist.append(string)
|
ignorelist.append(string)
|
||||||
ifile.close()
|
ifile.close()
|
||||||
ignorelist.sort()
|
ignorelist.sort()
|
||||||
except:
|
except:
|
||||||
print _("Error: file %s cannot be read.") % ignorefile
|
print _("Error: file %s cannot be read.") % ignorefile
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
return ignorelist
|
return ignorelist
|
||||||
|
|
||||||
def RemoveIgnored(dict_in, ignorelist):
|
def RemoveIgnored(dict_in, ignorelist):
|
||||||
|
@ -327,24 +327,24 @@ def RemoveIgnored(dict_in, ignorelist):
|
||||||
j = 0
|
j = 0
|
||||||
jmax = len(ignorelist)
|
jmax = len(ignorelist)
|
||||||
while (i < imax) and (j < jmax):
|
while (i < imax) and (j < jmax):
|
||||||
if ignorelist[j].endswith('*'):
|
if ignorelist[j].endswith('*'):
|
||||||
comp_str = ignorelist[j][:-1]
|
comp_str = ignorelist[j][:-1]
|
||||||
while (i < imax) and (sorted_list[i] < comp_str):
|
while (i < imax) and (sorted_list[i] < comp_str):
|
||||||
dict_out[sorted_list[i]] = dict_in[sorted_list[i]]
|
dict_out[sorted_list[i]] = dict_in[sorted_list[i]]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
while (i < imax) and sorted_list[i].startswith(comp_str):
|
while (i < imax) and sorted_list[i].startswith(comp_str):
|
||||||
i = i + 1
|
i = i + 1
|
||||||
else:
|
else:
|
||||||
comp_str = ignorelist[j]
|
comp_str = ignorelist[j]
|
||||||
while (i < imax) and (sorted_list[i] < comp_str):
|
while (i < imax) and (sorted_list[i] < comp_str):
|
||||||
dict_out[sorted_list[i]] = dict_in[sorted_list[i]]
|
dict_out[sorted_list[i]] = dict_in[sorted_list[i]]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if (i < imax) and (sorted_list[i] == comp_str):
|
if (i < imax) and (sorted_list[i] == comp_str):
|
||||||
i = i + 1
|
i = i + 1
|
||||||
j = j + 1
|
j = j + 1
|
||||||
if (i < imax) and (j == jmax):
|
if (i < imax) and (j == jmax):
|
||||||
for i in range(i, imax):
|
for i in range(i, imax):
|
||||||
dict_out[sorted_list[i]] = dict_in[sorted_list[i]]
|
dict_out[sorted_list[i]] = dict_in[sorted_list[i]]
|
||||||
return dict_out
|
return dict_out
|
||||||
|
|
||||||
def RenameSynthFile(localdir, arg):
|
def RenameSynthFile(localdir, arg):
|
||||||
|
@ -443,8 +443,8 @@ def ParseSynthesis(synthfile, pkgdict, arg):
|
||||||
synth = open(synthfile)
|
synth = open(synthfile)
|
||||||
tmp = ['', '', '']
|
tmp = ['', '', '']
|
||||||
for synthline in synth:
|
for synthline in synth:
|
||||||
if not synthline.startswith('@'):
|
if not synthline.startswith('@'):
|
||||||
continue
|
continue
|
||||||
if synthline.endswith('\n'):
|
if synthline.endswith('\n'):
|
||||||
synthline = synthline[:-1]
|
synthline = synthline[:-1]
|
||||||
tmpline = synthline.split('@')
|
tmpline = synthline.split('@')
|
||||||
|
@ -458,7 +458,7 @@ def ParseSynthesis(synthfile, pkgdict, arg):
|
||||||
disttagepoch = ChkTagEpoch(tmp[0]) #disttag + distepoch
|
disttagepoch = ChkTagEpoch(tmp[0]) #disttag + distepoch
|
||||||
tmp[2] = ParseVersion(tmp[2])
|
tmp[2] = ParseVersion(tmp[2])
|
||||||
(name, version, release) = RPMNameFilter(tmp[0][0],
|
(name, version, release) = RPMNameFilter(tmp[0][0],
|
||||||
disttagepoch, ifreleaseignore)
|
disttagepoch, ifreleaseignore)
|
||||||
verrel = (version, release, tmp[0][1])
|
verrel = (version, release, tmp[0][1])
|
||||||
if(not name in pkgdict):
|
if(not name in pkgdict):
|
||||||
pkgdict[name]=(verrel, (tmp[0], tmp[1], tmp[2]))
|
pkgdict[name]=(verrel, (tmp[0], tmp[1], tmp[2]))
|
||||||
|
@ -510,8 +510,8 @@ def RPMNameFilter(rpmname, disttagepoch, ifreleaseignore):
|
||||||
issrc = (tmp.pop() == "src")
|
issrc = (tmp.pop() == "src")
|
||||||
ismageia = 0
|
ismageia = 0
|
||||||
if tmp[-1].startswith("mga"):
|
if tmp[-1].startswith("mga"):
|
||||||
tmp.pop()
|
tmp.pop()
|
||||||
ismageia = 1
|
ismageia = 1
|
||||||
lastpart = '.'.join(tmp)
|
lastpart = '.'.join(tmp)
|
||||||
if (lastpart[0].isdigit() or (not lastpart.startswith(disttagepoch))) and\
|
if (lastpart[0].isdigit() or (not lastpart.startswith(disttagepoch))) and\
|
||||||
((not lastpart.isdigit()) or issrc or ismageia):
|
((not lastpart.isdigit()) or issrc or ismageia):
|
||||||
|
@ -523,8 +523,8 @@ def RPMNameFilter(rpmname, disttagepoch, ifreleaseignore):
|
||||||
ver = string[-2]
|
ver = string[-2]
|
||||||
rel = string[-1]
|
rel = string[-1]
|
||||||
if ifreleaseignore:
|
if ifreleaseignore:
|
||||||
rel = ""
|
rel = ""
|
||||||
return (name, ver, rel)
|
return (name, ver, rel)
|
||||||
|
|
||||||
def compare_versions(first_entry, second_entry):
|
def compare_versions(first_entry, second_entry):
|
||||||
"""Compare two verrel tuples.
|
"""Compare two verrel tuples.
|
||||||
|
@ -557,16 +557,16 @@ def ParsePackage(arg):
|
||||||
ignorelist = arg.ignorelist
|
ignorelist = arg.ignorelist
|
||||||
pkgdict_old = {}
|
pkgdict_old = {}
|
||||||
for tmp_list in arg.temp_old:
|
for tmp_list in arg.temp_old:
|
||||||
for directory in tmp_list:
|
for directory in tmp_list:
|
||||||
RenameSynthFile(directory, arg)
|
RenameSynthFile(directory, arg)
|
||||||
UnpackFiles(directory, ifchangelog, ifnotquiet)
|
UnpackFiles(directory, ifchangelog, ifnotquiet)
|
||||||
ParseSynthesis(directory + synthesis_file, pkgdict_old, arg)
|
ParseSynthesis(directory + synthesis_file, pkgdict_old, arg)
|
||||||
pkgdict_new = {}
|
pkgdict_new = {}
|
||||||
for tmp_list in arg.temp_new:
|
for tmp_list in arg.temp_new:
|
||||||
for directory in tmp_list:
|
for directory in tmp_list:
|
||||||
RenameSynthFile(directory, arg)
|
RenameSynthFile(directory, arg)
|
||||||
UnpackFiles(directory, ifchangelog, ifnotquiet)
|
UnpackFiles(directory, ifchangelog, ifnotquiet)
|
||||||
ParseSynthesis(directory + synthesis_file, pkgdict_new, arg)
|
ParseSynthesis(directory + synthesis_file, pkgdict_new, arg)
|
||||||
pkgdict_old = RemoveIgnored(pkgdict_old, ignorelist)
|
pkgdict_old = RemoveIgnored(pkgdict_old, ignorelist)
|
||||||
pdkdict_new = RemoveIgnored(pkgdict_new, ignorelist)
|
pdkdict_new = RemoveIgnored(pkgdict_new, ignorelist)
|
||||||
ignorelist = ""
|
ignorelist = ""
|
||||||
|
@ -797,8 +797,8 @@ def ParseLogfile(dict_log, logfile, dict_upd_packages, mode, arg):
|
||||||
else:
|
else:
|
||||||
entry_time = 0
|
entry_time = 0
|
||||||
|
|
||||||
if(mode == 1) and (not ifdowngraded) and\
|
if(mode == 1) and (not ifdowngraded) and\
|
||||||
(result_key in dict_log) and\
|
(result_key in dict_log) and\
|
||||||
(entry_time <= dict_log[result_key][0][1][0]):
|
(entry_time <= dict_log[result_key][0][1][0]):
|
||||||
break
|
break
|
||||||
log_child = log_current.children
|
log_child = log_current.children
|
||||||
|
@ -809,16 +809,16 @@ def ParseLogfile(dict_log, logfile, dict_upd_packages, mode, arg):
|
||||||
entry_text = log_child.content
|
entry_text = log_child.content
|
||||||
log_child = log_child.next
|
log_child = log_child.next
|
||||||
result_changelog.append((entry_time, entry_name, entry_text))
|
result_changelog.append((entry_time, entry_name, entry_text))
|
||||||
# if "old" repository do not have changelog of the package
|
# if "old" repository do not have changelog of the package
|
||||||
if(mode == 1) and (not result_key in dict_log):
|
if(mode == 1) and (not result_key in dict_log):
|
||||||
dict_log[result_key] = []
|
dict_log[result_key] = []
|
||||||
dict_log[result_key].append([])
|
dict_log[result_key].append([])
|
||||||
dict_log[result_key].append([])
|
dict_log[result_key].append([])
|
||||||
dict_log[result_key][0] = (verrel, [])
|
dict_log[result_key][0] = (verrel, [])
|
||||||
if not ifdowngraded:
|
if not ifdowngraded:
|
||||||
dict_log[result_key][0] = (verrel, result_changelog[0])
|
dict_log[result_key][0] = (verrel, result_changelog[0])
|
||||||
else:
|
else:
|
||||||
dict_log[result_key][0] = (verrel, result_changelog)
|
dict_log[result_key][0] = (verrel, result_changelog)
|
||||||
if(mode == ifdowngraded):
|
if(mode == ifdowngraded):
|
||||||
break
|
break
|
||||||
log_current = log_current.next
|
log_current = log_current.next
|
||||||
|
@ -867,11 +867,11 @@ def GenerateLogfileDiff(dict_upd_packages, arg):
|
||||||
dict_log = {}
|
dict_log = {}
|
||||||
|
|
||||||
for i in temp_old:
|
for i in temp_old:
|
||||||
for old_dir in temp_old[i]:
|
for old_dir in temp_old[i]:
|
||||||
ParseLogfile(dict_log, old_dir + changelog_file, dict_upd_packages, 0, arg)
|
ParseLogfile(dict_log, old_dir + changelog_file, dict_upd_packages, 0, arg)
|
||||||
for i in temp_new:
|
for i in temp_new:
|
||||||
for new_dir in temp_new[i]:
|
for new_dir in temp_new[i]:
|
||||||
ParseLogfile(dict_log, new_dir + changelog_file, dict_upd_packages, 1, arg)
|
ParseLogfile(dict_log, new_dir + changelog_file, dict_upd_packages, 1, arg)
|
||||||
|
|
||||||
for name in dict_upd_packages:
|
for name in dict_upd_packages:
|
||||||
if(name in dict_log):
|
if(name in dict_log):
|
||||||
|
@ -895,11 +895,11 @@ def ChangelogPrint(changes_list, file_output):
|
||||||
changes_list = [(time,author,text)]
|
changes_list = [(time,author,text)]
|
||||||
"""
|
"""
|
||||||
if len(changes_list) > 0:
|
if len(changes_list) > 0:
|
||||||
for entry in changes_list:
|
for entry in changes_list:
|
||||||
file_output.write("* " + str(date.fromtimestamp(float(entry[0]))) +\
|
file_output.write("* " + str(date.fromtimestamp(float(entry[0]))) +\
|
||||||
" " + entry[1] + '\n' + entry[2] + '\n\n')
|
" " + entry[1] + '\n' + entry[2] + '\n\n')
|
||||||
else:
|
else:
|
||||||
file_output.write('\n')
|
file_output.write('\n')
|
||||||
|
|
||||||
def PrintLogfileDiff(package_name, dict_logfile_diff, file_output):
|
def PrintLogfileDiff(package_name, dict_logfile_diff, file_output):
|
||||||
"""Changelog difference.
|
"""Changelog difference.
|
||||||
|
@ -936,21 +936,21 @@ def ProcessUpdPackages(dict_upd_packages, dict_logfile_diff, arg):
|
||||||
for name in sorted_list:
|
for name in sorted_list:
|
||||||
package = dict_upd_packages[name][1][1][0][0]
|
package = dict_upd_packages[name][1][1][0][0]
|
||||||
if ifnotsimple:
|
if ifnotsimple:
|
||||||
if dict_upd_packages[name][2]:
|
if dict_upd_packages[name][2]:
|
||||||
if ifdown:
|
if ifdown:
|
||||||
file_output.write(package + '\n' + '-'*len(package) + '\n')
|
file_output.write(package + '\n' + '-'*len(package) + '\n')
|
||||||
file_output.write(_(" ***DOWNGRADED***\n"))
|
file_output.write(_(" ***DOWNGRADED***\n"))
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if ifup:
|
if ifup:
|
||||||
file_output.write(package + '\n' + '-'*len(package) + '\n')
|
file_output.write(package + '\n' + '-'*len(package) + '\n')
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
if ifchangelog:
|
if ifchangelog:
|
||||||
PrintLogfileDiff(name, dict_logfile_diff, file_output)
|
PrintLogfileDiff(name, dict_logfile_diff, file_output)
|
||||||
else:
|
else:
|
||||||
file_output.write('\n')
|
file_output.write('\n')
|
||||||
else:
|
else:
|
||||||
old_package = dict_upd_packages[name][0][1][0][0]
|
old_package = dict_upd_packages[name][0][1][0][0]
|
||||||
file_output.write(name + ": " + old_package + " -> " + package + '\n')
|
file_output.write(name + ": " + old_package + " -> " + package + '\n')
|
||||||
|
@ -968,42 +968,42 @@ def PrintSummary(dict_new_packages, dict_del_packages, dict_upd_packages, arg):
|
||||||
ifhtml = arg.html
|
ifhtml = arg.html
|
||||||
|
|
||||||
if ifhtml:
|
if ifhtml:
|
||||||
endstr = '<br />'
|
endstr = '<br />'
|
||||||
else:
|
else:
|
||||||
endstr = '\n'
|
endstr = '\n'
|
||||||
|
|
||||||
tmp_str = _("Summary:")
|
tmp_str = _("Summary:")
|
||||||
if ifhtml:
|
if ifhtml:
|
||||||
tmp_str = '<p class="bold">' + tmp_str + '</p>'
|
tmp_str = '<p class="bold">' + tmp_str + '</p>'
|
||||||
else:
|
else:
|
||||||
tmp_str = tmp_str + endstr
|
tmp_str = tmp_str + endstr
|
||||||
if arg.show_new:
|
if arg.show_new:
|
||||||
length = len(dict_new_packages)
|
length = len(dict_new_packages)
|
||||||
if length:
|
if length:
|
||||||
tmp_str = tmp_str + (_(" Total added packages: ") + str(length) + endstr)
|
tmp_str = tmp_str + (_(" Total added packages: ") + str(length) + endstr)
|
||||||
if arg.show_removed:
|
if arg.show_removed:
|
||||||
length = len(dict_del_packages)
|
length = len(dict_del_packages)
|
||||||
if length:
|
if length:
|
||||||
tmp_str = tmp_str + (_(" Total removed packages: ") + str(length) + endstr)
|
tmp_str = tmp_str + (_(" Total removed packages: ") + str(length) + endstr)
|
||||||
if arg.show_updated or arg.show_downgraded:
|
if arg.show_updated or arg.show_downgraded:
|
||||||
length = 0
|
length = 0
|
||||||
length_d = 0
|
length_d = 0
|
||||||
for packagename in dict_upd_packages:
|
for packagename in dict_upd_packages:
|
||||||
if dict_upd_packages[packagename][2] == 0:
|
if dict_upd_packages[packagename][2] == 0:
|
||||||
length = length + 1
|
length = length + 1
|
||||||
else:
|
else:
|
||||||
length_d = length_d + 1
|
length_d = length_d + 1
|
||||||
if arg.show_updated:
|
if arg.show_updated:
|
||||||
if length:
|
if length:
|
||||||
tmp_str = tmp_str + (_(" Total updated packages: ") + str(length) + endstr)
|
tmp_str = tmp_str + (_(" Total updated packages: ") + str(length) + endstr)
|
||||||
if arg.show_downgraded:
|
if arg.show_downgraded:
|
||||||
if length_d:
|
if length_d:
|
||||||
tmp_str = tmp_str + (_(" Total downgraded packages: ") + str(length_d) + endstr)
|
tmp_str = tmp_str + (_(" Total downgraded packages: ") + str(length_d) + endstr)
|
||||||
|
|
||||||
if ifhtml:
|
if ifhtml:
|
||||||
return tmp_str
|
return tmp_str
|
||||||
else:
|
else:
|
||||||
file_ouput.write(tmp_str)
|
file_ouput.write(tmp_str)
|
||||||
|
|
||||||
def HTML_ParsePackage(arg):
|
def HTML_ParsePackage(arg):
|
||||||
"""Parse hdlist.
|
"""Parse hdlist.
|
||||||
|
@ -1018,19 +1018,19 @@ def HTML_ParsePackage(arg):
|
||||||
html_new_dict_list = []
|
html_new_dict_list = []
|
||||||
|
|
||||||
for tmp_list in arg.temp_old:
|
for tmp_list in arg.temp_old:
|
||||||
tmp_dict = {}
|
tmp_dict = {}
|
||||||
for directory in tmp_list:
|
for directory in tmp_list:
|
||||||
RenameSynthFile(directory, arg)
|
RenameSynthFile(directory, arg)
|
||||||
UnpackFiles(directory, 0, ifnotquiet)
|
UnpackFiles(directory, 0, ifnotquiet)
|
||||||
ParseSynthesis(directory + synthesis_file, tmp_dict, arg)
|
ParseSynthesis(directory + synthesis_file, tmp_dict, arg)
|
||||||
html_old_dict_list.append(RemoveIgnored(tmp_dict, ignorelist))
|
html_old_dict_list.append(RemoveIgnored(tmp_dict, ignorelist))
|
||||||
for tmp_list in arg.temp_new:
|
for tmp_list in arg.temp_new:
|
||||||
tmp_dict = {}
|
tmp_dict = {}
|
||||||
for directory in tmp_list:
|
for directory in tmp_list:
|
||||||
RenameSynthFile(directory, arg)
|
RenameSynthFile(directory, arg)
|
||||||
UnpackFiles(directory, 0, ifnotquiet)
|
UnpackFiles(directory, 0, ifnotquiet)
|
||||||
ParseSynthesis(directory + synthesis_file, tmp_dict, arg)
|
ParseSynthesis(directory + synthesis_file, tmp_dict, arg)
|
||||||
html_new_dict_list.append(RemoveIgnored(tmp_dict, ignorelist))
|
html_new_dict_list.append(RemoveIgnored(tmp_dict, ignorelist))
|
||||||
ignorelist = ""
|
ignorelist = ""
|
||||||
arg.ignorelist = ""
|
arg.ignorelist = ""
|
||||||
return html_old_dict_list, html_new_dict_list
|
return html_old_dict_list, html_new_dict_list
|
||||||
|
@ -1292,7 +1292,7 @@ def HTML_OutputHead(arg):
|
||||||
'<body>\n\n')
|
'<body>\n\n')
|
||||||
|
|
||||||
def GetRepoInfo(dict_packages, packagename, lenold, lennew, list_dict_old,
|
def GetRepoInfo(dict_packages, packagename, lenold, lennew, list_dict_old,
|
||||||
list_dict_new, ifreleaseignore):
|
list_dict_new, ifreleaseignore):
|
||||||
"""Generate package-specific information.
|
"""Generate package-specific information.
|
||||||
|
|
||||||
Generates class and name to be displayed in the table.
|
Generates class and name to be displayed in the table.
|
||||||
|
@ -1304,9 +1304,9 @@ def GetRepoInfo(dict_packages, packagename, lenold, lennew, list_dict_old,
|
||||||
tmpstr = ""
|
tmpstr = ""
|
||||||
for i in range(lenold):
|
for i in range(lenold):
|
||||||
if packagename in list_dict_old[i]:
|
if packagename in list_dict_old[i]:
|
||||||
tmpstr = list_dict_old[i][packagename][0][0]
|
tmpstr = list_dict_old[i][packagename][0][0]
|
||||||
if not ifreleaseignore:
|
if not ifreleaseignore:
|
||||||
tmpstr = tmpstr + '-' + list_dict_old[i][packagename][0][1]
|
tmpstr = tmpstr + '-' + list_dict_old[i][packagename][0][1]
|
||||||
result1.append(tmpstr)
|
result1.append(tmpstr)
|
||||||
else:
|
else:
|
||||||
result1.append("N/A")
|
result1.append("N/A")
|
||||||
|
@ -1315,9 +1315,9 @@ def GetRepoInfo(dict_packages, packagename, lenold, lennew, list_dict_old,
|
||||||
tmplist = dict_packages[packagename]
|
tmplist = dict_packages[packagename]
|
||||||
tmpdict = {}
|
tmpdict = {}
|
||||||
for (entry, reponum, entry_type) in dict_packages[packagename]:
|
for (entry, reponum, entry_type) in dict_packages[packagename]:
|
||||||
tmpstr = entry[0][0]
|
tmpstr = entry[0][0]
|
||||||
if not ifreleaseignore:
|
if not ifreleaseignore:
|
||||||
tmpstr = tmpstr + '-' + entry[0][1]
|
tmpstr = tmpstr + '-' + entry[0][1]
|
||||||
tmpdict[reponum] = (tmpstr, entry_type)
|
tmpdict[reponum] = (tmpstr, entry_type)
|
||||||
|
|
||||||
for i in range(lennew):
|
for i in range(lennew):
|
||||||
|
@ -1326,9 +1326,9 @@ def GetRepoInfo(dict_packages, packagename, lenold, lennew, list_dict_old,
|
||||||
result1.append("N/A")
|
result1.append("N/A")
|
||||||
result2.append("")
|
result2.append("")
|
||||||
else:
|
else:
|
||||||
tmpstr = list_dict_new[i][packagename][0][0]
|
tmpstr = list_dict_new[i][packagename][0][0]
|
||||||
if not ifreleaseignore:
|
if not ifreleaseignore:
|
||||||
tmpstr = tmpstr + '-' + list_dict_new[i][packagename][0][1]
|
tmpstr = tmpstr + '-' + list_dict_new[i][packagename][0][1]
|
||||||
result1.append(tmpstr)
|
result1.append(tmpstr)
|
||||||
result2.append("")
|
result2.append("")
|
||||||
else:
|
else:
|
||||||
|
@ -1380,22 +1380,22 @@ def HTML_OutputBody(dict_packages, list_dict_old, list_dict_new, arg):
|
||||||
|
|
||||||
all_list = []
|
all_list = []
|
||||||
for tmp_list in old:
|
for tmp_list in old:
|
||||||
all_list.extend(tmp_list)
|
all_list.extend(tmp_list)
|
||||||
for tmp_list in new:
|
for tmp_list in new:
|
||||||
all_list.extend(tmp_list)
|
all_list.extend(tmp_list)
|
||||||
lenold = len(old)
|
lenold = len(old)
|
||||||
lennew = len(new)
|
lennew = len(new)
|
||||||
length = lenold + lennew
|
length = lenold + lennew
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for k in range(lenold):
|
for k in range(lenold):
|
||||||
if reponames == '':
|
if reponames == '':
|
||||||
tmp_string = 'Repository group A' + str(i)
|
tmp_string = 'Repository group A' + str(i)
|
||||||
else:
|
else:
|
||||||
tmp_string = reponames[i]
|
tmp_string = reponames[i]
|
||||||
tmp_string = '<h2>' + tmp_string + ':</h2>\n<ul>\n'
|
tmp_string = '<h2>' + tmp_string + ':</h2>\n<ul>\n'
|
||||||
for z in range(len(old[k])):
|
for z in range(len(old[k])):
|
||||||
tmp_string = tmp_string + '<li><a href="' +\
|
tmp_string = tmp_string + '<li><a href="' +\
|
||||||
old[k][z] + '">' + old[k][z] + '</a></li>\n'
|
old[k][z] + '">' + old[k][z] + '</a></li>\n'
|
||||||
tmp_string = tmp_string + '</ul>\n'
|
tmp_string = tmp_string + '</ul>\n'
|
||||||
file_output.write(tmp_string)
|
file_output.write(tmp_string)
|
||||||
|
@ -1403,32 +1403,32 @@ def HTML_OutputBody(dict_packages, list_dict_old, list_dict_new, arg):
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for k in range(lennew):
|
for k in range(lennew):
|
||||||
if reponames == '':
|
if reponames == '':
|
||||||
tmp_string = 'Repository group B' + str(i)
|
tmp_string = 'Repository group B' + str(i)
|
||||||
else:
|
else:
|
||||||
tmp_string = reponames[i + lenold]
|
tmp_string = reponames[i + lenold]
|
||||||
tmp_string = '<h2>' + tmp_string + ':</h2>\n<ul>\n'
|
tmp_string = '<h2>' + tmp_string + ':</h2>\n<ul>\n'
|
||||||
for z in range(len(new[k])):
|
for z in range(len(new[k])):
|
||||||
tmp_string = tmp_string + '<li><a href="' +\
|
tmp_string = tmp_string + '<li><a href="' +\
|
||||||
new[k][z] + '">' + new[k][z] + '</a></li>\n'
|
new[k][z] + '">' + new[k][z] + '</a></li>\n'
|
||||||
tmp_string = tmp_string + '</ul>\n'
|
tmp_string = tmp_string + '</ul>\n'
|
||||||
file_output.write(tmp_string)
|
file_output.write(tmp_string)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while(i < length):
|
while(i < length):
|
||||||
if i < lenold:
|
if i < lenold:
|
||||||
if reponames == '':
|
if reponames == '':
|
||||||
temp = 'Group A' + str(i)
|
temp = 'Group A' + str(i)
|
||||||
else:
|
else:
|
||||||
temp = reponames[i]
|
temp = reponames[i]
|
||||||
repo_list.append('<th>' + temp + '</th>')
|
repo_list.append('<th>' + temp + '</th>')
|
||||||
else:
|
else:
|
||||||
ii = i + 1
|
ii = i + 1
|
||||||
if reponames == '':
|
if reponames == '':
|
||||||
temp = 'Group B' + str(i - lenold)
|
temp = 'Group B' + str(i - lenold)
|
||||||
else:
|
else:
|
||||||
temp = reponames[i]
|
temp = reponames[i]
|
||||||
repo_list.append('<th id="sortCelldiff'+str(ii)+'"><a id="sortCellLinkdiff'+str(ii)+'" title="Sort Ascending" href="javascript:sort_diff('+str(ii)+', \'className\')">'+temp+'</a></th>')
|
repo_list.append('<th id="sortCelldiff'+str(ii)+'"><a id="sortCellLinkdiff'+str(ii)+'" title="Sort Ascending" href="javascript:sort_diff('+str(ii)+', \'className\')">'+temp+'</a></th>')
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
|
@ -1447,12 +1447,12 @@ def HTML_OutputBody(dict_packages, list_dict_old, list_dict_new, arg):
|
||||||
for packagename in sorted_list:
|
for packagename in sorted_list:
|
||||||
(repo_name, repo_class, flag, show_filter) = GetRepoInfo(dict_packages, packagename,
|
(repo_name, repo_class, flag, show_filter) = GetRepoInfo(dict_packages, packagename,
|
||||||
lenold, lennew, list_dict_old, list_dict_new, ifreleaseignore)
|
lenold, lennew, list_dict_old, list_dict_new, ifreleaseignore)
|
||||||
res = 0
|
res = 0
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
if show_filter[i]*show_mask[i] == 1:
|
if show_filter[i]*show_mask[i] == 1:
|
||||||
res = 1
|
res = 1
|
||||||
if res == 0:
|
if res == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if flag:
|
if flag:
|
||||||
res = 0
|
res = 0
|
||||||
|
@ -1517,15 +1517,15 @@ def main(args):
|
||||||
tmp_output = arg.output
|
tmp_output = arg.output
|
||||||
arg.output = None
|
arg.output = None
|
||||||
for i in range(len(arg.old)):
|
for i in range(len(arg.old)):
|
||||||
arg.temp_old.append([])
|
arg.temp_old.append([])
|
||||||
for j in range(len(arg.old[i])):
|
for j in range(len(arg.old[i])):
|
||||||
arg.old[i][j] = CheckArgs(arg.old[i][j], arg)
|
arg.old[i][j] = CheckArgs(arg.old[i][j], arg)
|
||||||
arg.temp_old[i].append(head_old + str(i) + '-' + str(j) + '/')
|
arg.temp_old[i].append(head_old + str(i) + '-' + str(j) + '/')
|
||||||
for i in range(len(arg.new)):
|
for i in range(len(arg.new)):
|
||||||
arg.temp_new.append([])
|
arg.temp_new.append([])
|
||||||
for j in range(len(arg.new[i])):
|
for j in range(len(arg.new[i])):
|
||||||
arg.new[i][j] = CheckArgs(arg.new[i][j], arg)
|
arg.new[i][j] = CheckArgs(arg.new[i][j], arg)
|
||||||
arg.temp_new[i].append(head_new + str(i) + '-' + str(j) + '/')
|
arg.temp_new[i].append(head_new + str(i) + '-' + str(j) + '/')
|
||||||
arg.output = tmp_output
|
arg.output = tmp_output
|
||||||
arg.summary = ''
|
arg.summary = ''
|
||||||
CheckOutput(arg)
|
CheckOutput(arg)
|
||||||
|
@ -1538,16 +1538,16 @@ def main(args):
|
||||||
ifhtml = arg.html
|
ifhtml = arg.html
|
||||||
ifchangelog = arg.changelog
|
ifchangelog = arg.changelog
|
||||||
if (not arg.show_new) and (not arg.show_removed) and\
|
if (not arg.show_new) and (not arg.show_removed) and\
|
||||||
(not arg.show_updated) and (not arg.show_downgraded):
|
(not arg.show_updated) and (not arg.show_downgraded):
|
||||||
arg.show_new=True
|
arg.show_new=True
|
||||||
arg.show_removed=True
|
arg.show_removed=True
|
||||||
arg.show_updated=True
|
arg.show_updated=True
|
||||||
arg.show_downgraded=True
|
arg.show_downgraded=True
|
||||||
|
|
||||||
if arg.ignore:
|
if arg.ignore:
|
||||||
arg.ignorelist = ReadIgnoreList(arg)
|
arg.ignorelist = ReadIgnoreList(arg)
|
||||||
else:
|
else:
|
||||||
arg.ignorelist = []
|
arg.ignorelist = []
|
||||||
|
|
||||||
GetFiles(arg)
|
GetFiles(arg)
|
||||||
|
|
||||||
|
@ -1566,27 +1566,27 @@ def main(args):
|
||||||
dict_logfile_diff = {}
|
dict_logfile_diff = {}
|
||||||
|
|
||||||
if arg.show_new:
|
if arg.show_new:
|
||||||
ProcessNewPackages(dict_new_packages, arg.output)
|
ProcessNewPackages(dict_new_packages, arg.output)
|
||||||
if arg.show_removed:
|
if arg.show_removed:
|
||||||
ProcessDelPackages(dict_del_packages, dict_obsoleted, arg.output)
|
ProcessDelPackages(dict_del_packages, dict_obsoleted, arg.output)
|
||||||
if dict_upd_packages and (arg.show_updated or arg.show_downgraded):
|
if dict_upd_packages and (arg.show_updated or arg.show_downgraded):
|
||||||
ProcessUpdPackages(dict_upd_packages, dict_logfile_diff, arg)
|
ProcessUpdPackages(dict_upd_packages, dict_logfile_diff, arg)
|
||||||
if arg.show_summary:
|
if arg.show_summary:
|
||||||
PrintSummary(dict_new_packages, dict_del_packages, dict_upd_packages, arg)
|
PrintSummary(dict_new_packages, dict_del_packages, dict_upd_packages, arg)
|
||||||
else:
|
else:
|
||||||
(list_dict_old, list_dict_new) = HTML_ParsePackage(arg)
|
(list_dict_old, list_dict_new) = HTML_ParsePackage(arg)
|
||||||
dict_old = HTML_UniteDicts(list_dict_old)
|
dict_old = HTML_UniteDicts(list_dict_old)
|
||||||
if arg.show_summary:
|
if arg.show_summary:
|
||||||
dict_new = HTML_UniteDicts(list_dict_new)
|
dict_new = HTML_UniteDicts(list_dict_new)
|
||||||
(dict_new_packages, dict_del_packages, dict_upd_packages) = CreateDicts(
|
(dict_new_packages, dict_del_packages, dict_upd_packages) = CreateDicts(
|
||||||
dict_old, dict_new)
|
dict_old, dict_new)
|
||||||
arg.summary = PrintSummary(dict_new_packages, dict_del_packages, dict_upd_packages, arg)
|
arg.summary = PrintSummary(dict_new_packages, dict_del_packages, dict_upd_packages, arg)
|
||||||
dict_new = ''
|
dict_new = ''
|
||||||
dict_new_packages = ''
|
dict_new_packages = ''
|
||||||
dict_del_packages = ''
|
dict_del_packages = ''
|
||||||
dict_upd_packages = ''
|
dict_upd_packages = ''
|
||||||
dict_packages = HTML_CreateDicts(dict_old, list_dict_new)
|
dict_packages = HTML_CreateDicts(dict_old, list_dict_new)
|
||||||
dict_old = ''
|
dict_old = ''
|
||||||
HTML_Output(dict_packages, list_dict_old, list_dict_new, arg)
|
HTML_Output(dict_packages, list_dict_old, list_dict_new, arg)
|
||||||
|
|
||||||
exit_proc(arg)
|
exit_proc(arg)
|
||||||
|
|
Loading…
Add table
Reference in a new issue