mirror of
https://github.com/ejwa/gitinspector.git
synced 2024-11-16 00:28:25 +01:00
Refactored all outputable modules.
This prepares the source code for the changes discussed in issue #24. Note that this is just a quick restructuring in order to see the resulting classes and separation. More work will be done to make it more elegant and with less dependencies between modules.
This commit is contained in:
parent
6aa41ade9f
commit
34337dec17
17 changed files with 912 additions and 708 deletions
|
@ -20,21 +20,17 @@
|
|||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from outputable import Outputable
|
||||
from changes import FileDiff
|
||||
import comment
|
||||
import datetime
|
||||
import filtering
|
||||
import format
|
||||
import gravatar
|
||||
import interval
|
||||
import json
|
||||
import multiprocessing
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import terminal
|
||||
import textwrap
|
||||
import threading
|
||||
|
||||
NUM_THREADS = multiprocessing.cpu_count()
|
||||
|
@ -205,107 +201,3 @@ def get(hard, useweeks, changes):
|
|||
__blame__ = Blame(hard, useweeks, changes)
|
||||
|
||||
return __blame__
|
||||
|
||||
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
|
||||
"intact in the current revision")
|
||||
|
||||
class BlameOutput(Outputable):
|
||||
def __init__(self, changes, hard, useweeks):
|
||||
if format.is_interactive_format():
|
||||
print("")
|
||||
|
||||
self.changes = changes
|
||||
self.hard = hard
|
||||
self.useweeks = useweeks
|
||||
get(self.hard, self.useweeks, self.changes)
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_html(self):
|
||||
blame_xml = "<div><div class=\"box\">"
|
||||
blame_xml += "<p>" + _(BLAME_INFO_TEXT) + ".</p><div><table id=\"blame\" class=\"git\">"
|
||||
blame_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th> </tr></thead>".format(
|
||||
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments"))
|
||||
blame_xml += "<tbody>"
|
||||
chart_data = ""
|
||||
blames = sorted(__blame__.get_summed_blames().items())
|
||||
total_blames = 0
|
||||
|
||||
for i in blames:
|
||||
total_blames += i[1].rows
|
||||
|
||||
for i, entry in enumerate(blames):
|
||||
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
|
||||
blame_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(entry[0])
|
||||
blame_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(author_email), entry[0])
|
||||
else:
|
||||
blame_xml += "<td>" + entry[0] + "</td>"
|
||||
|
||||
blame_xml += "<td>" + str(entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + ("{0:.1f}".format(Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "</td>")
|
||||
blame_xml += "<td>" + "{0:.1f}".format(float(entry[1].skew) / entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
|
||||
blame_xml += "<td style=\"display: none\">" + work_percentage + "</td>"
|
||||
blame_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry[0]), work_percentage)
|
||||
|
||||
if blames[-1] != entry:
|
||||
chart_data += ", "
|
||||
|
||||
blame_xml += "<tfoot><tr> <td colspan=\"5\"> </td> </tr></tfoot></tbody></table>"
|
||||
blame_xml += "<div class=\"chart\" id=\"blame_chart\"></div></div>"
|
||||
blame_xml += "<script type=\"text/javascript\">"
|
||||
blame_xml += " blame_plot = $.plot($(\"#blame_chart\"), [{0}], {{".format(chart_data)
|
||||
blame_xml += " series: {"
|
||||
blame_xml += " pie: {"
|
||||
blame_xml += " innerRadius: 0.4,"
|
||||
blame_xml += " show: true,"
|
||||
blame_xml += " combine: {"
|
||||
blame_xml += " threshold: 0.01,"
|
||||
blame_xml += " label: \"" + _("Minor Authors") + "\""
|
||||
blame_xml += " }"
|
||||
blame_xml += " }"
|
||||
blame_xml += " }, grid: {"
|
||||
blame_xml += " hoverable: true"
|
||||
blame_xml += " }"
|
||||
blame_xml += " });"
|
||||
blame_xml += "</script></div></div>"
|
||||
|
||||
print(blame_xml)
|
||||
|
||||
def output_text(self):
|
||||
if sys.stdout.isatty() and format.is_interactive_format():
|
||||
terminal.clear_row()
|
||||
|
||||
print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Rows"), 10) + terminal.rjust(_("Stability"), 15) +
|
||||
terminal.rjust(_("Age"), 13) + terminal.rjust(_("% in comments"), 20))
|
||||
|
||||
for i in sorted(__blame__.get_summed_blames().items()):
|
||||
print(terminal.ljust(i[0], 20)[0:20 - terminal.get_excess_column_count(i[0])], end=" ")
|
||||
print(str(i[1].rows).rjust(10), end=" ")
|
||||
print("{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)).rjust(14), end=" ")
|
||||
print("{0:.1f}".format(float(i[1].skew) / i[1].rows).rjust(12), end=" ")
|
||||
print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
|
||||
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\n"
|
||||
blame_xml = ""
|
||||
|
||||
for i in sorted(__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] + "</name>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
|
||||
stability_xml = ("\t\t\t\t<stability>" + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows,
|
||||
self.changes)) + "</stability>\n")
|
||||
age_xml = ("\t\t\t\t<age>" + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + "</age>\n")
|
||||
percentage_in_comments_xml = ("\t\t\t\t<percentage-in-comments>" + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
|
||||
"</percentage-in-comments>\n")
|
||||
blame_xml += ("\t\t\t<author>\n" + name_xml + gravatar_xml + rows_xml + stability_xml + age_xml +
|
||||
percentage_in_comments_xml + "\t\t\t</author>\n")
|
||||
|
||||
print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")
|
||||
|
|
|
@ -17,22 +17,14 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from outputable import Outputable
|
||||
import datetime
|
||||
import extensions
|
||||
import filtering
|
||||
import format
|
||||
import gravatar
|
||||
import interval
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import subprocess
|
||||
import terminal
|
||||
import textwrap
|
||||
import threading
|
||||
|
||||
CHANGES_PER_THREAD = 200
|
||||
|
@ -269,127 +261,3 @@ def get(hard):
|
|||
|
||||
return __changes__
|
||||
|
||||
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")
|
||||
|
||||
class ChangesOutput(Outputable):
|
||||
def __init__(self, hard):
|
||||
self.changes = get(hard)
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_html(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
changes_xml = "<div><div class=\"box\">"
|
||||
chart_data = ""
|
||||
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
|
||||
if authorinfo_list:
|
||||
changes_xml += "<p>" + _(HISTORICAL_INFO_TEXT) + ".</p><div><table id=\"changes\" class=\"git\">"
|
||||
changes_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th>".format(
|
||||
_("Author"), _("Commits"), _("Insertions"), _("Deletions"), _("% of changes"))
|
||||
changes_xml += "</tr></thead><tbody>"
|
||||
|
||||
for i, entry in enumerate(sorted(authorinfo_list)):
|
||||
authorinfo = authorinfo_list.get(entry)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
|
||||
changes_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
changes_xml += "<td><img src=\"{0}\"/>{1}</td>".format(
|
||||
gravatar.get_url(self.changes.get_latest_email_by_author(entry)), entry)
|
||||
else:
|
||||
changes_xml += "<td>" + entry + "</td>"
|
||||
|
||||
changes_xml += "<td>" + str(authorinfo.commits) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.insertions) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.deletions) + "</td>"
|
||||
changes_xml += "<td>" + "{0:.2f}".format(percentage) + "</td>"
|
||||
changes_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry), "{0:.2f}".format(percentage))
|
||||
|
||||
if sorted(authorinfo_list)[-1] != entry:
|
||||
chart_data += ", "
|
||||
|
||||
changes_xml += ("<tfoot><tr> <td colspan=\"5\"> </td> </tr></tfoot></tbody></table>")
|
||||
changes_xml += "<div class=\"chart\" id=\"changes_chart\"></div></div>"
|
||||
changes_xml += "<script type=\"text/javascript\">"
|
||||
changes_xml += " changes_plot = $.plot($(\"#changes_chart\"), [{0}], {{".format(chart_data)
|
||||
changes_xml += " series: {"
|
||||
changes_xml += " pie: {"
|
||||
changes_xml += " innerRadius: 0.4,"
|
||||
changes_xml += " show: true,"
|
||||
changes_xml += " combine: {"
|
||||
changes_xml += " threshold: 0.01,"
|
||||
changes_xml += " label: \"" + _("Minor Authors") + "\""
|
||||
changes_xml += " }"
|
||||
changes_xml += " }"
|
||||
changes_xml += " }, grid: {"
|
||||
changes_xml += " hoverable: true"
|
||||
changes_xml += " }"
|
||||
changes_xml += " });"
|
||||
changes_xml += "</script>"
|
||||
else:
|
||||
changes_xml += "<p>" + _(NO_COMMITED_FILES_TEXT) + ".</p>"
|
||||
|
||||
changes_xml += "</div></div>"
|
||||
print(changes_xml)
|
||||
|
||||
def output_text(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:
|
||||
print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Commits"), 13) +
|
||||
terminal.rjust(_("Insertions"), 14) + terminal.rjust(_("Deletions"), 15) +
|
||||
terminal.rjust(_("% of changes"), 16))
|
||||
|
||||
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
|
||||
|
||||
print(terminal.ljust(i, 20)[0:20 - terminal.get_excess_column_count(i)], end=" ")
|
||||
print(str(authorinfo.commits).rjust(13), end=" ")
|
||||
print(str(authorinfo.insertions).rjust(13), end=" ")
|
||||
print(str(authorinfo.deletions).rjust(14), end=" ")
|
||||
print("{0:.2f}".format(percentage).rjust(15))
|
||||
else:
|
||||
print(_(NO_COMMITED_FILES_TEXT) + ".")
|
||||
|
||||
def output_xml(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<message>" + _(HISTORICAL_INFO_TEXT) + "</message>\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 + "</name>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(self.changes.get_latest_email_by_author(i)) + "</gravatar>\n"
|
||||
commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
|
||||
insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
|
||||
deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
|
||||
percentage_xml = "\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
|
||||
|
||||
changes_xml += ("\t\t\t<author>\n" + name_xml + gravatar_xml + commits_xml + insertions_xml +
|
||||
deletions_xml + percentage_xml + "\t\t\t</author>\n")
|
||||
|
||||
print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
|
||||
else:
|
||||
print("\t<changes>\n\t\t<exception>" + _(NO_COMMITED_FILES_TEXT) + "</exception>\n\t</changes>")
|
||||
|
|
|
@ -17,12 +17,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from outputable import Outputable
|
||||
import terminal
|
||||
import textwrap
|
||||
|
||||
DEFAULT_EXTENSIONS = ["java", "c", "cc", "cpp", "h", "hh", "hpp", "py", "glsl", "rb", "js", "sql"]
|
||||
|
||||
|
@ -41,56 +36,3 @@ def add_located(string):
|
|||
__located_extensions__.add("*")
|
||||
else:
|
||||
__located_extensions__.add(string)
|
||||
|
||||
EXTENSIONS_INFO_TEXT = N_("The extensions below were found in the repository history")
|
||||
EXTENSIONS_MARKED_TEXT = N_("(extensions used during statistical analysis are marked)")
|
||||
|
||||
class Extensions(Outputable):
|
||||
@staticmethod
|
||||
def is_marked(extension):
|
||||
if extension in __extensions__ or "**" in __extensions__:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def output_html(self):
|
||||
if __located_extensions__:
|
||||
extensions_xml = "<div><div class=\"box\">"
|
||||
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
|
||||
|
||||
for i in sorted(__located_extensions__):
|
||||
if Extensions.is_marked(i):
|
||||
extensions_xml += "<strong>" + i + "</strong>"
|
||||
else:
|
||||
extensions_xml += i
|
||||
extensions_xml += " "
|
||||
|
||||
extensions_xml += "</p></div></div>"
|
||||
print(extensions_xml)
|
||||
|
||||
def output_text(self):
|
||||
if __located_extensions__:
|
||||
print("\n" + textwrap.fill("{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)),
|
||||
width=terminal.get_size()[0]))
|
||||
|
||||
for i in sorted(__located_extensions__):
|
||||
if Extensions.is_marked(i):
|
||||
print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
|
||||
else:
|
||||
print (i, end=" ")
|
||||
print("")
|
||||
|
||||
def output_xml(self):
|
||||
if __located_extensions__:
|
||||
message_xml = "\t\t<message>" + _(EXTENSIONS_INFO_TEXT) + "</message>\n"
|
||||
used_extensions_xml = ""
|
||||
unused_extensions_xml = ""
|
||||
|
||||
for i in sorted(__located_extensions__):
|
||||
if Extensions.is_marked(i):
|
||||
used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
else:
|
||||
unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
|
||||
print("\t<extensions>\n" + message_xml + "\t\t<used>\n" + used_extensions_xml + "\t\t</used>\n" +
|
||||
"\t\t<unused>\n" + unused_extensions_xml + "\t\t</unused>\n" + "\t</extensions>")
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from outputable import Outputable
|
||||
import re
|
||||
import subprocess
|
||||
import terminal
|
||||
|
@ -92,71 +90,3 @@ def set_filtered(string, filter_type="file"):
|
|||
except:
|
||||
raise InvalidRegExpError(_("invalid regular expression specified"))
|
||||
return False
|
||||
|
||||
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 " \
|
||||
"exclusion patterns")
|
||||
FILTERING_COMMIT_INFO_TEXT = N_("The following commit revisions were excluded from the statistics due to the specified " \
|
||||
"exclusion patterns")
|
||||
|
||||
class Filtering(Outputable):
|
||||
@staticmethod
|
||||
def __output_html_section__(info_string, filtered):
|
||||
filtering_xml = ""
|
||||
|
||||
if filtered:
|
||||
filtering_xml += "<p>" + info_string + "."+ "</p>"
|
||||
|
||||
for i in filtered:
|
||||
filtering_xml += "<p>" + i + "</p>"
|
||||
|
||||
return filtering_xml
|
||||
|
||||
def output_html(self):
|
||||
if has_filtered():
|
||||
filtering_xml = "<div><div class=\"box\">"
|
||||
Filtering.__output_html_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
Filtering.__output_html_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
Filtering.__output_html_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
Filtering.__output_html_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
filtering_xml += "</div></div>"
|
||||
|
||||
print(filtering_xml)
|
||||
|
||||
@staticmethod
|
||||
def __output_text_section__(info_string, filtered):
|
||||
if filtered:
|
||||
print("\n" + textwrap.fill(info_string + ":", width=terminal.get_size()[0]))
|
||||
|
||||
for i in filtered:
|
||||
(width, _unused) = terminal.get_size()
|
||||
print("...%s" % i[-width+3:] if len(i) > width else i)
|
||||
|
||||
def output_text(self):
|
||||
Filtering.__output_text_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
Filtering.__output_text_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
Filtering.__output_text_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
Filtering.__output_text_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
|
||||
@staticmethod
|
||||
def __output_xml_section__(info_string, filtered, container_tagname):
|
||||
if filtered:
|
||||
message_xml = "\t\t\t<message>" +info_string + "</message>\n"
|
||||
filtering_xml = ""
|
||||
|
||||
for i in filtered:
|
||||
filtering_xml += "\t\t\t\t<entry>".format(container_tagname) + i + "</entry>\n".format(container_tagname)
|
||||
|
||||
print("\t\t<{0}>".format(container_tagname))
|
||||
print(message_xml + "\t\t\t<entries>\n" + filtering_xml + "\t\t\t</entries>\n")
|
||||
print("\t\t</{0}>".format(container_tagname))
|
||||
|
||||
def output_xml(self):
|
||||
if has_filtered():
|
||||
print("\t<filtering>")
|
||||
Filtering.__output_xml_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
||||
Filtering.__output_xml_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
|
||||
Filtering.__output_xml_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||
Filtering.__output_xml_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
|
||||
print("\t</filtering>")
|
||||
|
|
|
@ -20,10 +20,17 @@
|
|||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import localization
|
||||
localization.init()
|
||||
|
||||
from output import outputable
|
||||
from output.blameoutput import BlameOutput
|
||||
from output.changesoutput import ChangesOutput
|
||||
from output.extensionsoutput import ExtensionsOutput
|
||||
from output.filteringoutput import FilteringOutput
|
||||
from output.metricsoutput import MetricsOutput
|
||||
from output.responsibilitiesoutput import ResponsibilitiesOutput
|
||||
from output.timelineoutput import TimelineOutput
|
||||
import atexit
|
||||
import basedir
|
||||
import blame
|
||||
|
@ -39,7 +46,6 @@ import getopt
|
|||
import metrics
|
||||
import os
|
||||
import optval
|
||||
import outputable
|
||||
import responsibilities
|
||||
import sys
|
||||
import terminal
|
||||
|
@ -72,24 +78,24 @@ class Runner:
|
|||
absolute_path = basedir.get_basedir_git()
|
||||
os.chdir(absolute_path)
|
||||
format.output_header()
|
||||
outputable.output(changes.ChangesOutput(self.hard))
|
||||
outputable.output(ChangesOutput(self.hard))
|
||||
|
||||
if changes.get(self.hard).get_commits():
|
||||
outputable.output(blame.BlameOutput(changes.get(self.hard), self.hard, self.useweeks))
|
||||
outputable.output(BlameOutput(changes.get(self.hard), self.hard, self.useweeks))
|
||||
|
||||
if self.timeline:
|
||||
outputable.output(timeline.Timeline(changes.get(self.hard), self.useweeks))
|
||||
outputable.output(TimelineOutput(changes.get(self.hard), self.useweeks))
|
||||
|
||||
if self.include_metrics:
|
||||
outputable.output(metrics.Metrics())
|
||||
outputable.output(MetricsOutput())
|
||||
|
||||
if self.responsibilities:
|
||||
outputable.output(responsibilities.ResponsibilitiesOutput(self.hard, self.useweeks))
|
||||
outputable.output(ResponsibilitiesOutput(self.hard, self.useweeks))
|
||||
|
||||
outputable.output(filtering.Filtering())
|
||||
outputable.output(FilteringOutput())
|
||||
|
||||
if self.list_file_types:
|
||||
outputable.output(extensions.Extensions())
|
||||
outputable.output(ExtensionsOutput())
|
||||
|
||||
format.output_footer()
|
||||
os.chdir(previous_directory)
|
||||
|
|
|
@ -116,103 +116,3 @@ class MetricsLogic:
|
|||
eloc_counter += 1
|
||||
|
||||
return eloc_counter
|
||||
|
||||
ELOC_INFO_TEXT = N_("The following files are suspiciously big (in order of severity)")
|
||||
CYCLOMATIC_COMPLEXITY_TEXT = N_("The following files have an elevated cyclomatic complexity (in order of severity)")
|
||||
CYCLOMATIC_COMPLEXITY_DENSITY_TEXT = N_("The following files have an elevated cyclomatic complexity density " \
|
||||
"(in order of severity)")
|
||||
METRICS_MISSING_INFO_TEXT = N_("No metrics violations were found in the repository")
|
||||
|
||||
METRICS_VIOLATION_SCORES = [[1.0, "minimal"], [1.25, "minor"], [1.5, "medium"], [2.0, "bad"], [3.0, "severe"]]
|
||||
|
||||
def __get_metrics_score__(ceiling, value):
|
||||
for i in reversed(METRICS_VIOLATION_SCORES):
|
||||
if value > ceiling * i[0]:
|
||||
return i[1]
|
||||
|
||||
class Metrics(Outputable):
|
||||
def output_text(self):
|
||||
metrics_logic = MetricsLogic()
|
||||
|
||||
if not metrics_logic.eloc and not metrics_logic.cyclomatic_complexity and not metrics_logic.cyclomatic_complexity_density:
|
||||
print("\n" + _(METRICS_MISSING_INFO_TEXT) + ".")
|
||||
|
||||
if metrics_logic.eloc:
|
||||
print("\n" + _(ELOC_INFO_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse = True):
|
||||
print(_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])))
|
||||
|
||||
if metrics_logic.cyclomatic_complexity:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity.items()]), reverse = True):
|
||||
print(_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])))
|
||||
|
||||
if metrics_logic.cyclomatic_complexity_density:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity_density.items()]), reverse = True):
|
||||
print(_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]))
|
||||
|
||||
def output_html(self):
|
||||
metrics_logic = MetricsLogic()
|
||||
metrics_xml = "<div><div class=\"box\" id=\"metrics\">"
|
||||
|
||||
if not metrics_logic.eloc and not metrics_logic.cyclomatic_complexity and not metrics_logic.cyclomatic_complexity_density:
|
||||
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
|
||||
|
||||
if metrics_logic.eloc:
|
||||
metrics_xml += "<div><h4>" + _(ELOC_INFO_TEXT) + ".</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse = True)):
|
||||
metrics_xml += "<div class=\"" + __get_metrics_score__(__metric_eloc__[FileDiff.get_extension(i[1])], i[0]) + \
|
||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
||||
_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])) + "</div>"
|
||||
metrics_xml += "</div>"
|
||||
|
||||
if metrics_logic.cyclomatic_complexity:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity.items()]), reverse = True)):
|
||||
metrics_xml += "<div class=\"" + __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, i[0]) + \
|
||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
||||
_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])) + "</div>"
|
||||
metrics_xml += "</div>"
|
||||
|
||||
if metrics_logic.cyclomatic_complexity_density:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity_density.items()]), reverse = True)):
|
||||
metrics_xml += "<div class=\"" + __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD, i[0]) + \
|
||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
||||
_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]) + "</div>"
|
||||
metrics_xml += "</div>"
|
||||
|
||||
metrics_xml += "</div></div>"
|
||||
print(metrics_xml)
|
||||
|
||||
def output_xml(self):
|
||||
metrics_logic = MetricsLogic()
|
||||
|
||||
if not metrics_logic.eloc and not metrics_logic.cyclomatic_complexity and not metrics_logic.cyclomatic_complexity_density:
|
||||
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
|
||||
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 += "\t\t\t<estimated-lines-of-code>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</estimated-lines-of-code>\n"
|
||||
|
||||
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 += "\t\t\t<cyclomatic-complexity>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity>\n"
|
||||
|
||||
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 += "\t\t\t<cyclomatic-complexity-density>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>{0:.3f}</value>\n".format(i[0])
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity-density>\n"
|
||||
|
||||
print("\t<metrics>\n\t\t<violations>\n" + eloc_xml + "\t\t</violations>\n\t</metrics>")
|
||||
|
|
20
gitinspector/output/__init__.py
Normal file
20
gitinspector/output/__init__.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2013 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This file was intentionally left blank.
|
135
gitinspector/output/blameoutput.py
Normal file
135
gitinspector/output/blameoutput.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from .outputable import Outputable
|
||||
import blame
|
||||
import format
|
||||
import gravatar
|
||||
import json
|
||||
import sys
|
||||
import terminal
|
||||
import textwrap
|
||||
|
||||
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
|
||||
"intact in the current revision")
|
||||
|
||||
class BlameOutput(Outputable):
|
||||
def __init__(self, changes, hard, useweeks):
|
||||
if format.is_interactive_format():
|
||||
print("")
|
||||
|
||||
self.changes = changes
|
||||
self.hard = hard
|
||||
self.useweeks = useweeks
|
||||
blame.get(self.hard, self.useweeks, self.changes)
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_html(self):
|
||||
blame_xml = "<div><div class=\"box\">"
|
||||
blame_xml += "<p>" + _(BLAME_INFO_TEXT) + ".</p><div><table id=\"blame\" class=\"git\">"
|
||||
blame_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th> </tr></thead>".format(
|
||||
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments"))
|
||||
blame_xml += "<tbody>"
|
||||
chart_data = ""
|
||||
blames = sorted(blame.__blame__.get_summed_blames().items())
|
||||
total_blames = 0
|
||||
|
||||
for i in blames:
|
||||
total_blames += i[1].rows
|
||||
|
||||
for i, entry in enumerate(blames):
|
||||
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
|
||||
blame_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(entry[0])
|
||||
blame_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(author_email), entry[0])
|
||||
else:
|
||||
blame_xml += "<td>" + entry[0] + "</td>"
|
||||
|
||||
blame_xml += "<td>" + str(entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + ("{0:.1f}".format(blame.Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "</td>")
|
||||
blame_xml += "<td>" + "{0:.1f}".format(float(entry[1].skew) / entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
|
||||
blame_xml += "<td style=\"display: none\">" + work_percentage + "</td>"
|
||||
blame_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry[0]), work_percentage)
|
||||
|
||||
if blames[-1] != entry:
|
||||
chart_data += ", "
|
||||
|
||||
blame_xml += "<tfoot><tr> <td colspan=\"5\"> </td> </tr></tfoot></tbody></table>"
|
||||
blame_xml += "<div class=\"chart\" id=\"blame_chart\"></div></div>"
|
||||
blame_xml += "<script type=\"text/javascript\">"
|
||||
blame_xml += " blame_plot = $.plot($(\"#blame_chart\"), [{0}], {{".format(chart_data)
|
||||
blame_xml += " series: {"
|
||||
blame_xml += " pie: {"
|
||||
blame_xml += " innerRadius: 0.4,"
|
||||
blame_xml += " show: true,"
|
||||
blame_xml += " combine: {"
|
||||
blame_xml += " threshold: 0.01,"
|
||||
blame_xml += " label: \"" + _("Minor Authors") + "\""
|
||||
blame_xml += " }"
|
||||
blame_xml += " }"
|
||||
blame_xml += " }, grid: {"
|
||||
blame_xml += " hoverable: true"
|
||||
blame_xml += " }"
|
||||
blame_xml += " });"
|
||||
blame_xml += "</script></div></div>"
|
||||
|
||||
print(blame_xml)
|
||||
|
||||
def output_text(self):
|
||||
if sys.stdout.isatty() and format.is_interactive_format():
|
||||
terminal.clear_row()
|
||||
|
||||
print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Rows"), 10) + terminal.rjust(_("Stability"), 15) +
|
||||
terminal.rjust(_("Age"), 13) + terminal.rjust(_("% in comments"), 20))
|
||||
|
||||
for i in sorted(blame.__blame__.get_summed_blames().items()):
|
||||
print(terminal.ljust(i[0], 20)[0:20 - terminal.get_excess_column_count(i[0])], end=" ")
|
||||
print(str(i[1].rows).rjust(10), end=" ")
|
||||
print("{0:.1f}".format(blame.Blame.get_stability(i[0], i[1].rows, self.changes)).rjust(14), end=" ")
|
||||
print("{0:.1f}".format(float(i[1].skew) / i[1].rows).rjust(12), end=" ")
|
||||
print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
|
||||
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\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] + "</name>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
|
||||
stability_xml = ("\t\t\t\t<stability>" + "{0:.1f}".format(blame.Blame.get_stability(i[0], i[1].rows,
|
||||
self.changes)) + "</stability>\n")
|
||||
age_xml = ("\t\t\t\t<age>" + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + "</age>\n")
|
||||
percentage_in_comments_xml = ("\t\t\t\t<percentage-in-comments>" + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) +
|
||||
"</percentage-in-comments>\n")
|
||||
blame_xml += ("\t\t\t<author>\n" + name_xml + gravatar_xml + rows_xml + stability_xml + age_xml +
|
||||
percentage_in_comments_xml + "\t\t\t</author>\n")
|
||||
|
||||
print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")
|
155
gitinspector/output/changesoutput.py
Normal file
155
gitinspector/output/changesoutput.py
Normal file
|
@ -0,0 +1,155 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from .outputable import Outputable
|
||||
import changes
|
||||
import format
|
||||
import gravatar
|
||||
import json
|
||||
import terminal
|
||||
import textwrap
|
||||
|
||||
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")
|
||||
|
||||
class ChangesOutput(Outputable):
|
||||
def __init__(self, hard):
|
||||
self.changes = changes.get(hard)
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_html(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
changes_xml = "<div><div class=\"box\">"
|
||||
chart_data = ""
|
||||
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
|
||||
if authorinfo_list:
|
||||
changes_xml += "<p>" + _(HISTORICAL_INFO_TEXT) + ".</p><div><table id=\"changes\" class=\"git\">"
|
||||
changes_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th>".format(
|
||||
_("Author"), _("Commits"), _("Insertions"), _("Deletions"), _("% of changes"))
|
||||
changes_xml += "</tr></thead><tbody>"
|
||||
|
||||
for i, entry in enumerate(sorted(authorinfo_list)):
|
||||
authorinfo = authorinfo_list.get(entry)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
|
||||
changes_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
changes_xml += "<td><img src=\"{0}\"/>{1}</td>".format(
|
||||
gravatar.get_url(self.changes.get_latest_email_by_author(entry)), entry)
|
||||
else:
|
||||
changes_xml += "<td>" + entry + "</td>"
|
||||
|
||||
changes_xml += "<td>" + str(authorinfo.commits) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.insertions) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.deletions) + "</td>"
|
||||
changes_xml += "<td>" + "{0:.2f}".format(percentage) + "</td>"
|
||||
changes_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry), "{0:.2f}".format(percentage))
|
||||
|
||||
if sorted(authorinfo_list)[-1] != entry:
|
||||
chart_data += ", "
|
||||
|
||||
changes_xml += ("<tfoot><tr> <td colspan=\"5\"> </td> </tr></tfoot></tbody></table>")
|
||||
changes_xml += "<div class=\"chart\" id=\"changes_chart\"></div></div>"
|
||||
changes_xml += "<script type=\"text/javascript\">"
|
||||
changes_xml += " changes_plot = $.plot($(\"#changes_chart\"), [{0}], {{".format(chart_data)
|
||||
changes_xml += " series: {"
|
||||
changes_xml += " pie: {"
|
||||
changes_xml += " innerRadius: 0.4,"
|
||||
changes_xml += " show: true,"
|
||||
changes_xml += " combine: {"
|
||||
changes_xml += " threshold: 0.01,"
|
||||
changes_xml += " label: \"" + _("Minor Authors") + "\""
|
||||
changes_xml += " }"
|
||||
changes_xml += " }"
|
||||
changes_xml += " }, grid: {"
|
||||
changes_xml += " hoverable: true"
|
||||
changes_xml += " }"
|
||||
changes_xml += " });"
|
||||
changes_xml += "</script>"
|
||||
else:
|
||||
changes_xml += "<p>" + _(NO_COMMITED_FILES_TEXT) + ".</p>"
|
||||
|
||||
changes_xml += "</div></div>"
|
||||
print(changes_xml)
|
||||
|
||||
def output_text(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:
|
||||
print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Commits"), 13) +
|
||||
terminal.rjust(_("Insertions"), 14) + terminal.rjust(_("Deletions"), 15) +
|
||||
terminal.rjust(_("% of changes"), 16))
|
||||
|
||||
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
|
||||
|
||||
print(terminal.ljust(i, 20)[0:20 - terminal.get_excess_column_count(i)], end=" ")
|
||||
print(str(authorinfo.commits).rjust(13), end=" ")
|
||||
print(str(authorinfo.insertions).rjust(13), end=" ")
|
||||
print(str(authorinfo.deletions).rjust(14), end=" ")
|
||||
print("{0:.2f}".format(percentage).rjust(15))
|
||||
else:
|
||||
print(_(NO_COMMITED_FILES_TEXT) + ".")
|
||||
|
||||
def output_xml(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<message>" + _(HISTORICAL_INFO_TEXT) + "</message>\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 + "</name>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(self.changes.get_latest_email_by_author(i)) + "</gravatar>\n"
|
||||
commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
|
||||
insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
|
||||
deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
|
||||
percentage_xml = "\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
|
||||
|
||||
changes_xml += ("\t\t\t<author>\n" + name_xml + gravatar_xml + commits_xml + insertions_xml +
|
||||
deletions_xml + percentage_xml + "\t\t\t</author>\n")
|
||||
|
||||
print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
|
||||
else:
|
||||
print("\t<changes>\n\t\t<exception>" + _(NO_COMMITED_FILES_TEXT) + "</exception>\n\t</changes>")
|
80
gitinspector/output/extensionsoutput.py
Normal file
80
gitinspector/output/extensionsoutput.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import extensions
|
||||
from localization import N_
|
||||
from .outputable import Outputable
|
||||
import terminal
|
||||
import textwrap
|
||||
|
||||
EXTENSIONS_INFO_TEXT = N_("The extensions below were found in the repository history")
|
||||
EXTENSIONS_MARKED_TEXT = N_("(extensions used during statistical analysis are marked)")
|
||||
|
||||
class ExtensionsOutput(Outputable):
|
||||
@staticmethod
|
||||
def is_marked(extension):
|
||||
if extension in extensions.__extensions__ or "**" in extensions.__extensions__:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def output_html(self):
|
||||
if extensions.__located_extensions__:
|
||||
extensions_xml = "<div><div class=\"box\">"
|
||||
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
extensions_xml += "<strong>" + i + "</strong>"
|
||||
else:
|
||||
extensions_xml += i
|
||||
extensions_xml += " "
|
||||
|
||||
extensions_xml += "</p></div></div>"
|
||||
print(extensions_xml)
|
||||
|
||||
def output_text(self):
|
||||
if extensions.__located_extensions__:
|
||||
print("\n" + textwrap.fill("{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)),
|
||||
width=terminal.get_size()[0]))
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
|
||||
else:
|
||||
print (i, end=" ")
|
||||
print("")
|
||||
|
||||
def output_xml(self):
|
||||
if extensions.__located_extensions__:
|
||||
message_xml = "\t\t<message>" + _(EXTENSIONS_INFO_TEXT) + "</message>\n"
|
||||
used_extensions_xml = ""
|
||||
unused_extensions_xml = ""
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
else:
|
||||
unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
|
||||
print("\t<extensions>\n" + message_xml + "\t\t<used>\n" + used_extensions_xml + "\t\t</used>\n" +
|
||||
"\t\t<unused>\n" + unused_extensions_xml + "\t\t</unused>\n" + "\t</extensions>")
|
96
gitinspector/output/filteringoutput.py
Normal file
96
gitinspector/output/filteringoutput.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from filtering import __filters__
|
||||
from filtering import has_filtered
|
||||
from localization import N_
|
||||
from .outputable import Outputable
|
||||
import terminal
|
||||
import textwrap
|
||||
|
||||
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 " \
|
||||
"exclusion patterns")
|
||||
FILTERING_COMMIT_INFO_TEXT = N_("The following commit revisions were excluded from the statistics due to the specified " \
|
||||
"exclusion patterns")
|
||||
|
||||
class FilteringOutput(Outputable):
|
||||
@staticmethod
|
||||
def __output_html_section__(info_string, filtered):
|
||||
filtering_xml = ""
|
||||
|
||||
if filtered:
|
||||
filtering_xml += "<p>" + info_string + "."+ "</p>"
|
||||
|
||||
for i in filtered:
|
||||
filtering_xml += "<p>" + i + "</p>"
|
||||
|
||||
return filtering_xml
|
||||
|
||||
def output_html(self):
|
||||
if has_filtered():
|
||||
filtering_xml = "<div><div class=\"box\">"
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
filtering_xml += "</div></div>"
|
||||
|
||||
print(filtering_xml)
|
||||
|
||||
@staticmethod
|
||||
def __output_text_section__(info_string, filtered):
|
||||
if filtered:
|
||||
print("\n" + textwrap.fill(info_string + ":", width=terminal.get_size()[0]))
|
||||
|
||||
for i in filtered:
|
||||
(width, _unused) = terminal.get_size()
|
||||
print("...%s" % i[-width+3:] if len(i) > width else i)
|
||||
|
||||
def output_text(self):
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
|
||||
@staticmethod
|
||||
def __output_xml_section__(info_string, filtered, container_tagname):
|
||||
if filtered:
|
||||
message_xml = "\t\t\t<message>" +info_string + "</message>\n"
|
||||
filtering_xml = ""
|
||||
|
||||
for i in filtered:
|
||||
filtering_xml += "\t\t\t\t<entry>".format(container_tagname) + i + "</entry>\n".format(container_tagname)
|
||||
|
||||
print("\t\t<{0}>".format(container_tagname))
|
||||
print(message_xml + "\t\t\t<entries>\n" + filtering_xml + "\t\t\t</entries>\n")
|
||||
print("\t\t</{0}>".format(container_tagname))
|
||||
|
||||
def output_xml(self):
|
||||
if has_filtered():
|
||||
print("\t<filtering>")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
|
||||
print("\t</filtering>")
|
125
gitinspector/output/metricsoutput.py
Normal file
125
gitinspector/output/metricsoutput.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from .outputable import Outputable
|
||||
import metrics
|
||||
|
||||
ELOC_INFO_TEXT = N_("The following files are suspiciously big (in order of severity)")
|
||||
CYCLOMATIC_COMPLEXITY_TEXT = N_("The following files have an elevated cyclomatic complexity (in order of severity)")
|
||||
CYCLOMATIC_COMPLEXITY_DENSITY_TEXT = N_("The following files have an elevated cyclomatic complexity density " \
|
||||
"(in order of severity)")
|
||||
METRICS_MISSING_INFO_TEXT = N_("No metrics violations were found in the repository")
|
||||
|
||||
METRICS_VIOLATION_SCORES = [[1.0, "minimal"], [1.25, "minor"], [1.5, "medium"], [2.0, "bad"], [3.0, "severe"]]
|
||||
|
||||
def __get_metrics_score__(ceiling, value):
|
||||
for i in reversed(METRICS_VIOLATION_SCORES):
|
||||
if value > ceiling * i[0]:
|
||||
return i[1]
|
||||
|
||||
class MetricsOutput(Outputable):
|
||||
def output_text(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" + _(METRICS_MISSING_INFO_TEXT) + ".")
|
||||
|
||||
if metrics_logic.eloc:
|
||||
print("\n" + _(ELOC_INFO_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse=True):
|
||||
print(_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])))
|
||||
|
||||
if metrics_logic.cyclomatic_complexity:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity.items()]), reverse=True):
|
||||
print(_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])))
|
||||
|
||||
if metrics_logic.cyclomatic_complexity_density:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity_density.items()]), reverse=True):
|
||||
print(_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]))
|
||||
|
||||
def output_html(self):
|
||||
metrics_logic = metrics.MetricsLogic()
|
||||
metrics_xml = "<div><div class=\"box\" id=\"metrics\">"
|
||||
|
||||
if not metrics_logic.eloc and not metrics_logic.cyclomatic_complexity and not metrics_logic.cyclomatic_complexity_density:
|
||||
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
|
||||
|
||||
if metrics_logic.eloc:
|
||||
metrics_xml += "<div><h4>" + _(ELOC_INFO_TEXT) + ".</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in metrics_logic.eloc.items()]), reverse=True)):
|
||||
metrics_xml += "<div class=\"" + __get_metrics_score__(__metric_eloc__[FileDiff.get_extension(i[1])], i[0]) + \
|
||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
||||
_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])) + "</div>"
|
||||
metrics_xml += "</div>"
|
||||
|
||||
if metrics_logic.cyclomatic_complexity:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity.items()]), reverse=True)):
|
||||
metrics_xml += "<div class=\"" + __get_metrics_score__(metrics.METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, i[0]) + \
|
||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
||||
_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])) + "</div>"
|
||||
metrics_xml += "</div>"
|
||||
|
||||
if metrics_logic.cyclomatic_complexity_density:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in metrics_logic.cyclomatic_complexity_density.items()]), reverse=True)):
|
||||
metrics_xml += "<div class=\"" + __get_metrics_score__(metrics.METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD, i[0]) + \
|
||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
||||
_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]) + "</div>"
|
||||
metrics_xml += "</div>"
|
||||
|
||||
metrics_xml += "</div></div>"
|
||||
print(metrics_xml)
|
||||
|
||||
def output_xml(self):
|
||||
metrics_logic = metrics.MetricsLogic()
|
||||
|
||||
if not metrics_logic.eloc and not metrics_logic.cyclomatic_complexity and not metrics_logic.cyclomatic_complexity_density:
|
||||
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
|
||||
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 += "\t\t\t<estimated-lines-of-code>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</estimated-lines-of-code>\n"
|
||||
|
||||
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 += "\t\t\t<cyclomatic-complexity>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity>\n"
|
||||
|
||||
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 += "\t\t\t<cyclomatic-complexity-density>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>{0:.3f}</value>\n".format(i[0])
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity-density>\n"
|
||||
|
||||
print("\t<metrics>\n\t\t<violations>\n" + eloc_xml + "\t\t</violations>\n\t</metrics>")
|
115
gitinspector/output/responsibilitiesoutput.py
Normal file
115
gitinspector/output/responsibilitiesoutput.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012-2014 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from .outputable import Outputable
|
||||
import blame
|
||||
import changes
|
||||
import format
|
||||
import gravatar
|
||||
import responsibilities as resp
|
||||
import terminal
|
||||
import textwrap
|
||||
|
||||
RESPONSIBILITIES_INFO_TEXT = N_("The following repsonsibilties, by author, were found in the current "
|
||||
"revision of the repository (comments are exluded from the line count, "
|
||||
"if possible)")
|
||||
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
|
||||
|
||||
class ResponsibilitiesOutput(Outputable):
|
||||
def __init__(self, hard, useweeks):
|
||||
self.hard = hard
|
||||
self.useweeks = useweeks
|
||||
Outputable.__init__(self)
|
||||
self.changes = changes.get(hard)
|
||||
|
||||
def output_text(self):
|
||||
print("\n" + textwrap.fill(_(RESPONSIBILITIES_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
|
||||
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:
|
||||
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
(width, _unused) = terminal.get_size()
|
||||
width -= 7
|
||||
|
||||
print(str(entry[0]).rjust(6), end=" ")
|
||||
print("...%s" % entry[1][-width+3:] if len(entry[1]) > width else entry[1])
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
def output_html(self):
|
||||
resp_xml = "<div><div class=\"box\" id=\"responsibilities\">"
|
||||
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
||||
|
||||
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:
|
||||
resp_xml += "<div>"
|
||||
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
resp_xml += "<h3><img src=\"{0}\"/>{1} {2}</h3>".format(gravatar.get_url(author_email, size=32),
|
||||
i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
||||
else:
|
||||
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "<div" + (" class=\"odd\">" if j % 2 == 1 else ">") + entry[1] + \
|
||||
" (" + str(entry[0]) + " eloc)</div>"
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_xml += "</div>"
|
||||
resp_xml += "</div></div>"
|
||||
print(resp_xml)
|
||||
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\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 += "\t\t\t<author>\n"
|
||||
resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
|
||||
resp_xml += "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
resp_xml += "\t\t\t\t<files>\n"
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "\t\t\t\t\t<file>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
|
||||
resp_xml += "\t\t\t\t\t</file>\n"
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_xml += "\t\t\t\t</files>\n"
|
||||
resp_xml += "\t\t\t</author>\n"
|
||||
|
||||
print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")
|
171
gitinspector/output/timelineoutput.py
Normal file
171
gitinspector/output/timelineoutput.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012-2014 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from .outputable import Outputable
|
||||
import format
|
||||
import gravatar
|
||||
import terminal
|
||||
import textwrap
|
||||
import timeline
|
||||
|
||||
TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from the repository")
|
||||
MODIFIED_ROWS_TEXT = N_("Modified Rows:")
|
||||
|
||||
def __output_row__text__(timeline_data, periods, names):
|
||||
print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
|
||||
|
||||
for period in periods:
|
||||
print(terminal.rjust(period, 10), end=" ")
|
||||
|
||||
print(terminal.__normal__)
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
print(terminal.ljust(name[0], 20)[0:20 - terminal.get_excess_column_count(name[0])], end=" ")
|
||||
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 9)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = (signs[1] * "-" + signs[0] * "+")
|
||||
print (("." if timeline_data.is_author_in_period(period, name[0]) and
|
||||
len(signs_str) == 0 else signs_str).rjust(10), end=" ")
|
||||
print("")
|
||||
|
||||
print(terminal.__bold__ + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
|
||||
|
||||
for period in periods:
|
||||
total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
|
||||
|
||||
if hasattr(total_changes, 'decode'):
|
||||
total_changes = total_changes.decode("utf-8", "replace")
|
||||
|
||||
print(terminal.rjust(total_changes, 10), end=" ")
|
||||
|
||||
print("")
|
||||
|
||||
def __output_row__html__(timeline_data, periods, names):
|
||||
timeline_xml = "<table class=\"git full\"><thead><tr><th>" + _("Author") + "</th>"
|
||||
|
||||
for period in periods:
|
||||
timeline_xml += "<th>" + str(period) + "</th>"
|
||||
|
||||
timeline_xml += "</tr></thead><tbody>"
|
||||
i = 0
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
timeline_xml += "<tr" + (" class=\"odd\">" if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
timeline_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(name[1]), name[0])
|
||||
else:
|
||||
timeline_xml += "<td>" + name[0] + "</td>"
|
||||
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 18)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = (signs[1] * "<div class=\"remove\"> </div>" + signs[0] * "<div class=\"insert\"> </div>")
|
||||
|
||||
timeline_xml += "<td>" + ("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str)
|
||||
timeline_xml += "</td>"
|
||||
timeline_xml += "</tr>"
|
||||
i = i + 1
|
||||
|
||||
timeline_xml += "<tfoot><tr><td><strong>" + _(MODIFIED_ROWS_TEXT) + "</strong></td>"
|
||||
|
||||
for period in periods:
|
||||
total_changes = timeline_data.get_total_changes_in_period(period)
|
||||
timeline_xml += "<td>" + str(total_changes[2]) + "</td>"
|
||||
|
||||
timeline_xml += "</tr></tfoot></tbody></table>"
|
||||
print(timeline_xml)
|
||||
|
||||
class TimelineOutput(Outputable):
|
||||
def __init__(self, changes, useweeks):
|
||||
self.changes = changes
|
||||
self.useweeks = useweeks
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_text(self):
|
||||
if self.changes.get_commits():
|
||||
print("\n" + textwrap.fill(_(TIMELINE_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
(width, _unused) = terminal.get_size()
|
||||
max_periods_per_row = int((width - 21) / 11)
|
||||
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__text__(timeline_data, periods[i:i+max_periods_per_row], names)
|
||||
|
||||
def output_html(self):
|
||||
if self.changes.get_commits():
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
max_periods_per_row = 8
|
||||
|
||||
timeline_xml = "<div><div id=\"timeline\" class=\"box\">"
|
||||
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
|
||||
print(timeline_xml)
|
||||
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__html__(timeline_data, periods[i:i+max_periods_per_row], names)
|
||||
|
||||
timeline_xml = "</div></div>"
|
||||
print(timeline_xml)
|
||||
|
||||
def output_xml(self):
|
||||
if self.changes.get_commits():
|
||||
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
|
||||
timeline_xml = ""
|
||||
periods_xml = "\t\t<periods length=\"{0}\">\n".format("week" if self.useweeks else "month")
|
||||
|
||||
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) + "</name>\n"
|
||||
authors_xml = "\t\t\t\t<authors>\n"
|
||||
|
||||
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 += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name[0] + "</name>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<gravatar>" + gravatar.get_url(name[1]) + "</gravatar>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
|
||||
|
||||
authors_xml += "\t\t\t\t</authors>\n"
|
||||
modified_rows_xml = "\t\t\t\t<modified_rows>" + \
|
||||
str(timeline_data.get_total_changes_in_period(period)[2]) + "</modified_rows>\n"
|
||||
timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + modified_rows_xml + "\t\t\t</period>\n"
|
||||
|
||||
print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from outputable import Outputable
|
||||
import blame
|
||||
import changes
|
||||
import format
|
||||
|
@ -43,87 +41,3 @@ class Responsibilities:
|
|||
author_blames[i[0][1]] = total_rows
|
||||
|
||||
return sorted(author_blames.items())
|
||||
|
||||
RESPONSIBILITIES_INFO_TEXT = N_("The following repsonsibilties, by author, were found in the current "
|
||||
"revision of the repository (comments are exluded from the line count, "
|
||||
"if possible)")
|
||||
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
|
||||
|
||||
class ResponsibilitiesOutput(Outputable):
|
||||
def __init__(self, hard, useweeks):
|
||||
self.hard = hard
|
||||
self.useweeks = useweeks
|
||||
Outputable.__init__(self)
|
||||
self.changes = changes.get(hard)
|
||||
|
||||
def output_text(self):
|
||||
print("\n" + textwrap.fill(_(RESPONSIBILITIES_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
|
||||
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 Responsibilities.get(self.hard, self.useweeks, i)), reverse=True)
|
||||
if responsibilities:
|
||||
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
(width, _unused) = terminal.get_size()
|
||||
width -= 7
|
||||
|
||||
print(str(entry[0]).rjust(6), end=" ")
|
||||
print("...%s" % entry[1][-width+3:] if len(entry[1]) > width else entry[1])
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
def output_html(self):
|
||||
resp_xml = "<div><div class=\"box\" id=\"responsibilities\">"
|
||||
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
||||
|
||||
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 Responsibilities.get(self.hard, self.useweeks, i)), reverse=True)
|
||||
if responsibilities:
|
||||
resp_xml += "<div>"
|
||||
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
resp_xml += "<h3><img src=\"{0}\"/>{1} {2}</h3>".format(gravatar.get_url(author_email, size=32),
|
||||
i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
||||
else:
|
||||
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "<div" + (" class=\"odd\">" if j % 2 == 1 else ">") + entry[1] + \
|
||||
" (" + str(entry[0]) + " eloc)</div>"
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_xml += "</div>"
|
||||
resp_xml += "</div></div>"
|
||||
print(resp_xml)
|
||||
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\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 Responsibilities.get(self.hard, self.useweeks, i)), reverse=True)
|
||||
if responsibilities:
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
|
||||
resp_xml += "\t\t\t<author>\n"
|
||||
resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
|
||||
resp_xml += "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
resp_xml += "\t\t\t\t<files>\n"
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "\t\t\t\t\t<file>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
|
||||
resp_xml += "\t\t\t\t\t</file>\n"
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_xml += "\t\t\t\t</files>\n"
|
||||
resp_xml += "\t\t\t</author>\n"
|
||||
|
||||
print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from localization import N_
|
||||
from outputable import Outputable
|
||||
import datetime
|
||||
import format
|
||||
import gravatar
|
||||
|
@ -105,146 +103,3 @@ class TimelineData:
|
|||
if self.is_author_in_period(period, author):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from the repository")
|
||||
MODIFIED_ROWS_TEXT = N_("Modified Rows:")
|
||||
|
||||
def __output_row__text__(timeline_data, periods, names):
|
||||
print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
|
||||
|
||||
for period in periods:
|
||||
print(terminal.rjust(period, 10), end=" ")
|
||||
|
||||
print(terminal.__normal__)
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
print(terminal.ljust(name[0], 20)[0:20 - terminal.get_excess_column_count(name[0])], end=" ")
|
||||
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 9)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = (signs[1] * "-" + signs[0] * "+")
|
||||
print (("." if timeline_data.is_author_in_period(period, name[0]) and
|
||||
len(signs_str) == 0 else signs_str).rjust(10), end=" ")
|
||||
print("")
|
||||
|
||||
print(terminal.__bold__ + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
|
||||
|
||||
for period in periods:
|
||||
total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
|
||||
|
||||
if hasattr(total_changes, 'decode'):
|
||||
total_changes = total_changes.decode("utf-8", "replace")
|
||||
|
||||
print(terminal.rjust(total_changes, 10), end=" ")
|
||||
|
||||
print("")
|
||||
|
||||
def __output_row__html__(timeline_data, periods, names):
|
||||
timeline_xml = "<table class=\"git full\"><thead><tr><th>" + _("Author") + "</th>"
|
||||
|
||||
for period in periods:
|
||||
timeline_xml += "<th>" + str(period) + "</th>"
|
||||
|
||||
timeline_xml += "</tr></thead><tbody>"
|
||||
i = 0
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
timeline_xml += "<tr" + (" class=\"odd\">" if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
timeline_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(name[1]), name[0])
|
||||
else:
|
||||
timeline_xml += "<td>" + name[0] + "</td>"
|
||||
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 18)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = (signs[1] * "<div class=\"remove\"> </div>" + signs[0] * "<div class=\"insert\"> </div>")
|
||||
|
||||
timeline_xml += "<td>" + ("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str)
|
||||
timeline_xml += "</td>"
|
||||
timeline_xml += "</tr>"
|
||||
i = i + 1
|
||||
|
||||
timeline_xml += "<tfoot><tr><td><strong>" + _(MODIFIED_ROWS_TEXT) + "</strong></td>"
|
||||
|
||||
for period in periods:
|
||||
total_changes = timeline_data.get_total_changes_in_period(period)
|
||||
timeline_xml += "<td>" + str(total_changes[2]) + "</td>"
|
||||
|
||||
timeline_xml += "</tr></tfoot></tbody></table>"
|
||||
print(timeline_xml)
|
||||
|
||||
class Timeline(Outputable):
|
||||
def __init__(self, changes, useweeks):
|
||||
self.changes = changes
|
||||
self.useweeks = useweeks
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_text(self):
|
||||
if self.changes.get_commits():
|
||||
print("\n" + textwrap.fill(_(TIMELINE_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
|
||||
timeline_data = TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
(width, _unused) = terminal.get_size()
|
||||
max_periods_per_row = int((width - 21) / 11)
|
||||
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__text__(timeline_data, periods[i:i+max_periods_per_row], names)
|
||||
|
||||
def output_html(self):
|
||||
if self.changes.get_commits():
|
||||
timeline_data = TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
max_periods_per_row = 8
|
||||
|
||||
timeline_xml = "<div><div id=\"timeline\" class=\"box\">"
|
||||
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
|
||||
print(timeline_xml)
|
||||
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__html__(timeline_data, periods[i:i+max_periods_per_row], names)
|
||||
|
||||
timeline_xml = "</div></div>"
|
||||
print(timeline_xml)
|
||||
|
||||
def output_xml(self):
|
||||
if self.changes.get_commits():
|
||||
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
|
||||
timeline_xml = ""
|
||||
periods_xml = "\t\t<periods length=\"{0}\">\n".format("week" if self.useweeks else "month")
|
||||
|
||||
timeline_data = 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) + "</name>\n"
|
||||
authors_xml = "\t\t\t\t<authors>\n"
|
||||
|
||||
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 += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name[0] + "</name>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<gravatar>" + gravatar.get_url(name[1]) + "</gravatar>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
|
||||
|
||||
authors_xml += "\t\t\t\t</authors>\n"
|
||||
modified_rows_xml = "\t\t\t\t<modified_rows>" + \
|
||||
str(timeline_data.get_total_changes_in_period(period)[2]) + "</modified_rows>\n"
|
||||
timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + modified_rows_xml + "\t\t\t</period>\n"
|
||||
|
||||
print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")
|
||||
|
|
Loading…
Reference in a new issue