Added support for JSON output format (Fixes #50).
This commit is contained in:
parent
cff8dd109b
commit
bbe07f061d
|
@ -26,9 +26,9 @@ import time
|
|||
import zipfile
|
||||
from . import basedir, localization, terminal, version
|
||||
|
||||
__available_formats__ = ["html", "htmlembedded", "text", "xml"]
|
||||
__available_formats__ = ["html", "htmlembedded", "json", "text", "xml"]
|
||||
|
||||
DEFAULT_FORMAT = __available_formats__[2]
|
||||
DEFAULT_FORMAT = __available_formats__[3]
|
||||
|
||||
__selected_format__ = DEFAULT_FORMAT
|
||||
|
||||
|
@ -53,7 +53,6 @@ def __output_html_template__(name):
|
|||
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), name)
|
||||
file_r = open(template_path, "rb")
|
||||
return file_r.read().decode("utf-8", "replace")
|
||||
|
||||
def __get_zip_file_content__(name, file_name="/html/flot.zip"):
|
||||
zip_file = zipfile.ZipFile(basedir.get_basedir() + file_name, "r")
|
||||
content = zip_file.read(name)
|
||||
|
@ -98,6 +97,8 @@ def output_header():
|
|||
hide_minor_authors=_("Hide minor authors"),
|
||||
show_minor_rows=_("Show rows with minor work"),
|
||||
hide_minor_rows=_("Hide rows with minor work")))
|
||||
elif __selected_format__ == "json":
|
||||
print("{\n\t\"gitinspector\": {")
|
||||
elif __selected_format__ == "xml":
|
||||
print("<gitinspector>")
|
||||
print("\t<version>" + version.__version__ + "</version>")
|
||||
|
@ -112,5 +113,7 @@ def output_footer():
|
|||
base = basedir.get_basedir()
|
||||
html_footer = __output_html_template__(base + "/html/html.footer")
|
||||
print(html_footer)
|
||||
elif __selected_format__ == "json":
|
||||
print("\n\t}\n}")
|
||||
elif __selected_format__ == "xml":
|
||||
print("</gitinspector>")
|
||||
|
|
|
@ -36,7 +36,6 @@ from .output.timelineoutput import TimelineOutput
|
|||
|
||||
localization.init()
|
||||
|
||||
|
||||
class Runner(object):
|
||||
def __init__(self):
|
||||
self.hard = False
|
||||
|
|
|
@ -32,7 +32,7 @@ def get_url(email, size=20):
|
|||
base_url = "https://www.gravatar.com/avatar/" + md5hash
|
||||
params = None
|
||||
|
||||
if format.get_selected() == "html":
|
||||
if format.get_selected() == "html" or format.get_selected() == "json":
|
||||
params = {"default": "identicon", "size": size}
|
||||
elif format.get_selected() == "xml":
|
||||
params = {"default": "identicon"}
|
||||
|
|
|
@ -26,7 +26,6 @@ from ..localization import N_
|
|||
from .. import blame, format, gravatar, terminal
|
||||
from .outputable import Outputable
|
||||
|
||||
|
||||
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
|
||||
"intact in the current revision")
|
||||
|
||||
|
@ -96,6 +95,28 @@ class BlameOutput(Outputable):
|
|||
|
||||
print(blame_xml)
|
||||
|
||||
def output_json(self):
|
||||
message_xml = "\t\t\t\"message\": \"" + _(BLAME_INFO_TEXT) + "\",\n"
|
||||
blame_xml = ""
|
||||
|
||||
for i in sorted(blame.__blame__.get_summed_blames().items()):
|
||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||
|
||||
name_xml = "\t\t\t\t\"name\": \"" + i[0] + "\",\n"
|
||||
gravatar_xml = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
|
||||
rows_xml = "\t\t\t\t\"rows\": " + str(i[1].rows) + ",\n"
|
||||
stability_xml = ("\t\t\t\t\"stability\": " + "{0:.1f}".format(blame.Blame.get_stability(i[0], i[1].rows,
|
||||
self.changes)) + ",\n")
|
||||
age_xml = ("\t\t\t\t\"age\": " + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n")
|
||||
percentage_in_comments_xml = ("\t\t\t\t\"percentage-in-comments\": " + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
|
||||
"\n")
|
||||
blame_xml += ("{\n" + name_xml + gravatar_xml + rows_xml + stability_xml + age_xml +
|
||||
percentage_in_comments_xml + "\t\t\t},")
|
||||
else:
|
||||
blame_xml = blame_xml[:-1]
|
||||
|
||||
print(",\n\t\t\"blame\": {\n" + message_xml + "\t\t\t\"authors\": [\n\t\t\t" + blame_xml + "]\n\t\t}", end="")
|
||||
|
||||
def output_text(self):
|
||||
if sys.stdout.isatty() and format.is_interactive_format():
|
||||
terminal.clear_row()
|
||||
|
|
|
@ -25,7 +25,6 @@ from ..localization import N_
|
|||
from .. import changes, format, gravatar, terminal
|
||||
from .outputable import Outputable
|
||||
|
||||
|
||||
HISTORICAL_INFO_TEXT = N_("The following historical commit information, by author, was found in the repository")
|
||||
NO_COMMITED_FILES_TEXT = N_("No commited files with the specified extensions were found")
|
||||
|
||||
|
@ -96,6 +95,38 @@ class ChangesOutput(Outputable):
|
|||
changes_xml += "</div></div>"
|
||||
print(changes_xml)
|
||||
|
||||
def output_json(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
|
||||
if authorinfo_list:
|
||||
message_xml = "\t\t\t\"message\": \"" + _(HISTORICAL_INFO_TEXT) + "\",\n"
|
||||
changes_xml = ""
|
||||
|
||||
for i in sorted(authorinfo_list):
|
||||
authorinfo = authorinfo_list.get(i)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
name_xml = "\t\t\t\t\"name\": \"" + i + "\",\n"
|
||||
gravatar_xml = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(self.changes.get_latest_email_by_author(i)) + "\",\n"
|
||||
commits_xml = "\t\t\t\t\"commits\": " + str(authorinfo.commits) + ",\n"
|
||||
insertions_xml = "\t\t\t\t\"insertions\": " + str(authorinfo.insertions) + ",\n"
|
||||
deletions_xml = "\t\t\t\t\"deletions\": " + str(authorinfo.deletions) + ",\n"
|
||||
percentage_xml = "\t\t\t\t\"percentage-of-changes\": " + "{0:.2f}".format(percentage) + "\n"
|
||||
|
||||
changes_xml += ("{\n" + name_xml + gravatar_xml + commits_xml + insertions_xml +
|
||||
deletions_xml + percentage_xml + "\t\t\t}")
|
||||
changes_xml += ","
|
||||
else:
|
||||
changes_xml = changes_xml[:-1]
|
||||
|
||||
print("\t\t\"changes\": {\n" + message_xml + "\t\t\t\"authors\": [\n\t\t\t" + changes_xml + "]\n\t\t}", end="")
|
||||
else:
|
||||
print("\t\t\"exception\": \"" + _(NO_COMMITED_FILES_TEXT) + "\"")
|
||||
|
||||
def output_text(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
|
|
|
@ -51,6 +51,24 @@ class ExtensionsOutput(Outputable):
|
|||
extensions_xml += "</p></div></div>"
|
||||
print(extensions_xml)
|
||||
|
||||
def output_json(self):
|
||||
if extensions.__located_extensions__:
|
||||
message_xml = "\t\t\t\"message\": \"" + _(EXTENSIONS_INFO_TEXT) + "\",\n"
|
||||
used_extensions_xml = ""
|
||||
unused_extensions_xml = ""
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
used_extensions_xml += "\"" + i + "\", "
|
||||
else:
|
||||
unused_extensions_xml += "\"" + i + "\", "
|
||||
|
||||
used_extensions_xml = used_extensions_xml[:-2]
|
||||
unused_extensions_xml = unused_extensions_xml[:-2]
|
||||
|
||||
print(",\n\t\t\"extensions\": {\n" + message_xml + "\t\t\t\"used\": [ " + used_extensions_xml + " ],\n" +
|
||||
"\t\t\t\"unused\": [ " + unused_extensions_xml + " ]\n" + "\t\t}", end="")
|
||||
|
||||
def output_text(self):
|
||||
if extensions.__located_extensions__:
|
||||
print("\n" + textwrap.fill("{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)),
|
||||
|
|
|
@ -25,7 +25,6 @@ from ..filtering import __filters__, has_filtered
|
|||
from .. import terminal
|
||||
from .outputable import Outputable
|
||||
|
||||
|
||||
FILTERING_INFO_TEXT = N_("The following files were excluded from the statistics due to the specified exclusion patterns")
|
||||
FILTERING_AUTHOR_INFO_TEXT = N_("The following authors were excluded from the statistics due to the specified exclusion patterns")
|
||||
FILTERING_EMAIL_INFO_TEXT = N_("The authors with the following emails were excluded from the statistics due to the specified " \
|
||||
|
@ -57,6 +56,33 @@ class FilteringOutput(Outputable):
|
|||
|
||||
print(filtering_xml)
|
||||
|
||||
@staticmethod
|
||||
def __output_json_section__(info_string, filtered, container_tagname):
|
||||
if filtered:
|
||||
message_xml = "\t\t\t\t\"message\": \"" + info_string + "\",\n"
|
||||
filtering_xml = ""
|
||||
|
||||
for i in filtered:
|
||||
filtering_xml += "\t\t\t\t\t\"" + i + "\",\n"
|
||||
else:
|
||||
filtering_xml = filtering_xml[:-3]
|
||||
|
||||
return "\n\t\t\t\"{0}\": {{\n".format(container_tagname) + message_xml + \
|
||||
"\t\t\t\t\"entries\": [\n" + filtering_xml + "\"\n\t\t\t\t]\n\t\t\t},"
|
||||
|
||||
return ""
|
||||
|
||||
def output_json(self):
|
||||
if has_filtered():
|
||||
output = ",\n\t\t\"filtering\": {"
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
|
||||
output = output[:-1]
|
||||
output += "\n\t\t}"
|
||||
print(output, end="")
|
||||
|
||||
@staticmethod
|
||||
def __output_text_section__(info_string, filtered):
|
||||
if filtered:
|
||||
|
|
|
@ -92,6 +92,42 @@ class MetricsOutput(Outputable):
|
|||
metrics_xml += "</div></div>"
|
||||
print(metrics_xml)
|
||||
|
||||
def output_json(self):
|
||||
metrics_logic = metrics.MetricsLogic()
|
||||
|
||||
if not metrics_logic.eloc and not metrics_logic.cyclomatic_complexity and not metrics_logic.cyclomatic_complexity_density:
|
||||
print(",\n\t\t\"metrics\": {\n\t\t\t\"message\": \"" + _(METRICS_MISSING_INFO_TEXT) + "\"\n\t\t}", end="")
|
||||
else:
|
||||
eloc_xml = ""
|
||||
|
||||
if metrics_logic.eloc:
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse=True):
|
||||
eloc_xml += "{\n\t\t\t\t\"type\": \"estimated-lines-of-code\",\n"
|
||||
eloc_xml += "\t\t\t\t\"file-name\": \"" + i[1] + "\",\n"
|
||||
eloc_xml += "\t\t\t\t\"value\": " + str(i[0]) + "\n"
|
||||
eloc_xml += "\t\t\t},"
|
||||
else:
|
||||
eloc_xml = eloc_xml[:-1]
|
||||
|
||||
if metrics_logic.cyclomatic_complexity:
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity.items()]), reverse=True):
|
||||
eloc_xml += "{\n\t\t\t\t\"type\": \"cyclomatic-complexity\",\n"
|
||||
eloc_xml += "\t\t\t\t\"file-name\": \"" + i[1] + "\",\n"
|
||||
eloc_xml += "\t\t\t\t\"value\": " + str(i[0]) + "\n"
|
||||
eloc_xml += "\t\t\t},"
|
||||
else:
|
||||
eloc_xml = eloc_xml[:-1]
|
||||
|
||||
if metrics_logic.cyclomatic_complexity_density:
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity_density.items()]), reverse=True):
|
||||
eloc_xml += "{\n\t\t\t\t\"type\": \"cyclomatic-complexity-density\",\n"
|
||||
eloc_xml += "\t\t\t\t\"file-name\": \"" + i[1] + "\",\n"
|
||||
eloc_xml += "\t\t\t\t\"value\": {0:.3f} \"\n".format(i[0])
|
||||
eloc_xml += "\t\t\t},"
|
||||
else:
|
||||
eloc_xml = eloc_xml[:-1]
|
||||
|
||||
print(",\n\t\t\"metrics\": {\n\t\t\t\"violations\": [\n\t\t\t" + eloc_xml + "]\n\t\t}", end="")
|
||||
def output_xml(self):
|
||||
metrics_logic = metrics.MetricsLogic()
|
||||
|
||||
|
|
|
@ -23,17 +23,23 @@ from .. import format
|
|||
|
||||
class Outputable(object):
|
||||
def output_html(self):
|
||||
print(_("HTML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
||||
raise NotImplementedError(_("HTML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
||||
|
||||
def output_json(self):
|
||||
raise NotImplementedError(_("JSON output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
||||
|
||||
def output_text(self):
|
||||
print(_("Text output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
||||
raise NotImplementedError(_("Text output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
||||
|
||||
def output_xml(self):
|
||||
print(_("XML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
||||
NotImplementedError
|
||||
raise NotImplementedError(_("XML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
||||
|
||||
def output(outputable):
|
||||
if format.get_selected() == "html" or format.get_selected() == "htmlembedded":
|
||||
outputable.output_html()
|
||||
elif format.get_selected() == "json":
|
||||
outputable.output_json()
|
||||
elif format.get_selected() == "text":
|
||||
outputable.output_text()
|
||||
else:
|
||||
|
|
|
@ -81,6 +81,36 @@ class ResponsibilitiesOutput(Outputable):
|
|||
resp_xml += "</div></div>"
|
||||
print(resp_xml)
|
||||
|
||||
def output_json(self):
|
||||
message_xml = "\t\t\t\"message\": \"" + _(RESPONSIBILITIES_INFO_TEXT) + "\",\n"
|
||||
resp_xml = ""
|
||||
|
||||
for i in sorted(set(i[0] for i in blame.get(self.hard, self.useweeks, self.changes).blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.hard, self.useweeks, i)), reverse=True)
|
||||
if responsibilities:
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
|
||||
resp_xml += "{\n"
|
||||
resp_xml += "\t\t\t\t\"name\": \"" + i + "\",\n"
|
||||
resp_xml += "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
|
||||
resp_xml += "\t\t\t\t\"files\": [\n\t\t\t\t"
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "{\n"
|
||||
resp_xml += "\t\t\t\t\t\"name\": \"" + entry[1] + "\",\n"
|
||||
resp_xml += "\t\t\t\t\t\"rows\": " + str(entry[0]) + "\n"
|
||||
resp_xml += "\t\t\t\t},"
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_xml = resp_xml[:-1]
|
||||
resp_xml += "]\n"
|
||||
resp_xml += "\t\t\t},"
|
||||
|
||||
resp_xml = resp_xml[:-1]
|
||||
print(",\n\t\t\"responsibilities\": {\n" + message_xml + "\t\t\t\"authors\": [\n\t\t\t" + resp_xml + "]\n\t\t}", end="")
|
||||
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
|
||||
resp_xml = ""
|
||||
|
|
|
@ -132,6 +132,45 @@ class TimelineOutput(Outputable):
|
|||
timeline_xml = "</div></div>"
|
||||
print(timeline_xml)
|
||||
|
||||
def output_json(self):
|
||||
if self.changes.get_commits():
|
||||
message_xml = "\t\t\t\"message\": \"" + _(TIMELINE_INFO_TEXT) + "\",\n"
|
||||
timeline_xml = ""
|
||||
periods_xml = "\t\t\t\"period-length\": \"{0}\",\n".format("week" if self.useweeks else "month")
|
||||
periods_xml += "\t\t\t\"periods\": [\n\t\t\t"
|
||||
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
|
||||
for period in periods:
|
||||
name_xml = "\t\t\t\t\"name\": \"" + str(period) + "\",\n"
|
||||
authors_xml = "\t\t\t\t\"authors\": [\n\t\t\t\t"
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_period(period, name[0]):
|
||||
multiplier = timeline_data.get_multiplier(period, 24)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = (signs[1] * "-" + signs[0] * "+")
|
||||
|
||||
if len(signs_str) == 0:
|
||||
signs_str = "."
|
||||
|
||||
authors_xml += "{\n\t\t\t\t\t\"name\": \"" + name[0] + "\",\n"
|
||||
authors_xml += "\t\t\t\t\t\"gravatar\": \"" + gravatar.get_url(name[1]) + "\",\n"
|
||||
authors_xml += "\t\t\t\t\t\"work\": \"" + signs_str + "\"\n\t\t\t\t},"
|
||||
else:
|
||||
authors_xml = authors_xml[:-1]
|
||||
|
||||
authors_xml += "],\n"
|
||||
modified_rows_xml = "\t\t\t\t\"modified_rows\": " + \
|
||||
str(timeline_data.get_total_changes_in_period(period)[2]) + "\n"
|
||||
timeline_xml += "{\n" + name_xml + authors_xml + modified_rows_xml + "\t\t\t},"
|
||||
else:
|
||||
timeline_xml = timeline_xml[:-1]
|
||||
|
||||
print(",\n\t\t\"timeline\": {\n" + message_xml + periods_xml + timeline_xml + "]\n\t\t}", end="")
|
||||
|
||||
def output_xml(self):
|
||||
if self.changes.get_commits():
|
||||
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
|
||||
|
|
Loading…
Reference in New Issue