mirror of
https://github.com/ejwa/gitinspector.git
synced 2024-10-01 19:41:30 +02:00
Massive Linting and clean up
This commit is contained in:
parent
2dd72c1d96
commit
c17c5795ad
9
.github/workflows/python-package.yml
vendored
9
.github/workflows/python-package.yml
vendored
@ -26,14 +26,11 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
|
make requirements
|
||||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
make test-coverage
|
|
||||||
- name: Lint with flake8
|
- name: Lint with flake8
|
||||||
run: |
|
run: |
|
||||||
# stop the build if there are Python syntax errors or undefined names
|
make lint
|
||||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
||||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
|
||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: |
|
run: |
|
||||||
pytest
|
make test-coverage
|
||||||
|
10
Makefile
10
Makefile
@ -35,9 +35,13 @@ clean-test: ## remove test and coverage artifacts
|
|||||||
rm -fr .pytest_cache
|
rm -fr .pytest_cache
|
||||||
|
|
||||||
lint: ## check style with flake8
|
lint: ## check style with flake8
|
||||||
black gitinspector --line-length 120
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
find . -name '*.py' -exec autopep8 -i {} --max-line-length=120 \;
|
flake8 gitinspector tests --count --select=E9,F63,F7,F82 --show-source --statistics --builtins="_"
|
||||||
flake8 gitinspector tests --max-line-length=120
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
|
flake8 gitinspector tests --count --ignore=E722,W503,E401,C901 --exit-zero --max-complexity=10 --max-line-length=127 --statistics --builtins="_"
|
||||||
|
|
||||||
|
format: ## auto format all the code with black
|
||||||
|
black gitinspector --line-length 127
|
||||||
|
|
||||||
test: ## run tests quickly with the default Python
|
test: ## run tests quickly with the default Python
|
||||||
pytest
|
pytest
|
||||||
|
@ -21,43 +21,45 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def get_basedir():
|
def get_basedir():
|
||||||
if hasattr(sys, "frozen"): # exists when running via py2exe
|
if hasattr(sys, "frozen"): # exists when running via py2exe
|
||||||
return sys.prefix
|
return sys.prefix
|
||||||
else:
|
else:
|
||||||
return os.path.dirname(os.path.realpath(__file__))
|
return os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
def get_basedir_git(path=None):
|
def get_basedir_git(path=None):
|
||||||
previous_directory = None
|
previous_directory = None
|
||||||
|
|
||||||
if path != None:
|
if path is not None:
|
||||||
previous_directory = os.getcwd()
|
previous_directory = os.getcwd()
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
|
|
||||||
bare_command = subprocess.Popen(["git", "rev-parse", "--is-bare-repository"],
|
bare_command = subprocess.Popen(
|
||||||
stdout=subprocess.PIPE, stderr=open(os.devnull, "w"))
|
["git", "rev-parse", "--is-bare-repository"], stdout=subprocess.PIPE, stderr=open(os.devnull, "w")
|
||||||
|
)
|
||||||
|
|
||||||
isbare = bare_command.stdout.readlines()
|
isbare = bare_command.stdout.readlines()
|
||||||
bare_command.wait()
|
bare_command.wait()
|
||||||
|
|
||||||
if bare_command.returncode != 0:
|
if bare_command.returncode != 0:
|
||||||
sys.exit(_("Error processing git repository at \"%s\"." % os.getcwd()))
|
sys.exit(_('Error processing git repository at "%s".' % os.getcwd()))
|
||||||
|
|
||||||
isbare = (isbare[0].decode("utf-8", "replace").strip() == "true")
|
isbare = isbare[0].decode("utf-8", "replace").strip() == "true"
|
||||||
absolute_path = None
|
absolute_path = None
|
||||||
|
|
||||||
if isbare:
|
if isbare:
|
||||||
absolute_path = subprocess.Popen(["git", "rev-parse", "--git-dir"], stdout=subprocess.PIPE).stdout
|
absolute_path = subprocess.Popen(["git", "rev-parse", "--git-dir"], stdout=subprocess.PIPE).stdout
|
||||||
else:
|
else:
|
||||||
absolute_path = subprocess.Popen(["git", "rev-parse", "--show-toplevel"],
|
absolute_path = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE).stdout
|
||||||
stdout=subprocess.PIPE).stdout
|
|
||||||
|
|
||||||
absolute_path = absolute_path.readlines()
|
absolute_path = absolute_path.readlines()
|
||||||
|
|
||||||
if len(absolute_path) == 0:
|
if len(absolute_path) == 0:
|
||||||
sys.exit(_("Unable to determine absolute path of git repository."))
|
sys.exit(_("Unable to determine absolute path of git repository."))
|
||||||
|
|
||||||
if path != None:
|
if path is not None:
|
||||||
os.chdir(previous_directory)
|
os.chdir(previous_directory)
|
||||||
|
|
||||||
return absolute_path[0].decode("utf-8", "replace").strip()
|
return absolute_path[0].decode("utf-8", "replace").strip()
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import re
|
import re
|
||||||
@ -30,16 +29,19 @@ from . import comment, extensions, filtering, format, interval, terminal
|
|||||||
|
|
||||||
NUM_THREADS = multiprocessing.cpu_count()
|
NUM_THREADS = multiprocessing.cpu_count()
|
||||||
|
|
||||||
|
|
||||||
class BlameEntry(object):
|
class BlameEntry(object):
|
||||||
rows = 0
|
rows = 0
|
||||||
skew = 0 # Used when calculating average code age.
|
skew = 0 # Used when calculating average code age.
|
||||||
comments = 0
|
comments = 0
|
||||||
|
|
||||||
|
|
||||||
__thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
|
__thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
|
||||||
__blame_lock__ = threading.Lock()
|
__blame_lock__ = threading.Lock()
|
||||||
|
|
||||||
AVG_DAYS_PER_MONTH = 30.4167
|
AVG_DAYS_PER_MONTH = 30.4167
|
||||||
|
|
||||||
|
|
||||||
class BlameThread(threading.Thread):
|
class BlameThread(threading.Thread):
|
||||||
def __init__(self, useweeks, changes, blame_command, extension, blames, filename):
|
def __init__(self, useweeks, changes, blame_command, extension, blames, filename):
|
||||||
__thread_lock__.acquire() # Lock controlling the number of threads running
|
__thread_lock__.acquire() # Lock controlling the number of threads running
|
||||||
@ -72,21 +74,24 @@ class BlameThread(threading.Thread):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not filtering.set_filtered(author, "author") and not \
|
if (
|
||||||
filtering.set_filtered(self.blamechunk_email, "email") and not \
|
not filtering.set_filtered(author, "author")
|
||||||
filtering.set_filtered(self.blamechunk_revision, "revision"):
|
and not filtering.set_filtered(self.blamechunk_email, "email")
|
||||||
|
and not filtering.set_filtered(self.blamechunk_revision, "revision")
|
||||||
|
):
|
||||||
|
|
||||||
__blame_lock__.acquire() # Global lock used to protect calls from here...
|
__blame_lock__.acquire() # Global lock used to protect calls from here...
|
||||||
|
|
||||||
if self.blames.get((author, self.filename), None) == None:
|
if self.blames.get((author, self.filename), None) is None:
|
||||||
self.blames[(author, self.filename)] = BlameEntry()
|
self.blames[(author, self.filename)] = BlameEntry()
|
||||||
|
|
||||||
self.blames[(author, self.filename)].comments += comments
|
self.blames[(author, self.filename)].comments += comments
|
||||||
self.blames[(author, self.filename)].rows += 1
|
self.blames[(author, self.filename)].rows += 1
|
||||||
|
|
||||||
if (self.blamechunk_time - self.changes.first_commit_date).days > 0:
|
if (self.blamechunk_time - self.changes.first_commit_date).days > 0:
|
||||||
self.blames[(author, self.filename)].skew += ((self.changes.last_commit_date - self.blamechunk_time).days /
|
self.blames[(author, self.filename)].skew += (self.changes.last_commit_date - self.blamechunk_time).days / (
|
||||||
(7.0 if self.useweeks else AVG_DAYS_PER_MONTH))
|
7.0 if self.useweeks else AVG_DAYS_PER_MONTH
|
||||||
|
)
|
||||||
|
|
||||||
__blame_lock__.release() # ...to here.
|
__blame_lock__.release() # ...to here.
|
||||||
|
|
||||||
@ -118,34 +123,45 @@ class BlameThread(threading.Thread):
|
|||||||
|
|
||||||
__thread_lock__.release() # Lock controlling the number of threads running
|
__thread_lock__.release() # Lock controlling the number of threads running
|
||||||
|
|
||||||
|
|
||||||
PROGRESS_TEXT = N_("Checking how many rows belong to each author (2 of 2): {0:.0f}%")
|
PROGRESS_TEXT = N_("Checking how many rows belong to each author (2 of 2): {0:.0f}%")
|
||||||
|
|
||||||
|
|
||||||
class Blame(object):
|
class Blame(object):
|
||||||
def __init__(self, repo, hard, useweeks, changes):
|
def __init__(self, repo, hard, useweeks, changes):
|
||||||
self.blames = {}
|
self.blames = {}
|
||||||
ls_tree_p = subprocess.Popen(["git", "ls-tree", "--name-only", "-r", interval.get_ref()],
|
ls_tree_p = subprocess.Popen(
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
["git", "ls-tree", "--name-only", "-r", interval.get_ref()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||||
|
)
|
||||||
lines = ls_tree_p.communicate()[0].splitlines()
|
lines = ls_tree_p.communicate()[0].splitlines()
|
||||||
ls_tree_p.stdout.close()
|
ls_tree_p.stdout.close()
|
||||||
|
|
||||||
if ls_tree_p.returncode == 0:
|
if ls_tree_p.returncode == 0:
|
||||||
progress_text = _(PROGRESS_TEXT)
|
progress_text = _(PROGRESS_TEXT)
|
||||||
|
|
||||||
if repo != None:
|
if repo is not None:
|
||||||
progress_text = "[%s] " % repo.name + progress_text
|
progress_text = "[%s] " % repo.name + progress_text
|
||||||
|
|
||||||
for i, row in enumerate(lines):
|
for i, row in enumerate(lines):
|
||||||
row = row.strip().decode("unicode_escape", "ignore")
|
row = row.strip().decode("unicode_escape", "ignore")
|
||||||
row = row.encode("latin-1", "replace")
|
row = row.encode("latin-1", "replace")
|
||||||
row = row.decode("utf-8", "replace").strip("\"").strip("'").strip()
|
row = row.decode("utf-8", "replace").strip('"').strip("'").strip()
|
||||||
|
|
||||||
if FileDiff.get_extension(row) in extensions.get_located() and \
|
if (
|
||||||
FileDiff.is_valid_extension(row) and not filtering.set_filtered(FileDiff.get_filename(row)):
|
FileDiff.get_extension(row) in extensions.get_located()
|
||||||
blame_command = [_f for _f in ["git", "blame", "--line-porcelain", "-w"] + \
|
and FileDiff.is_valid_extension(row)
|
||||||
(["-C", "-C", "-M"] if hard else []) +
|
and not filtering.set_filtered(FileDiff.get_filename(row))
|
||||||
[interval.get_since(), interval.get_ref(), "--", row] if _f]
|
):
|
||||||
thread = BlameThread(useweeks, changes, blame_command, FileDiff.get_extension(row),
|
blame_command = [
|
||||||
self.blames, row.strip())
|
_f
|
||||||
|
for _f in ["git", "blame", "--line-porcelain", "-w"]
|
||||||
|
+ (["-C", "-C", "-M"] if hard else [])
|
||||||
|
+ [interval.get_since(), interval.get_ref(), "--", row]
|
||||||
|
if _f
|
||||||
|
]
|
||||||
|
thread = BlameThread(
|
||||||
|
useweeks, changes, blame_command, FileDiff.get_extension(row), self.blames, row.strip()
|
||||||
|
)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
@ -163,15 +179,15 @@ class Blame(object):
|
|||||||
def __iadd__(self, other):
|
def __iadd__(self, other):
|
||||||
try:
|
try:
|
||||||
self.blames.update(other.blames)
|
self.blames.update(other.blames)
|
||||||
return self;
|
return self
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return other;
|
return other
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_revision(string):
|
def is_revision(string):
|
||||||
revision = re.search("([0-9a-f]{40})", string)
|
revision = re.search("([0-9a-f]{40})", string)
|
||||||
|
|
||||||
if revision == None:
|
if revision is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return revision.group(1).strip()
|
return revision.group(1).strip()
|
||||||
@ -191,7 +207,7 @@ class Blame(object):
|
|||||||
def get_summed_blames(self):
|
def get_summed_blames(self):
|
||||||
summed_blames = {}
|
summed_blames = {}
|
||||||
for i in list(self.blames.items()):
|
for i in list(self.blames.items()):
|
||||||
if summed_blames.get(i[0][0], None) == None:
|
if summed_blames.get(i[0][0], None) is None:
|
||||||
summed_blames[i[0][0]] = BlameEntry()
|
summed_blames[i[0][0]] = BlameEntry()
|
||||||
|
|
||||||
summed_blames[i[0][0]].rows += i[1].rows
|
summed_blames[i[0][0]].rows += i[1].rows
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import bisect
|
import bisect
|
||||||
import datetime
|
import datetime
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
@ -34,6 +33,7 @@ NUM_THREADS = multiprocessing.cpu_count()
|
|||||||
__thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
|
__thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
|
||||||
__changes_lock__ = threading.Lock()
|
__changes_lock__ = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
class FileDiff(object):
|
class FileDiff(object):
|
||||||
def __init__(self, string):
|
def __init__(self, string):
|
||||||
commit_line = string.split("|")
|
commit_line = string.split("|")
|
||||||
@ -46,26 +46,27 @@ class FileDiff(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def is_filediff_line(string):
|
def is_filediff_line(string):
|
||||||
string = string.split("|")
|
string = string.split("|")
|
||||||
return string.__len__() == 2 and string[1].find("Bin") == -1 and ('+' in string[1] or '-' in string[1])
|
return string.__len__() == 2 and string[1].find("Bin") == -1 and ("+" in string[1] or "-" in string[1])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_extension(string):
|
def get_extension(string):
|
||||||
string = string.split("|")[0].strip().strip("{}").strip("\"").strip("'")
|
string = string.split("|")[0].strip().strip("{}").strip('"').strip("'")
|
||||||
return os.path.splitext(string)[1][1:]
|
return os.path.splitext(string)[1][1:]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_filename(string):
|
def get_filename(string):
|
||||||
return string.split("|")[0].strip().strip("{}").strip("\"").strip("'")
|
return string.split("|")[0].strip().strip("{}").strip('"').strip("'")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_valid_extension(string):
|
def is_valid_extension(string):
|
||||||
extension = FileDiff.get_extension(string)
|
extension = FileDiff.get_extension(string)
|
||||||
|
|
||||||
for i in extensions.get():
|
for i in extensions.get():
|
||||||
if (extension == "" and i == "*") or extension == i or i == '**':
|
if (extension == "" and i == "*") or extension == i or i == "**":
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Commit(object):
|
class Commit(object):
|
||||||
def __init__(self, string):
|
def __init__(self, string):
|
||||||
self.filediffs = []
|
self.filediffs = []
|
||||||
@ -98,12 +99,14 @@ class Commit(object):
|
|||||||
def is_commit_line(string):
|
def is_commit_line(string):
|
||||||
return string.split("|").__len__() == 5
|
return string.split("|").__len__() == 5
|
||||||
|
|
||||||
|
|
||||||
class AuthorInfo(object):
|
class AuthorInfo(object):
|
||||||
email = None
|
email = None
|
||||||
insertions = 0
|
insertions = 0
|
||||||
deletions = 0
|
deletions = 0
|
||||||
commits = 0
|
commits = 0
|
||||||
|
|
||||||
|
|
||||||
class ChangesThread(threading.Thread):
|
class ChangesThread(threading.Thread):
|
||||||
def __init__(self, hard, changes, first_hash, second_hash, offset):
|
def __init__(self, hard, changes, first_hash, second_hash, offset):
|
||||||
__thread_lock__.acquire() # Lock controlling the number of threads running
|
__thread_lock__.acquire() # Lock controlling the number of threads running
|
||||||
@ -122,10 +125,27 @@ class ChangesThread(threading.Thread):
|
|||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
git_log_r = subprocess.Popen([_f for _f in ["git", "log", "--reverse", "--pretty=%ct|%cd|%H|%aN|%aE",
|
git_log_r = subprocess.Popen(
|
||||||
"--stat=100000,8192", "--no-merges", "-w", interval.get_since(),
|
[
|
||||||
interval.get_until(), "--date=short"] + (["-C", "-C", "-M"] if self.hard else []) +
|
_f
|
||||||
[self.first_hash + self.second_hash] if _f], stdout=subprocess.PIPE).stdout
|
for _f in [
|
||||||
|
"git",
|
||||||
|
"log",
|
||||||
|
"--reverse",
|
||||||
|
"--pretty=%ct|%cd|%H|%aN|%aE",
|
||||||
|
"--stat=100000,8192",
|
||||||
|
"--no-merges",
|
||||||
|
"-w",
|
||||||
|
interval.get_since(),
|
||||||
|
interval.get_until(),
|
||||||
|
"--date=short",
|
||||||
|
]
|
||||||
|
+ (["-C", "-C", "-M"] if self.hard else [])
|
||||||
|
+ [self.first_hash + self.second_hash]
|
||||||
|
if _f
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
).stdout
|
||||||
lines = git_log_r.readlines()
|
lines = git_log_r.readlines()
|
||||||
git_log_r.close()
|
git_log_r.close()
|
||||||
|
|
||||||
@ -154,15 +174,15 @@ class ChangesThread(threading.Thread):
|
|||||||
is_filtered = False
|
is_filtered = False
|
||||||
commit = Commit(j)
|
commit = Commit(j)
|
||||||
|
|
||||||
if Commit.is_commit_line(j) and \
|
if Commit.is_commit_line(j) and (
|
||||||
(filtering.set_filtered(commit.author, "author") or \
|
filtering.set_filtered(commit.author, "author")
|
||||||
filtering.set_filtered(commit.email, "email") or \
|
or filtering.set_filtered(commit.email, "email")
|
||||||
filtering.set_filtered(commit.sha, "revision") or \
|
or filtering.set_filtered(commit.sha, "revision")
|
||||||
filtering.set_filtered(commit.sha, "message")):
|
or filtering.set_filtered(commit.sha, "message")
|
||||||
|
):
|
||||||
is_filtered = True
|
is_filtered = True
|
||||||
|
|
||||||
if FileDiff.is_filediff_line(j) and not \
|
if FileDiff.is_filediff_line(j) and not filtering.set_filtered(FileDiff.get_filename(j)) and not is_filtered:
|
||||||
filtering.set_filtered(FileDiff.get_filename(j)) and not is_filtered:
|
|
||||||
extensions.add_located(FileDiff.get_extension(j))
|
extensions.add_located(FileDiff.get_extension(j))
|
||||||
|
|
||||||
if FileDiff.is_valid_extension(j):
|
if FileDiff.is_valid_extension(j):
|
||||||
@ -174,8 +194,10 @@ class ChangesThread(threading.Thread):
|
|||||||
__changes_lock__.release() # ...to here.
|
__changes_lock__.release() # ...to here.
|
||||||
__thread_lock__.release() # Lock controlling the number of threads running
|
__thread_lock__.release() # Lock controlling the number of threads running
|
||||||
|
|
||||||
|
|
||||||
PROGRESS_TEXT = N_("Fetching and calculating primary statistics (1 of 2): {0:.0f}%")
|
PROGRESS_TEXT = N_("Fetching and calculating primary statistics (1 of 2): {0:.0f}%")
|
||||||
|
|
||||||
|
|
||||||
class Changes(object):
|
class Changes(object):
|
||||||
authors = {}
|
authors = {}
|
||||||
authors_dateinfo = {}
|
authors_dateinfo = {}
|
||||||
@ -184,16 +206,22 @@ class Changes(object):
|
|||||||
|
|
||||||
def __init__(self, repo, hard):
|
def __init__(self, repo, hard):
|
||||||
self.commits = []
|
self.commits = []
|
||||||
interval.set_ref("HEAD");
|
interval.set_ref("HEAD")
|
||||||
git_rev_list_p = subprocess.Popen([_f for _f in ["git", "rev-list", "--reverse", "--no-merges",
|
git_rev_list_p = subprocess.Popen(
|
||||||
interval.get_since(), interval.get_until(), "HEAD"] if _f],
|
[
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
_f
|
||||||
|
for _f in ["git", "rev-list", "--reverse", "--no-merges", interval.get_since(), interval.get_until(), "HEAD"]
|
||||||
|
if _f
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
)
|
||||||
lines = git_rev_list_p.communicate()[0].splitlines()
|
lines = git_rev_list_p.communicate()[0].splitlines()
|
||||||
git_rev_list_p.stdout.close()
|
git_rev_list_p.stdout.close()
|
||||||
|
|
||||||
if git_rev_list_p.returncode == 0 and len(lines) > 0:
|
if git_rev_list_p.returncode == 0 and len(lines) > 0:
|
||||||
progress_text = _(PROGRESS_TEXT)
|
progress_text = _(PROGRESS_TEXT)
|
||||||
if repo != None:
|
if repo is not None:
|
||||||
progress_text = "[%s] " % repo.name + progress_text
|
progress_text = "[%s] " % repo.name + progress_text
|
||||||
|
|
||||||
chunks = len(lines) // CHANGES_PER_THREAD
|
chunks = len(lines) // CHANGES_PER_THREAD
|
||||||
@ -229,10 +257,12 @@ class Changes(object):
|
|||||||
if interval.has_interval():
|
if interval.has_interval():
|
||||||
interval.set_ref(self.commits[-1].sha)
|
interval.set_ref(self.commits[-1].sha)
|
||||||
|
|
||||||
self.first_commit_date = datetime.date(int(self.commits[0].date[0:4]), int(self.commits[0].date[5:7]),
|
self.first_commit_date = datetime.date(
|
||||||
int(self.commits[0].date[8:10]))
|
int(self.commits[0].date[0:4]), int(self.commits[0].date[5:7]), int(self.commits[0].date[8:10])
|
||||||
self.last_commit_date = datetime.date(int(self.commits[-1].date[0:4]), int(self.commits[-1].date[5:7]),
|
)
|
||||||
int(self.commits[-1].date[8:10]))
|
self.last_commit_date = datetime.date(
|
||||||
|
int(self.commits[-1].date[0:4]), int(self.commits[-1].date[5:7]), int(self.commits[-1].date[8:10])
|
||||||
|
)
|
||||||
|
|
||||||
def __iadd__(self, other):
|
def __iadd__(self, other):
|
||||||
try:
|
try:
|
||||||
@ -255,7 +285,7 @@ class Changes(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def modify_authorinfo(authors, key, commit):
|
def modify_authorinfo(authors, key, commit):
|
||||||
if authors.get(key, None) == None:
|
if authors.get(key, None) is None:
|
||||||
authors[key] = AuthorInfo()
|
authors[key] = AuthorInfo()
|
||||||
|
|
||||||
if commit.get_filediffs():
|
if commit.get_filediffs():
|
||||||
|
@ -31,6 +31,7 @@ except:
|
|||||||
|
|
||||||
__cloned_paths__ = []
|
__cloned_paths__ = []
|
||||||
|
|
||||||
|
|
||||||
def create(url):
|
def create(url):
|
||||||
class Repository(object):
|
class Repository(object):
|
||||||
def __init__(self, name, location):
|
def __init__(self, name, location):
|
||||||
@ -39,8 +40,13 @@ def create(url):
|
|||||||
|
|
||||||
parsed_url = urlparse(url)
|
parsed_url = urlparse(url)
|
||||||
|
|
||||||
if parsed_url.scheme == "file" or parsed_url.scheme == "git" or parsed_url.scheme == "http" or \
|
if (
|
||||||
parsed_url.scheme == "https" or parsed_url.scheme == "ssh":
|
parsed_url.scheme == "file"
|
||||||
|
or parsed_url.scheme == "git"
|
||||||
|
or parsed_url.scheme == "http"
|
||||||
|
or parsed_url.scheme == "https"
|
||||||
|
or parsed_url.scheme == "ssh"
|
||||||
|
):
|
||||||
path = tempfile.mkdtemp(suffix=".gitinspector")
|
path = tempfile.mkdtemp(suffix=".gitinspector")
|
||||||
git_clone = subprocess.Popen(["git", "clone", url, path], stdout=sys.stderr)
|
git_clone = subprocess.Popen(["git", "clone", url, path], stdout=sys.stderr)
|
||||||
git_clone.wait()
|
git_clone.wait()
|
||||||
@ -53,6 +59,7 @@ def create(url):
|
|||||||
|
|
||||||
return Repository(None, os.path.abspath(url))
|
return Repository(None, os.path.abspath(url))
|
||||||
|
|
||||||
|
|
||||||
def delete():
|
def delete():
|
||||||
for path in __cloned_paths__:
|
for path in __cloned_paths__:
|
||||||
shutil.rmtree(path, ignore_errors=True)
|
shutil.rmtree(path, ignore_errors=True)
|
||||||
|
@ -18,50 +18,128 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
__comment_begining__ = {
|
||||||
|
"java": "/*",
|
||||||
|
"c": "/*",
|
||||||
|
"cc": "/*",
|
||||||
|
"cpp": "/*",
|
||||||
|
"cs": "/*",
|
||||||
|
"h": "/*",
|
||||||
|
"hh": "/*",
|
||||||
|
"hpp": "/*",
|
||||||
|
"hs": "{-",
|
||||||
|
"html": "<!--",
|
||||||
|
"php": "/*",
|
||||||
|
"py": '"""',
|
||||||
|
"glsl": "/*",
|
||||||
|
"rb": "=begin",
|
||||||
|
"js": "/*",
|
||||||
|
"jspx": "<!--",
|
||||||
|
"scala": "/*",
|
||||||
|
"sql": "/*",
|
||||||
|
"tex": "\\begin{comment}",
|
||||||
|
"xhtml": "<!--",
|
||||||
|
"xml": "<!--",
|
||||||
|
"ml": "(*",
|
||||||
|
"mli": "(*",
|
||||||
|
"go": "/*",
|
||||||
|
"ly": "%{",
|
||||||
|
"ily": "%{",
|
||||||
|
}
|
||||||
|
|
||||||
__comment_begining__ = {"java": "/*", "c": "/*", "cc": "/*", "cpp": "/*", "cs": "/*", "h": "/*", "hh": "/*", "hpp": "/*",
|
__comment_end__ = {
|
||||||
"hs": "{-", "html": "<!--", "php": "/*", "py": "\"\"\"", "glsl": "/*", "rb": "=begin", "js": "/*",
|
"java": "*/",
|
||||||
"jspx": "<!--", "scala": "/*", "sql": "/*", "tex": "\\begin{comment}", "xhtml": "<!--",
|
"c": "*/",
|
||||||
"xml": "<!--", "ml": "(*", "mli": "(*", "go": "/*", "ly": "%{", "ily": "%{"}
|
"cc": "*/",
|
||||||
|
"cpp": "*/",
|
||||||
|
"cs": "*/",
|
||||||
|
"h": "*/",
|
||||||
|
"hh": "*/",
|
||||||
|
"hpp": "*/",
|
||||||
|
"hs": "-}",
|
||||||
|
"html": "-->",
|
||||||
|
"php": "*/",
|
||||||
|
"py": '"""',
|
||||||
|
"glsl": "*/",
|
||||||
|
"rb": "=end",
|
||||||
|
"js": "*/",
|
||||||
|
"jspx": "-->",
|
||||||
|
"scala": "*/",
|
||||||
|
"sql": "*/",
|
||||||
|
"tex": "\\end{comment}",
|
||||||
|
"xhtml": "-->",
|
||||||
|
"xml": "-->",
|
||||||
|
"ml": "*)",
|
||||||
|
"mli": "*)",
|
||||||
|
"go": "*/",
|
||||||
|
"ly": "%}",
|
||||||
|
"ily": "%}",
|
||||||
|
}
|
||||||
|
|
||||||
__comment_end__ = {"java": "*/", "c": "*/", "cc": "*/", "cpp": "*/", "cs": "*/", "h": "*/", "hh": "*/", "hpp": "*/", "hs": "-}",
|
__comment__ = {
|
||||||
"html": "-->", "php": "*/", "py": "\"\"\"", "glsl": "*/", "rb": "=end", "js": "*/", "jspx": "-->",
|
"java": "//",
|
||||||
"scala": "*/", "sql": "*/", "tex": "\\end{comment}", "xhtml": "-->", "xml": "-->", "ml": "*)", "mli": "*)",
|
"c": "//",
|
||||||
"go": "*/", "ly": "%}", "ily": "%}"}
|
"cc": "//",
|
||||||
|
"cpp": "//",
|
||||||
__comment__ = {"java": "//", "c": "//", "cc": "//", "cpp": "//", "cs": "//", "h": "//", "hh": "//", "hpp": "//", "hs": "--",
|
"cs": "//",
|
||||||
"pl": "#", "php": "//", "py": "#", "glsl": "//", "rb": "#", "robot": "#", "rs": "//", "rlib": "//", "js": "//",
|
"h": "//",
|
||||||
"scala": "//", "sql": "--", "tex": "%", "ada": "--", "ads": "--", "adb": "--", "pot": "#", "po": "#", "go": "//",
|
"hh": "//",
|
||||||
"ly": "%", "ily": "%"}
|
"hpp": "//",
|
||||||
|
"hs": "--",
|
||||||
|
"pl": "#",
|
||||||
|
"php": "//",
|
||||||
|
"py": "#",
|
||||||
|
"glsl": "//",
|
||||||
|
"rb": "#",
|
||||||
|
"robot": "#",
|
||||||
|
"rs": "//",
|
||||||
|
"rlib": "//",
|
||||||
|
"js": "//",
|
||||||
|
"scala": "//",
|
||||||
|
"sql": "--",
|
||||||
|
"tex": "%",
|
||||||
|
"ada": "--",
|
||||||
|
"ads": "--",
|
||||||
|
"adb": "--",
|
||||||
|
"pot": "#",
|
||||||
|
"po": "#",
|
||||||
|
"go": "//",
|
||||||
|
"ly": "%",
|
||||||
|
"ily": "%",
|
||||||
|
}
|
||||||
|
|
||||||
__comment_markers_must_be_at_begining__ = {"tex": True}
|
__comment_markers_must_be_at_begining__ = {"tex": True}
|
||||||
|
|
||||||
|
|
||||||
def __has_comment_begining__(extension, string):
|
def __has_comment_begining__(extension, string):
|
||||||
if __comment_markers_must_be_at_begining__.get(extension, None) == True:
|
if __comment_markers_must_be_at_begining__.get(extension, None):
|
||||||
return string.find(__comment_begining__[extension]) == 0
|
return string.find(__comment_begining__[extension]) == 0
|
||||||
elif __comment_begining__.get(extension, None) != None and string.find(__comment_end__[extension], 2) == -1:
|
elif __comment_begining__.get(extension, None) is not None and string.find(__comment_end__[extension], 2) == -1:
|
||||||
return string.find(__comment_begining__[extension]) != -1
|
return string.find(__comment_begining__[extension]) != -1
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __has_comment_end__(extension, string):
|
def __has_comment_end__(extension, string):
|
||||||
if __comment_markers_must_be_at_begining__.get(extension, None) == True:
|
if __comment_markers_must_be_at_begining__.get(extension, None):
|
||||||
return string.find(__comment_end__[extension]) == 0
|
return string.find(__comment_end__[extension]) == 0
|
||||||
elif __comment_end__.get(extension, None) != None:
|
elif __comment_end__.get(extension, None) is not None:
|
||||||
return string.find(__comment_end__[extension]) != -1
|
return string.find(__comment_end__[extension]) != -1
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_comment(extension, string):
|
def is_comment(extension, string):
|
||||||
if __comment_begining__.get(extension, None) != None and string.strip().startswith(__comment_begining__[extension]):
|
if __comment_begining__.get(extension, None) is not None and string.strip().startswith(__comment_begining__[extension]):
|
||||||
return True
|
return True
|
||||||
if __comment_end__.get(extension, None) != None and string.strip().endswith(__comment_end__[extension]):
|
if __comment_end__.get(extension, None) is not None and string.strip().endswith(__comment_end__[extension]):
|
||||||
return True
|
return True
|
||||||
if __comment__.get(extension, None) != None and string.strip().startswith(__comment__[extension]):
|
if __comment__.get(extension, None) is not None and string.strip().startswith(__comment__[extension]):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def handle_comment_block(is_inside_comment, extension, content):
|
def handle_comment_block(is_inside_comment, extension, content):
|
||||||
comments = 0
|
comments = 0
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
from . import extensions, filtering, format, interval, optval
|
from . import extensions, filtering, format, interval, optval
|
||||||
|
|
||||||
|
|
||||||
class GitConfig(object):
|
class GitConfig(object):
|
||||||
def __init__(self, run, repo, global_only=False):
|
def __init__(self, run, repo, global_only=False):
|
||||||
self.run = run
|
self.run = run
|
||||||
@ -31,8 +32,10 @@ class GitConfig(object):
|
|||||||
def __read_git_config__(self, variable):
|
def __read_git_config__(self, variable):
|
||||||
previous_directory = os.getcwd()
|
previous_directory = os.getcwd()
|
||||||
os.chdir(self.repo)
|
os.chdir(self.repo)
|
||||||
setting = subprocess.Popen([_f for _f in ["git", "config", "--global" if self.global_only else "",
|
setting = subprocess.Popen(
|
||||||
"inspector." + variable] if _f], stdout=subprocess.PIPE).stdout
|
[_f for _f in ["git", "config", "--global" if self.global_only else "", "inspector." + variable] if _f],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
).stdout
|
||||||
os.chdir(previous_directory)
|
os.chdir(previous_directory)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -18,24 +18,27 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_EXTENSIONS = ["java", "c", "cc", "cpp", "h", "hh", "hpp", "py", "glsl", "rb", "js", "sql"]
|
DEFAULT_EXTENSIONS = ["java", "c", "cc", "cpp", "h", "hh", "hpp", "py", "glsl", "rb", "js", "sql"]
|
||||||
|
|
||||||
__extensions__ = DEFAULT_EXTENSIONS
|
__extensions__ = DEFAULT_EXTENSIONS
|
||||||
__located_extensions__ = set()
|
__located_extensions__ = set()
|
||||||
|
|
||||||
|
|
||||||
def get():
|
def get():
|
||||||
return __extensions__
|
return __extensions__
|
||||||
|
|
||||||
|
|
||||||
def define(string):
|
def define(string):
|
||||||
global __extensions__
|
global __extensions__
|
||||||
__extensions__ = string.split(",")
|
__extensions__ = string.split(",")
|
||||||
|
|
||||||
|
|
||||||
def add_located(string):
|
def add_located(string):
|
||||||
if len(string) == 0:
|
if len(string) == 0:
|
||||||
__located_extensions__.add("*")
|
__located_extensions__.add("*")
|
||||||
else:
|
else:
|
||||||
__located_extensions__.add(string)
|
__located_extensions__.add(string)
|
||||||
|
|
||||||
|
|
||||||
def get_located():
|
def get_located():
|
||||||
return __located_extensions__
|
return __located_extensions__
|
||||||
|
@ -21,17 +21,25 @@
|
|||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
__filters__ = {"file": [set(), set()], "author": [set(), set()], "email": [set(), set()], "revision": [set(), set()],
|
__filters__ = {
|
||||||
"message" : [set(), None]}
|
"file": [set(), set()],
|
||||||
|
"author": [set(), set()],
|
||||||
|
"email": [set(), set()],
|
||||||
|
"revision": [set(), set()],
|
||||||
|
"message": [set(), None],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class InvalidRegExpError(ValueError):
|
class InvalidRegExpError(ValueError):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
super(InvalidRegExpError, self).__init__(msg)
|
super(InvalidRegExpError, self).__init__(msg)
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
|
|
||||||
def get():
|
def get():
|
||||||
return __filters__
|
return __filters__
|
||||||
|
|
||||||
|
|
||||||
def __add_one__(string):
|
def __add_one__(string):
|
||||||
for i in __filters__:
|
for i in __filters__:
|
||||||
if (i + ":").lower() == string[0:len(i) + 1].lower():
|
if (i + ":").lower() == string[0:len(i) + 1].lower():
|
||||||
@ -39,27 +47,33 @@ def __add_one__(string):
|
|||||||
return
|
return
|
||||||
__filters__["file"][0].add(string)
|
__filters__["file"][0].add(string)
|
||||||
|
|
||||||
|
|
||||||
def add(string):
|
def add(string):
|
||||||
rules = string.split(",")
|
rules = string.split(",")
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
__add_one__(rule)
|
__add_one__(rule)
|
||||||
|
|
||||||
|
|
||||||
def clear():
|
def clear():
|
||||||
for i in __filters__:
|
for i in __filters__:
|
||||||
__filters__[i][0] = set()
|
__filters__[i][0] = set()
|
||||||
|
|
||||||
|
|
||||||
def get_filered(filter_type="file"):
|
def get_filered(filter_type="file"):
|
||||||
return __filters__[filter_type][1]
|
return __filters__[filter_type][1]
|
||||||
|
|
||||||
|
|
||||||
def has_filtered():
|
def has_filtered():
|
||||||
for i in __filters__:
|
for i in __filters__:
|
||||||
if __filters__[i][1]:
|
if __filters__[i][1]:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __find_commit_message__(sha):
|
def __find_commit_message__(sha):
|
||||||
git_show_r = subprocess.Popen([_f for _f in ["git", "show", "-s", "--pretty=%B", "-w", sha] if _f],
|
git_show_r = subprocess.Popen(
|
||||||
stdout=subprocess.PIPE).stdout
|
[_f for _f in ["git", "show", "-s", "--pretty=%B", "-w", sha] if _f], stdout=subprocess.PIPE
|
||||||
|
).stdout
|
||||||
|
|
||||||
commit_message = git_show_r.read()
|
commit_message = git_show_r.read()
|
||||||
git_show_r.close()
|
git_show_r.close()
|
||||||
@ -68,6 +82,7 @@ def __find_commit_message__(sha):
|
|||||||
commit_message = commit_message.encode("latin-1", "replace")
|
commit_message = commit_message.encode("latin-1", "replace")
|
||||||
return commit_message.decode("utf-8", "replace")
|
return commit_message.decode("utf-8", "replace")
|
||||||
|
|
||||||
|
|
||||||
def set_filtered(string, filter_type="file"):
|
def set_filtered(string, filter_type="file"):
|
||||||
string = string.strip()
|
string = string.strip()
|
||||||
|
|
||||||
@ -78,7 +93,7 @@ def set_filtered(string, filter_type="file"):
|
|||||||
if filter_type == "message":
|
if filter_type == "message":
|
||||||
search_for = __find_commit_message__(string)
|
search_for = __find_commit_message__(string)
|
||||||
try:
|
try:
|
||||||
if re.search(i, search_for) != None:
|
if re.search(i, search_for) is not None:
|
||||||
if filter_type == "message":
|
if filter_type == "message":
|
||||||
__add_one__("revision:" + string)
|
__add_one__("revision:" + string)
|
||||||
else:
|
else:
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
import textwrap
|
import textwrap
|
||||||
@ -33,23 +32,28 @@ DEFAULT_FORMAT = __available_formats__[3]
|
|||||||
|
|
||||||
__selected_format__ = DEFAULT_FORMAT
|
__selected_format__ = DEFAULT_FORMAT
|
||||||
|
|
||||||
|
|
||||||
class InvalidFormatError(Exception):
|
class InvalidFormatError(Exception):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
super(InvalidFormatError, self).__init__(msg)
|
super(InvalidFormatError, self).__init__(msg)
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
|
|
||||||
def select(format):
|
def select(format):
|
||||||
global __selected_format__
|
global __selected_format__
|
||||||
__selected_format__ = format
|
__selected_format__ = format
|
||||||
|
|
||||||
return format in __available_formats__
|
return format in __available_formats__
|
||||||
|
|
||||||
|
|
||||||
def get_selected():
|
def get_selected():
|
||||||
return __selected_format__
|
return __selected_format__
|
||||||
|
|
||||||
|
|
||||||
def is_interactive_format():
|
def is_interactive_format():
|
||||||
return __selected_format__ == "text"
|
return __selected_format__ == "text"
|
||||||
|
|
||||||
|
|
||||||
def __output_html_template__(name):
|
def __output_html_template__(name):
|
||||||
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), name)
|
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), name)
|
||||||
file_r = open(template_path, "rb")
|
file_r = open(template_path, "rb")
|
||||||
@ -58,6 +62,7 @@ def __output_html_template__(name):
|
|||||||
file_r.close()
|
file_r.close()
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def __get_zip_file_content__(name, file_name="/html/flot.zip"):
|
def __get_zip_file_content__(name, file_name="/html/flot.zip"):
|
||||||
zip_file = zipfile.ZipFile(basedir.get_basedir() + file_name, "r")
|
zip_file = zipfile.ZipFile(basedir.get_basedir() + file_name, "r")
|
||||||
content = zip_file.read(name)
|
content = zip_file.read(name)
|
||||||
@ -65,17 +70,20 @@ def __get_zip_file_content__(name, file_name="/html/flot.zip"):
|
|||||||
zip_file.close()
|
zip_file.close()
|
||||||
return content.decode("utf-8", "replace")
|
return content.decode("utf-8", "replace")
|
||||||
|
|
||||||
|
|
||||||
INFO_ONE_REPOSITORY = N_("Statistical information for the repository '{0}' was gathered on {1}.")
|
INFO_ONE_REPOSITORY = N_("Statistical information for the repository '{0}' was gathered on {1}.")
|
||||||
INFO_MANY_REPOSITORIES = N_("Statistical information for the repositories '{0}' was gathered on {1}.")
|
INFO_MANY_REPOSITORIES = N_("Statistical information for the repositories '{0}' was gathered on {1}.")
|
||||||
|
|
||||||
|
|
||||||
def output_header(repos):
|
def output_header(repos):
|
||||||
repos_string = ", ".join([repo.name for repo in repos])
|
repos_string = ", ".join([repo.name for repo in repos])
|
||||||
|
|
||||||
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
||||||
base = basedir.get_basedir()
|
base = basedir.get_basedir()
|
||||||
html_header = __output_html_template__(base + "/html/html.header")
|
html_header = __output_html_template__(base + "/html/html.header")
|
||||||
tablesorter_js = __get_zip_file_content__("jquery.tablesorter.min.js",
|
tablesorter_js = __get_zip_file_content__("jquery.tablesorter.min.js", "/html/jquery.tablesorter.min.js.zip").encode(
|
||||||
"/html/jquery.tablesorter.min.js.zip").encode("latin-1", "replace")
|
"latin-1", "replace"
|
||||||
|
)
|
||||||
tablesorter_js = tablesorter_js.decode("utf-8", "ignore")
|
tablesorter_js = tablesorter_js.decode("utf-8", "ignore")
|
||||||
flot_js = __get_zip_file_content__("jquery.flot.js")
|
flot_js = __get_zip_file_content__("jquery.flot.js")
|
||||||
pie_js = __get_zip_file_content__("jquery.flot.pie.js")
|
pie_js = __get_zip_file_content__("jquery.flot.pie.js")
|
||||||
@ -89,40 +97,44 @@ def output_header(repos):
|
|||||||
if __selected_format__ == "htmlembedded":
|
if __selected_format__ == "htmlembedded":
|
||||||
jquery_js = ">" + __get_zip_file_content__("jquery.js")
|
jquery_js = ">" + __get_zip_file_content__("jquery.js")
|
||||||
else:
|
else:
|
||||||
jquery_js = " src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\">"
|
jquery_js = ' src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">'
|
||||||
|
|
||||||
print(html_header.format(title=_("Repository statistics for '{0}'").format(repos_string),
|
print(
|
||||||
|
html_header.format(
|
||||||
|
title=_("Repository statistics for '{0}'").format(repos_string),
|
||||||
jquery=jquery_js,
|
jquery=jquery_js,
|
||||||
jquery_tablesorter=tablesorter_js,
|
jquery_tablesorter=tablesorter_js,
|
||||||
jquery_flot=flot_js,
|
jquery_flot=flot_js,
|
||||||
jquery_flot_pie=pie_js,
|
jquery_flot_pie=pie_js,
|
||||||
jquery_flot_resize=resize_js,
|
jquery_flot_resize=resize_js,
|
||||||
logo=logo.decode("utf-8", "replace"),
|
logo=logo.decode("utf-8", "replace"),
|
||||||
logo_text=_("The output has been generated by {0} {1}. The statistical analysis tool"
|
logo_text=_(
|
||||||
" for git repositories.").format(
|
"The output has been generated by {0} {1}. The statistical analysis tool" " for git repositories."
|
||||||
"<a href=\"https://github.com/ejwa/gitinspector\">gitinspector</a>",
|
).format('<a href="https://github.com/ejwa/gitinspector">gitinspector</a>', version.__version__),
|
||||||
version.__version__),
|
|
||||||
repo_text=_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
|
repo_text=_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
|
||||||
repos_string, localization.get_date()),
|
repos_string, localization.get_date()
|
||||||
|
),
|
||||||
show_minor_authors=_("Show minor authors"),
|
show_minor_authors=_("Show minor authors"),
|
||||||
hide_minor_authors=_("Hide minor authors"),
|
hide_minor_authors=_("Hide minor authors"),
|
||||||
show_minor_rows=_("Show rows with minor work"),
|
show_minor_rows=_("Show rows with minor work"),
|
||||||
hide_minor_rows=_("Hide rows with minor work")))
|
hide_minor_rows=_("Hide rows with minor work"),
|
||||||
|
)
|
||||||
|
)
|
||||||
elif __selected_format__ == "json":
|
elif __selected_format__ == "json":
|
||||||
print("{\n\t\"gitinspector\": {")
|
print('{\n\t"gitinspector": {')
|
||||||
print("\t\t\"version\": \"" + version.__version__ + "\",")
|
print('\t\t"version": "' + version.__version__ + '",')
|
||||||
|
|
||||||
if len(repos) <= 1:
|
if len(repos) <= 1:
|
||||||
print("\t\t\"repository\": \"" + repos_string + "\",")
|
print('\t\t"repository": "' + repos_string + '",')
|
||||||
else:
|
else:
|
||||||
repos_json = "\t\t\"repositories\": [ "
|
repos_json = '\t\t"repositories": [ '
|
||||||
|
|
||||||
for repo in repos:
|
for repo in repos:
|
||||||
repos_json += "\"" + repo.name + "\", "
|
repos_json += '"' + repo.name + '", '
|
||||||
|
|
||||||
print(repos_json[:-2] + " ],")
|
print(repos_json[:-2] + " ],")
|
||||||
|
|
||||||
print("\t\t\"report_date\": \"" + time.strftime("%Y/%m/%d") + "\",")
|
print('\t\t"report_date": "' + time.strftime("%Y/%m/%d") + '",')
|
||||||
|
|
||||||
elif __selected_format__ == "xml":
|
elif __selected_format__ == "xml":
|
||||||
print("<gitinspector>")
|
print("<gitinspector>")
|
||||||
@ -140,8 +152,15 @@ def output_header(repos):
|
|||||||
|
|
||||||
print("\t<report-date>" + time.strftime("%Y/%m/%d") + "</report-date>")
|
print("\t<report-date>" + time.strftime("%Y/%m/%d") + "</report-date>")
|
||||||
else:
|
else:
|
||||||
print(textwrap.fill(_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
|
print(
|
||||||
repos_string, localization.get_date()), width=terminal.get_size()[0]))
|
textwrap.fill(
|
||||||
|
_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
|
||||||
|
repos_string, localization.get_date()
|
||||||
|
),
|
||||||
|
width=terminal.get_size()[0],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def output_footer():
|
def output_footer():
|
||||||
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
import getopt
|
import getopt
|
||||||
import os
|
import os
|
||||||
@ -27,8 +26,7 @@ from .blame import Blame
|
|||||||
from .changes import Changes
|
from .changes import Changes
|
||||||
from .config import GitConfig
|
from .config import GitConfig
|
||||||
from .metrics import MetricsLogic
|
from .metrics import MetricsLogic
|
||||||
from . import (basedir, clone, extensions, filtering, format, help, interval,
|
from . import basedir, clone, extensions, filtering, format, help, interval, localization, optval, terminal, version
|
||||||
localization, optval, terminal, version)
|
|
||||||
from .output import outputable
|
from .output import outputable
|
||||||
from .output.blameoutput import BlameOutput
|
from .output.blameoutput import BlameOutput
|
||||||
from .output.changesoutput import ChangesOutput
|
from .output.changesoutput import ChangesOutput
|
||||||
@ -40,6 +38,7 @@ from .output.timelineoutput import TimelineOutput
|
|||||||
|
|
||||||
localization.init()
|
localization.init()
|
||||||
|
|
||||||
|
|
||||||
class Runner(object):
|
class Runner(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.hard = False
|
self.hard = False
|
||||||
@ -102,11 +101,13 @@ class Runner(object):
|
|||||||
format.output_footer()
|
format.output_footer()
|
||||||
os.chdir(previous_directory)
|
os.chdir(previous_directory)
|
||||||
|
|
||||||
|
|
||||||
def __check_python_version__():
|
def __check_python_version__():
|
||||||
if sys.version_info < (2, 6):
|
if sys.version_info < (2, 6):
|
||||||
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
|
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
|
||||||
sys.exit(_("gitinspector requires at least Python 2.6 to run (version {0} was found).").format(python_version))
|
sys.exit(_("gitinspector requires at least Python 2.6 to run (version {0} was found).").format(python_version))
|
||||||
|
|
||||||
|
|
||||||
def __get_validated_git_repos__(repos_relative):
|
def __get_validated_git_repos__(repos_relative):
|
||||||
if not repos_relative:
|
if not repos_relative:
|
||||||
repos_relative = "."
|
repos_relative = "."
|
||||||
@ -117,7 +118,7 @@ def __get_validated_git_repos__(repos_relative):
|
|||||||
for repo in repos_relative:
|
for repo in repos_relative:
|
||||||
cloned_repo = clone.create(repo)
|
cloned_repo = clone.create(repo)
|
||||||
|
|
||||||
if cloned_repo.name == None:
|
if cloned_repo.name is None:
|
||||||
cloned_repo.location = basedir.get_basedir_git(cloned_repo.location)
|
cloned_repo.location = basedir.get_basedir_git(cloned_repo.location)
|
||||||
cloned_repo.name = os.path.basename(cloned_repo.location)
|
cloned_repo.name = os.path.basename(cloned_repo.location)
|
||||||
|
|
||||||
@ -125,6 +126,7 @@ def __get_validated_git_repos__(repos_relative):
|
|||||||
|
|
||||||
return repos
|
return repos
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
terminal.check_terminal_encoding()
|
terminal.check_terminal_encoding()
|
||||||
terminal.set_stdin_encoding()
|
terminal.set_stdin_encoding()
|
||||||
@ -133,10 +135,27 @@ def main():
|
|||||||
repos = []
|
repos = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opts, args = optval.gnu_getopt(argv[1:], "f:F:hHlLmrTwx:", ["exclude=", "file-types=", "format=",
|
opts, args = optval.gnu_getopt(
|
||||||
"hard:true", "help", "list-file-types:true", "localize-output:true",
|
argv[1:],
|
||||||
"metrics:true", "responsibilities:true", "since=", "grading:true",
|
"f:F:hHlLmrTwx:",
|
||||||
"timeline:true", "until=", "version", "weeks:true"])
|
[
|
||||||
|
"exclude=",
|
||||||
|
"file-types=",
|
||||||
|
"format=",
|
||||||
|
"hard:true",
|
||||||
|
"help",
|
||||||
|
"list-file-types:true",
|
||||||
|
"localize-output:true",
|
||||||
|
"metrics:true",
|
||||||
|
"responsibilities:true",
|
||||||
|
"since=",
|
||||||
|
"grading:true",
|
||||||
|
"timeline:true",
|
||||||
|
"until=",
|
||||||
|
"version",
|
||||||
|
"weeks:true",
|
||||||
|
],
|
||||||
|
)
|
||||||
repos = __get_validated_git_repos__(set(args))
|
repos = __get_validated_git_repos__(set(args))
|
||||||
|
|
||||||
# We need the repos above to be set before we read the git config.
|
# We need the repos above to be set before we read the git config.
|
||||||
@ -210,9 +229,11 @@ def main():
|
|||||||
print(_("Try `{0} --help' for more information.").format(sys.argv[0]), file=sys.stderr)
|
print(_("Try `{0} --help' for more information.").format(sys.argv[0]), file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
@atexit.register
|
@atexit.register
|
||||||
def cleanup():
|
def cleanup():
|
||||||
clone.delete()
|
clone.delete()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -27,6 +27,7 @@ except:
|
|||||||
|
|
||||||
from . import format
|
from . import format
|
||||||
|
|
||||||
|
|
||||||
def get_url(email, size=20):
|
def get_url(email, size=20):
|
||||||
md5hash = hashlib.md5(email.encode("utf-8").lower().strip()).hexdigest()
|
md5hash = hashlib.md5(email.encode("utf-8").lower().strip()).hexdigest()
|
||||||
base_url = "https://www.gravatar.com/avatar/" + md5hash
|
base_url = "https://www.gravatar.com/avatar/" + md5hash
|
||||||
|
@ -18,13 +18,13 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from .extensions import DEFAULT_EXTENSIONS
|
from .extensions import DEFAULT_EXTENSIONS
|
||||||
from .format import __available_formats__
|
from .format import __available_formats__
|
||||||
|
|
||||||
|
|
||||||
__doc__ = _("""Usage: {0} [OPTION]... [REPOSITORY]...
|
__doc__ = _(
|
||||||
|
"""Usage: {0} [OPTION]... [REPOSITORY]...
|
||||||
List information about the repository in REPOSITORY. If no repository is
|
List information about the repository in REPOSITORY. If no repository is
|
||||||
specified, the current directory is used. If multiple repositories are
|
specified, the current directory is used. If multiple repositories are
|
||||||
given, information will be merged into a unified statistical report.
|
given, information will be merged into a unified statistical report.
|
||||||
@ -76,7 +76,9 @@ add or remove one of the specified extensions, see -f or --file-types for
|
|||||||
more information.
|
more information.
|
||||||
|
|
||||||
gitinspector requires that the git executable is available in your PATH.
|
gitinspector requires that the git executable is available in your PATH.
|
||||||
Report gitinspector bugs to gitinspector@ejwa.se.""")
|
Report gitinspector bugs to gitinspector@ejwa.se."""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def output():
|
def output():
|
||||||
print(__doc__.format(sys.argv[0], ",".join(DEFAULT_EXTENSIONS), ",".join(__available_formats__)))
|
print(__doc__.format(sys.argv[0], ",".join(DEFAULT_EXTENSIONS), ",".join(__available_formats__)))
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from shlex import quote
|
from shlex import quote
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -30,26 +29,33 @@ __until__ = ""
|
|||||||
|
|
||||||
__ref__ = "HEAD"
|
__ref__ = "HEAD"
|
||||||
|
|
||||||
|
|
||||||
def has_interval():
|
def has_interval():
|
||||||
return __since__ + __until__ != ""
|
return __since__ + __until__ != ""
|
||||||
|
|
||||||
|
|
||||||
def get_since():
|
def get_since():
|
||||||
return __since__
|
return __since__
|
||||||
|
|
||||||
|
|
||||||
def set_since(since):
|
def set_since(since):
|
||||||
global __since__
|
global __since__
|
||||||
__since__ = "--since=" + quote(since)
|
__since__ = "--since=" + quote(since)
|
||||||
|
|
||||||
|
|
||||||
def get_until():
|
def get_until():
|
||||||
return __until__
|
return __until__
|
||||||
|
|
||||||
|
|
||||||
def set_until(until):
|
def set_until(until):
|
||||||
global __until__
|
global __until__
|
||||||
__until__ = "--until=" + quote(until)
|
__until__ = "--until=" + quote(until)
|
||||||
|
|
||||||
|
|
||||||
def get_ref():
|
def get_ref():
|
||||||
return __ref__
|
return __ref__
|
||||||
|
|
||||||
|
|
||||||
def set_ref(ref):
|
def set_ref(ref):
|
||||||
global __ref__
|
global __ref__
|
||||||
__ref__ = ref
|
__ref__ = ref
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
@ -31,10 +30,12 @@ __enabled__ = False
|
|||||||
__installed__ = False
|
__installed__ = False
|
||||||
__translation__ = None
|
__translation__ = None
|
||||||
|
|
||||||
|
|
||||||
# Dummy function used to handle string constants
|
# Dummy function used to handle string constants
|
||||||
def N_(message):
|
def N_(message):
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
global __enabled__
|
global __enabled__
|
||||||
global __installed__
|
global __installed__
|
||||||
@ -49,11 +50,11 @@ def init():
|
|||||||
lang = locale.getlocale()
|
lang = locale.getlocale()
|
||||||
|
|
||||||
# Fix for non-POSIX-compliant systems (Windows et al.).
|
# Fix for non-POSIX-compliant systems (Windows et al.).
|
||||||
if os.getenv('LANG') is None:
|
if os.getenv("LANG") is None:
|
||||||
lang = locale.getdefaultlocale()
|
lang = locale.getdefaultlocale()
|
||||||
|
|
||||||
if lang[0]:
|
if lang[0]:
|
||||||
os.environ['LANG'] = lang[0]
|
os.environ["LANG"] = lang[0]
|
||||||
|
|
||||||
if lang[0] is not None:
|
if lang[0] is not None:
|
||||||
filename = basedir.get_basedir() + "/translations/messages_%s.mo" % lang[0][0:2]
|
filename = basedir.get_basedir() + "/translations/messages_%s.mo" % lang[0][0:2]
|
||||||
@ -70,27 +71,32 @@ def init():
|
|||||||
__installed__ = True
|
__installed__ = True
|
||||||
__translation__.install()
|
__translation__.install()
|
||||||
|
|
||||||
|
|
||||||
def check_compatibility(version):
|
def check_compatibility(version):
|
||||||
if isinstance(__translation__, gettext.GNUTranslations):
|
if isinstance(__translation__, gettext.GNUTranslations):
|
||||||
header_pattern = re.compile("^([^:\n]+): *(.*?) *$", re.MULTILINE)
|
header_pattern = re.compile("^([^:\n]+): *(.*?) *$", re.MULTILINE)
|
||||||
header_entries = dict(header_pattern.findall(_("")))
|
header_entries = dict(header_pattern.findall(_("")))
|
||||||
|
|
||||||
if header_entries["Project-Id-Version"] != "gitinspector {0}".format(version):
|
if header_entries["Project-Id-Version"] != "gitinspector {0}".format(version):
|
||||||
print("WARNING: The translation for your system locale is not up to date with the current gitinspector "
|
print(
|
||||||
|
"WARNING: The translation for your system locale is not up to date with the current gitinspector "
|
||||||
"version. The current maintainer of this locale is {0}.".format(header_entries["Last-Translator"]),
|
"version. The current maintainer of this locale is {0}.".format(header_entries["Last-Translator"]),
|
||||||
file=sys.stderr)
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_date():
|
def get_date():
|
||||||
if __enabled__ and isinstance(__translation__, gettext.GNUTranslations):
|
if __enabled__ and isinstance(__translation__, gettext.GNUTranslations):
|
||||||
date = time.strftime("%x")
|
date = time.strftime("%x")
|
||||||
|
|
||||||
if hasattr(date, 'decode'):
|
if hasattr(date, "decode"):
|
||||||
date = date.decode("utf-8", "replace")
|
date = date.decode("utf-8", "replace")
|
||||||
|
|
||||||
return date
|
return date
|
||||||
else:
|
else:
|
||||||
return time.strftime("%Y/%m/%d")
|
return time.strftime("%Y/%m/%d")
|
||||||
|
|
||||||
|
|
||||||
def enable():
|
def enable():
|
||||||
if isinstance(__translation__, gettext.GNUTranslations):
|
if isinstance(__translation__, gettext.GNUTranslations):
|
||||||
__translation__.install(True)
|
__translation__.install(True)
|
||||||
@ -98,6 +104,7 @@ def enable():
|
|||||||
global __enabled__
|
global __enabled__
|
||||||
__enabled__ = True
|
__enabled__ = True
|
||||||
|
|
||||||
|
|
||||||
def disable():
|
def disable():
|
||||||
global __enabled__
|
global __enabled__
|
||||||
__enabled__ = False
|
__enabled__ = False
|
||||||
|
@ -23,29 +23,62 @@ import subprocess
|
|||||||
from .changes import FileDiff
|
from .changes import FileDiff
|
||||||
from . import comment, filtering, interval
|
from . import comment, filtering, interval
|
||||||
|
|
||||||
__metric_eloc__ = {"java": 500, "c": 500, "cpp": 500, "cs": 500, "h": 300, "hpp": 300, "php": 500, "py": 500, "glsl": 1000,
|
__metric_eloc__ = {
|
||||||
"rb": 500, "js": 500, "sql": 1000, "xml": 1000}
|
"java": 500,
|
||||||
|
"c": 500,
|
||||||
|
"cpp": 500,
|
||||||
|
"cs": 500,
|
||||||
|
"h": 300,
|
||||||
|
"hpp": 300,
|
||||||
|
"php": 500,
|
||||||
|
"py": 500,
|
||||||
|
"glsl": 1000,
|
||||||
|
"rb": 500,
|
||||||
|
"js": 500,
|
||||||
|
"sql": 1000,
|
||||||
|
"xml": 1000,
|
||||||
|
}
|
||||||
|
|
||||||
__metric_cc_tokens__ = [[["java", "js", "c", "cc", "cpp"], ["else", r"for\s+\(.*\)", r"if\s+\(.*\)", r"case\s+\w+:",
|
__metric_cc_tokens__ = [
|
||||||
"default:", r"while\s+\(.*\)"],
|
[
|
||||||
["assert", "break", "continue", "return"]],
|
["java", "js", "c", "cc", "cpp"],
|
||||||
[["cs"], ["else", r"for\s+\(.*\)", r"foreach\s+\(.*\)", r"goto\s+\w+:", r"if\s+\(.*\)", r"case\s+\w+:",
|
["else", r"for\s+\(.*\)", r"if\s+\(.*\)", r"case\s+\w+:", "default:", r"while\s+\(.*\)"],
|
||||||
"default:", r"while\s+\(.*\)"],
|
["assert", "break", "continue", "return"],
|
||||||
["assert", "break", "continue", "return"]],
|
],
|
||||||
[["py"], [r"^\s+elif .*:$", r"^\s+else:$", r"^\s+for .*:", r"^\s+if .*:$", r"^\s+while .*:$"],
|
[
|
||||||
[r"^\s+assert", "break", "continue", "return"]]]
|
["cs"],
|
||||||
|
[
|
||||||
|
"else",
|
||||||
|
r"for\s+\(.*\)",
|
||||||
|
r"foreach\s+\(.*\)",
|
||||||
|
r"goto\s+\w+:",
|
||||||
|
r"if\s+\(.*\)",
|
||||||
|
r"case\s+\w+:",
|
||||||
|
"default:",
|
||||||
|
r"while\s+\(.*\)",
|
||||||
|
],
|
||||||
|
["assert", "break", "continue", "return"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["py"],
|
||||||
|
[r"^\s+elif .*:$", r"^\s+else:$", r"^\s+for .*:", r"^\s+if .*:$", r"^\s+while .*:$"],
|
||||||
|
[r"^\s+assert", "break", "continue", "return"],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD = 50
|
METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD = 50
|
||||||
METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD = 0.75
|
METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD = 0.75
|
||||||
|
|
||||||
|
|
||||||
class MetricsLogic(object):
|
class MetricsLogic(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.eloc = {}
|
self.eloc = {}
|
||||||
self.cyclomatic_complexity = {}
|
self.cyclomatic_complexity = {}
|
||||||
self.cyclomatic_complexity_density = {}
|
self.cyclomatic_complexity_density = {}
|
||||||
|
|
||||||
ls_tree_p = subprocess.Popen(["git", "ls-tree", "--name-only", "-r", interval.get_ref()],
|
ls_tree_p = subprocess.Popen(
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
["git", "ls-tree", "--name-only", "-r", interval.get_ref()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||||
|
)
|
||||||
lines = ls_tree_p.communicate()[0].splitlines()
|
lines = ls_tree_p.communicate()[0].splitlines()
|
||||||
ls_tree_p.stdout.close()
|
ls_tree_p.stdout.close()
|
||||||
|
|
||||||
@ -53,17 +86,18 @@ class MetricsLogic(object):
|
|||||||
for i in lines:
|
for i in lines:
|
||||||
i = i.strip().decode("unicode_escape", "ignore")
|
i = i.strip().decode("unicode_escape", "ignore")
|
||||||
i = i.encode("latin-1", "replace")
|
i = i.encode("latin-1", "replace")
|
||||||
i = i.decode("utf-8", "replace").strip("\"").strip("'").strip()
|
i = i.decode("utf-8", "replace").strip('"').strip("'").strip()
|
||||||
|
|
||||||
if FileDiff.is_valid_extension(i) and not filtering.set_filtered(FileDiff.get_filename(i)):
|
if FileDiff.is_valid_extension(i) and not filtering.set_filtered(FileDiff.get_filename(i)):
|
||||||
file_r = subprocess.Popen(["git", "show", interval.get_ref() + ":{0}".format(i.strip())],
|
file_r = subprocess.Popen(
|
||||||
stdout=subprocess.PIPE).stdout.readlines()
|
["git", "show", interval.get_ref() + ":{0}".format(i.strip())], stdout=subprocess.PIPE
|
||||||
|
).stdout.readlines()
|
||||||
|
|
||||||
extension = FileDiff.get_extension(i)
|
extension = FileDiff.get_extension(i)
|
||||||
lines = MetricsLogic.get_eloc(file_r, extension)
|
lines = MetricsLogic.get_eloc(file_r, extension)
|
||||||
cycc = MetricsLogic.get_cyclomatic_complexity(file_r, extension)
|
cycc = MetricsLogic.get_cyclomatic_complexity(file_r, extension)
|
||||||
|
|
||||||
if __metric_eloc__.get(extension, None) != None and __metric_eloc__[extension] < lines:
|
if __metric_eloc__.get(extension, None) is not None and __metric_eloc__[extension] < lines:
|
||||||
self.eloc[i.strip()] = lines
|
self.eloc[i.strip()] = lines
|
||||||
|
|
||||||
if METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD < cycc:
|
if METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD < cycc:
|
||||||
@ -79,7 +113,7 @@ class MetricsLogic(object):
|
|||||||
self.cyclomatic_complexity_density.update(other.cyclomatic_complexity_density)
|
self.cyclomatic_complexity_density.update(other.cyclomatic_complexity_density)
|
||||||
return self
|
return self
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return other;
|
return other
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_cyclomatic_complexity(file_r, extension):
|
def get_cyclomatic_complexity(file_r, extension):
|
||||||
|
@ -20,11 +20,13 @@
|
|||||||
|
|
||||||
import getopt
|
import getopt
|
||||||
|
|
||||||
|
|
||||||
class InvalidOptionArgument(Exception):
|
class InvalidOptionArgument(Exception):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
super(InvalidOptionArgument, self).__init__(msg)
|
super(InvalidOptionArgument, self).__init__(msg)
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
|
|
||||||
def __find_arg_in_options__(arg, options):
|
def __find_arg_in_options__(arg, options):
|
||||||
for opt in options:
|
for opt in options:
|
||||||
if opt[0].find(arg) == 0:
|
if opt[0].find(arg) == 0:
|
||||||
@ -32,6 +34,7 @@ def __find_arg_in_options__(arg, options):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def __find_options_to_extend__(long_options):
|
def __find_options_to_extend__(long_options):
|
||||||
options_to_extend = []
|
options_to_extend = []
|
||||||
|
|
||||||
@ -43,8 +46,10 @@ def __find_options_to_extend__(long_options):
|
|||||||
|
|
||||||
return options_to_extend
|
return options_to_extend
|
||||||
|
|
||||||
|
|
||||||
# This is a duplicate of gnu_getopt, but with support for optional arguments in long options, in the form; "arg:default_value".
|
# This is a duplicate of gnu_getopt, but with support for optional arguments in long options, in the form; "arg:default_value".
|
||||||
|
|
||||||
|
|
||||||
def gnu_getopt(args, options, long_options):
|
def gnu_getopt(args, options, long_options):
|
||||||
options_to_extend = __find_options_to_extend__(long_options)
|
options_to_extend = __find_options_to_extend__(long_options)
|
||||||
|
|
||||||
@ -55,10 +60,11 @@ def gnu_getopt(args, options, long_options):
|
|||||||
|
|
||||||
return getopt.gnu_getopt(args, options, long_options)
|
return getopt.gnu_getopt(args, options, long_options)
|
||||||
|
|
||||||
|
|
||||||
def get_boolean_argument(arg):
|
def get_boolean_argument(arg):
|
||||||
if isinstance(arg, bool):
|
if isinstance(arg, bool):
|
||||||
return arg
|
return arg
|
||||||
elif arg == None or arg.lower() == "false" or arg.lower() == "f" or arg == "0":
|
elif arg is None or arg.lower() == "false" or arg.lower() == "f" or arg == "0":
|
||||||
return False
|
return False
|
||||||
elif arg.lower() == "true" or arg.lower() == "t" or arg == "1":
|
elif arg.lower() == "true" or arg.lower() == "t" or arg == "1":
|
||||||
return True
|
return True
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
@ -27,8 +26,10 @@ from .. import format, gravatar, terminal
|
|||||||
from ..blame import Blame
|
from ..blame import Blame
|
||||||
from .outputable import Outputable
|
from .outputable import Outputable
|
||||||
|
|
||||||
BLAME_INFO_TEXT = N_("Below are the number of rows from each author that have survived and are still "
|
BLAME_INFO_TEXT = N_(
|
||||||
"intact in the current revision")
|
"Below are the number of rows from each author that have survived and are still " "intact in the current revision"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BlameOutput(Outputable):
|
class BlameOutput(Outputable):
|
||||||
def __init__(self, changes, blame):
|
def __init__(self, changes, blame):
|
||||||
@ -40,10 +41,11 @@ class BlameOutput(Outputable):
|
|||||||
Outputable.__init__(self)
|
Outputable.__init__(self)
|
||||||
|
|
||||||
def output_html(self):
|
def output_html(self):
|
||||||
blame_xml = "<div><div class=\"box\">"
|
blame_xml = '<div><div class="box">'
|
||||||
blame_xml += "<p>" + _(BLAME_INFO_TEXT) + ".</p><div><table id=\"blame\" class=\"git\">"
|
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(
|
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"))
|
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments")
|
||||||
|
)
|
||||||
blame_xml += "<tbody>"
|
blame_xml += "<tbody>"
|
||||||
chart_data = ""
|
chart_data = ""
|
||||||
blames = sorted(self.blame.get_summed_blames().items())
|
blames = sorted(self.blame.get_summed_blames().items())
|
||||||
@ -54,11 +56,11 @@ class BlameOutput(Outputable):
|
|||||||
|
|
||||||
for i, entry in enumerate(blames):
|
for i, entry in enumerate(blames):
|
||||||
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
|
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
|
||||||
blame_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
|
blame_xml += "<tr " + ('class="odd">' if i % 2 == 1 else ">")
|
||||||
|
|
||||||
if format.get_selected() == "html":
|
if format.get_selected() == "html":
|
||||||
author_email = self.changes.get_latest_email_by_author(entry[0])
|
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])
|
blame_xml += '<td><img src="{0}"/>{1}</td>'.format(gravatar.get_url(author_email), entry[0])
|
||||||
else:
|
else:
|
||||||
blame_xml += "<td>" + entry[0] + "</td>"
|
blame_xml += "<td>" + entry[0] + "</td>"
|
||||||
|
|
||||||
@ -66,24 +68,24 @@ class BlameOutput(Outputable):
|
|||||||
blame_xml += "<td>" + ("{0:.1f}".format(Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "</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:.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>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
|
||||||
blame_xml += "<td style=\"display: none\">" + work_percentage + "</td>"
|
blame_xml += '<td style="display: none">' + work_percentage + "</td>"
|
||||||
blame_xml += "</tr>"
|
blame_xml += "</tr>"
|
||||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry[0]), work_percentage)
|
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry[0]), work_percentage)
|
||||||
|
|
||||||
if blames[-1] != entry:
|
if blames[-1] != entry:
|
||||||
chart_data += ", "
|
chart_data += ", "
|
||||||
|
|
||||||
blame_xml += "<tfoot><tr> <td colspan=\"5\"> </td> </tr></tfoot></tbody></table>"
|
blame_xml += '<tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table>'
|
||||||
blame_xml += "<div class=\"chart\" id=\"blame_chart\"></div></div>"
|
blame_xml += '<div class="chart" id="blame_chart"></div></div>'
|
||||||
blame_xml += "<script type=\"text/javascript\">"
|
blame_xml += '<script type="text/javascript">'
|
||||||
blame_xml += " blame_plot = $.plot($(\"#blame_chart\"), [{0}], {{".format(chart_data)
|
blame_xml += ' blame_plot = $.plot($("#blame_chart"), [{0}], {{'.format(chart_data)
|
||||||
blame_xml += " series: {"
|
blame_xml += " series: {"
|
||||||
blame_xml += " pie: {"
|
blame_xml += " pie: {"
|
||||||
blame_xml += " innerRadius: 0.4,"
|
blame_xml += " innerRadius: 0.4,"
|
||||||
blame_xml += " show: true,"
|
blame_xml += " show: true,"
|
||||||
blame_xml += " combine: {"
|
blame_xml += " combine: {"
|
||||||
blame_xml += " threshold: 0.01,"
|
blame_xml += " threshold: 0.01,"
|
||||||
blame_xml += " label: \"" + _("Minor Authors") + "\""
|
blame_xml += ' label: "' + _("Minor Authors") + '"'
|
||||||
blame_xml += " }"
|
blame_xml += " }"
|
||||||
blame_xml += " }"
|
blame_xml += " }"
|
||||||
blame_xml += " }, grid: {"
|
blame_xml += " }, grid: {"
|
||||||
@ -95,35 +97,51 @@ class BlameOutput(Outputable):
|
|||||||
print(blame_xml)
|
print(blame_xml)
|
||||||
|
|
||||||
def output_json(self):
|
def output_json(self):
|
||||||
message_json = "\t\t\t\"message\": \"" + _(BLAME_INFO_TEXT) + "\",\n"
|
message_json = '\t\t\t"message": "' + _(BLAME_INFO_TEXT) + '",\n'
|
||||||
blame_json = ""
|
blame_json = ""
|
||||||
|
|
||||||
for i in sorted(self.blame.get_summed_blames().items()):
|
for i in sorted(self.blame.get_summed_blames().items()):
|
||||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||||
|
|
||||||
name_json = "\t\t\t\t\"name\": \"" + i[0] + "\",\n"
|
name_json = '\t\t\t\t"name": "' + i[0] + '",\n'
|
||||||
email_json = "\t\t\t\t\"email\": \"" + author_email + "\",\n"
|
email_json = '\t\t\t\t"email": "' + author_email + '",\n'
|
||||||
gravatar_json = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
|
gravatar_json = '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||||
rows_json = "\t\t\t\t\"rows\": " + str(i[1].rows) + ",\n"
|
rows_json = '\t\t\t\t"rows": ' + str(i[1].rows) + ",\n"
|
||||||
stability_json = ("\t\t\t\t\"stability\": " + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows,
|
stability_json = (
|
||||||
self.changes)) + ",\n")
|
'\t\t\t\t"stability": ' + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)) + ",\n"
|
||||||
age_json = ("\t\t\t\t\"age\": " + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n")
|
)
|
||||||
percentage_in_comments_json = ("\t\t\t\t\"percentage_in_comments\": " +
|
age_json = '\t\t\t\t"age": ' + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n"
|
||||||
"{0:.2f}".format(100.0 * i[1].comments / i[1].rows) + "\n")
|
percentage_in_comments_json = (
|
||||||
blame_json += ("{\n" + name_json + email_json + gravatar_json + rows_json + stability_json + age_json +
|
'\t\t\t\t"percentage_in_comments": ' + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) + "\n"
|
||||||
percentage_in_comments_json + "\t\t\t},")
|
)
|
||||||
|
blame_json += (
|
||||||
|
"{\n"
|
||||||
|
+ name_json
|
||||||
|
+ email_json
|
||||||
|
+ gravatar_json
|
||||||
|
+ rows_json
|
||||||
|
+ stability_json
|
||||||
|
+ age_json
|
||||||
|
+ percentage_in_comments_json
|
||||||
|
+ "\t\t\t},"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
blame_json = blame_json[:-1]
|
blame_json = blame_json[:-1]
|
||||||
|
|
||||||
print(",\n\t\t\"blame\": {\n" + message_json + "\t\t\t\"authors\": [\n\t\t\t" + blame_json + "]\n\t\t}", end="")
|
print(',\n\t\t"blame": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + blame_json + "]\n\t\t}", end="")
|
||||||
|
|
||||||
def output_text(self):
|
def output_text(self):
|
||||||
if sys.stdout.isatty() and format.is_interactive_format():
|
if sys.stdout.isatty() and format.is_interactive_format():
|
||||||
terminal.clear_row()
|
terminal.clear_row()
|
||||||
|
|
||||||
print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
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.printb(
|
||||||
terminal.rjust(_("Age"), 13) + terminal.rjust(_("% in comments"), 20))
|
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(self.blame.get_summed_blames().items()):
|
for i in sorted(self.blame.get_summed_blames().items()):
|
||||||
print(terminal.ljust(i[0], 20)[0:20 - terminal.get_excess_column_count(i[0])], end=" ")
|
print(terminal.ljust(i[0], 20)[0:20 - terminal.get_excess_column_count(i[0])], end=" ")
|
||||||
@ -143,12 +161,25 @@ class BlameOutput(Outputable):
|
|||||||
email_xml = "\t\t\t\t<email>" + author_email + "</email>\n"
|
email_xml = "\t\t\t\t<email>" + author_email + "</email>\n"
|
||||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\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"
|
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,
|
stability_xml = (
|
||||||
self.changes)) + "</stability>\n")
|
"\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) +
|
age_xml = "\t\t\t\t<age>" + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + "</age>\n"
|
||||||
"</percentage-in-comments>\n")
|
percentage_in_comments_xml = (
|
||||||
blame_xml += ("\t\t\t<author>\n" + name_xml + email_xml + gravatar_xml + rows_xml + stability_xml +
|
"\t\t\t\t<percentage-in-comments>"
|
||||||
age_xml + percentage_in_comments_xml + "\t\t\t</author>\n")
|
+ "{0:.2f}".format(100.0 * i[1].comments / i[1].rows)
|
||||||
|
+ "</percentage-in-comments>\n"
|
||||||
|
)
|
||||||
|
blame_xml += (
|
||||||
|
"\t\t\t<author>\n"
|
||||||
|
+ name_xml
|
||||||
|
+ email_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>")
|
print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
@ -28,6 +27,7 @@ from .outputable import Outputable
|
|||||||
HISTORICAL_INFO_TEXT = N_("The following historical commit information, by author, was found")
|
HISTORICAL_INFO_TEXT = N_("The following historical commit information, by author, was found")
|
||||||
NO_COMMITED_FILES_TEXT = N_("No commited files with the specified extensions were found")
|
NO_COMMITED_FILES_TEXT = N_("No commited files with the specified extensions were found")
|
||||||
|
|
||||||
|
|
||||||
class ChangesOutput(Outputable):
|
class ChangesOutput(Outputable):
|
||||||
def __init__(self, changes):
|
def __init__(self, changes):
|
||||||
self.changes = changes
|
self.changes = changes
|
||||||
@ -36,7 +36,7 @@ class ChangesOutput(Outputable):
|
|||||||
def output_html(self):
|
def output_html(self):
|
||||||
authorinfo_list = self.changes.get_authorinfo_list()
|
authorinfo_list = self.changes.get_authorinfo_list()
|
||||||
total_changes = 0.0
|
total_changes = 0.0
|
||||||
changes_xml = "<div><div class=\"box\">"
|
changes_xml = '<div><div class="box">'
|
||||||
chart_data = ""
|
chart_data = ""
|
||||||
|
|
||||||
for i in authorinfo_list:
|
for i in authorinfo_list:
|
||||||
@ -44,20 +44,22 @@ class ChangesOutput(Outputable):
|
|||||||
total_changes += authorinfo_list.get(i).deletions
|
total_changes += authorinfo_list.get(i).deletions
|
||||||
|
|
||||||
if authorinfo_list:
|
if authorinfo_list:
|
||||||
changes_xml += "<p>" + _(HISTORICAL_INFO_TEXT) + ".</p><div><table id=\"changes\" class=\"git\">"
|
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(
|
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"))
|
_("Author"), _("Commits"), _("Insertions"), _("Deletions"), _("% of changes")
|
||||||
|
)
|
||||||
changes_xml += "</tr></thead><tbody>"
|
changes_xml += "</tr></thead><tbody>"
|
||||||
|
|
||||||
for i, entry in enumerate(sorted(authorinfo_list)):
|
for i, entry in enumerate(sorted(authorinfo_list)):
|
||||||
authorinfo = authorinfo_list.get(entry)
|
authorinfo = authorinfo_list.get(entry)
|
||||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||||
|
|
||||||
changes_xml += "<tr " + ("class=\"odd\">" if i % 2 == 1 else ">")
|
changes_xml += "<tr " + ('class="odd">' if i % 2 == 1 else ">")
|
||||||
|
|
||||||
if format.get_selected() == "html":
|
if format.get_selected() == "html":
|
||||||
changes_xml += "<td><img src=\"{0}\"/>{1}</td>".format(
|
changes_xml += '<td><img src="{0}"/>{1}</td>'.format(
|
||||||
gravatar.get_url(self.changes.get_latest_email_by_author(entry)), entry)
|
gravatar.get_url(self.changes.get_latest_email_by_author(entry)), entry
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
changes_xml += "<td>" + entry + "</td>"
|
changes_xml += "<td>" + entry + "</td>"
|
||||||
|
|
||||||
@ -71,17 +73,17 @@ class ChangesOutput(Outputable):
|
|||||||
if sorted(authorinfo_list)[-1] != entry:
|
if sorted(authorinfo_list)[-1] != entry:
|
||||||
chart_data += ", "
|
chart_data += ", "
|
||||||
|
|
||||||
changes_xml += ("<tfoot><tr> <td colspan=\"5\"> </td> </tr></tfoot></tbody></table>")
|
changes_xml += '<tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table>'
|
||||||
changes_xml += "<div class=\"chart\" id=\"changes_chart\"></div></div>"
|
changes_xml += '<div class="chart" id="changes_chart"></div></div>'
|
||||||
changes_xml += "<script type=\"text/javascript\">"
|
changes_xml += '<script type="text/javascript">'
|
||||||
changes_xml += " changes_plot = $.plot($(\"#changes_chart\"), [{0}], {{".format(chart_data)
|
changes_xml += ' changes_plot = $.plot($("#changes_chart"), [{0}], {{'.format(chart_data)
|
||||||
changes_xml += " series: {"
|
changes_xml += " series: {"
|
||||||
changes_xml += " pie: {"
|
changes_xml += " pie: {"
|
||||||
changes_xml += " innerRadius: 0.4,"
|
changes_xml += " innerRadius: 0.4,"
|
||||||
changes_xml += " show: true,"
|
changes_xml += " show: true,"
|
||||||
changes_xml += " combine: {"
|
changes_xml += " combine: {"
|
||||||
changes_xml += " threshold: 0.01,"
|
changes_xml += " threshold: 0.01,"
|
||||||
changes_xml += " label: \"" + _("Minor Authors") + "\""
|
changes_xml += ' label: "' + _("Minor Authors") + '"'
|
||||||
changes_xml += " }"
|
changes_xml += " }"
|
||||||
changes_xml += " }"
|
changes_xml += " }"
|
||||||
changes_xml += " }, grid: {"
|
changes_xml += " }, grid: {"
|
||||||
@ -104,7 +106,7 @@ class ChangesOutput(Outputable):
|
|||||||
total_changes += authorinfo_list.get(i).deletions
|
total_changes += authorinfo_list.get(i).deletions
|
||||||
|
|
||||||
if authorinfo_list:
|
if authorinfo_list:
|
||||||
message_json = "\t\t\t\"message\": \"" + _(HISTORICAL_INFO_TEXT) + "\",\n"
|
message_json = '\t\t\t"message": "' + _(HISTORICAL_INFO_TEXT) + '",\n'
|
||||||
changes_json = ""
|
changes_json = ""
|
||||||
|
|
||||||
for i in sorted(authorinfo_list):
|
for i in sorted(authorinfo_list):
|
||||||
@ -112,23 +114,32 @@ class ChangesOutput(Outputable):
|
|||||||
authorinfo = authorinfo_list.get(i)
|
authorinfo = authorinfo_list.get(i)
|
||||||
|
|
||||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||||
name_json = "\t\t\t\t\"name\": \"" + i + "\",\n"
|
name_json = '\t\t\t\t"name": "' + i + '",\n'
|
||||||
email_json = "\t\t\t\t\"email\": \"" + author_email + "\",\n"
|
email_json = '\t\t\t\t"email": "' + author_email + '",\n'
|
||||||
gravatar_json = "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
|
gravatar_json = '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||||
commits_json = "\t\t\t\t\"commits\": " + str(authorinfo.commits) + ",\n"
|
commits_json = '\t\t\t\t"commits": ' + str(authorinfo.commits) + ",\n"
|
||||||
insertions_json = "\t\t\t\t\"insertions\": " + str(authorinfo.insertions) + ",\n"
|
insertions_json = '\t\t\t\t"insertions": ' + str(authorinfo.insertions) + ",\n"
|
||||||
deletions_json = "\t\t\t\t\"deletions\": " + str(authorinfo.deletions) + ",\n"
|
deletions_json = '\t\t\t\t"deletions": ' + str(authorinfo.deletions) + ",\n"
|
||||||
percentage_json = "\t\t\t\t\"percentage_of_changes\": " + "{0:.2f}".format(percentage) + "\n"
|
percentage_json = '\t\t\t\t"percentage_of_changes": ' + "{0:.2f}".format(percentage) + "\n"
|
||||||
|
|
||||||
changes_json += ("{\n" + name_json + email_json + gravatar_json + commits_json +
|
changes_json += (
|
||||||
insertions_json + deletions_json + percentage_json + "\t\t\t}")
|
"{\n"
|
||||||
|
+ name_json
|
||||||
|
+ email_json
|
||||||
|
+ gravatar_json
|
||||||
|
+ commits_json
|
||||||
|
+ insertions_json
|
||||||
|
+ deletions_json
|
||||||
|
+ percentage_json
|
||||||
|
+ "\t\t\t}"
|
||||||
|
)
|
||||||
changes_json += ","
|
changes_json += ","
|
||||||
else:
|
else:
|
||||||
changes_json = changes_json[:-1]
|
changes_json = changes_json[:-1]
|
||||||
|
|
||||||
print("\t\t\"changes\": {\n" + message_json + "\t\t\t\"authors\": [\n\t\t\t" + changes_json + "]\n\t\t}", end="")
|
print('\t\t"changes": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + changes_json + "]\n\t\t}", end="")
|
||||||
else:
|
else:
|
||||||
print("\t\t\"exception\": \"" + _(NO_COMMITED_FILES_TEXT) + "\"")
|
print('\t\t"exception": "' + _(NO_COMMITED_FILES_TEXT) + '"')
|
||||||
|
|
||||||
def output_text(self):
|
def output_text(self):
|
||||||
authorinfo_list = self.changes.get_authorinfo_list()
|
authorinfo_list = self.changes.get_authorinfo_list()
|
||||||
@ -140,9 +151,13 @@ class ChangesOutput(Outputable):
|
|||||||
|
|
||||||
if authorinfo_list:
|
if authorinfo_list:
|
||||||
print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||||
terminal.printb(terminal.ljust(_("Author"), 21) + terminal.rjust(_("Commits"), 13) +
|
terminal.printb(
|
||||||
terminal.rjust(_("Insertions"), 14) + terminal.rjust(_("Deletions"), 15) +
|
terminal.ljust(_("Author"), 21)
|
||||||
terminal.rjust(_("% of changes"), 16))
|
+ terminal.rjust(_("Commits"), 13)
|
||||||
|
+ terminal.rjust(_("Insertions"), 14)
|
||||||
|
+ terminal.rjust(_("Deletions"), 15)
|
||||||
|
+ terminal.rjust(_("% of changes"), 16)
|
||||||
|
)
|
||||||
|
|
||||||
for i in sorted(authorinfo_list):
|
for i in sorted(authorinfo_list):
|
||||||
authorinfo = authorinfo_list.get(i)
|
authorinfo = authorinfo_list.get(i)
|
||||||
@ -179,10 +194,21 @@ class ChangesOutput(Outputable):
|
|||||||
commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\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"
|
insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
|
||||||
deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\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"
|
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 + email_xml + gravatar_xml + commits_xml +
|
changes_xml += (
|
||||||
insertions_xml + deletions_xml + percentage_xml + "\t\t\t</author>\n")
|
"\t\t\t<author>\n"
|
||||||
|
+ name_xml
|
||||||
|
+ email_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>")
|
print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
|
||||||
else:
|
else:
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
from .. import extensions, terminal
|
from .. import extensions, terminal
|
||||||
@ -28,6 +27,7 @@ from .outputable import Outputable
|
|||||||
EXTENSIONS_INFO_TEXT = N_("The extensions below were found in the repository history")
|
EXTENSIONS_INFO_TEXT = N_("The extensions below were found in the repository history")
|
||||||
EXTENSIONS_MARKED_TEXT = N_("(extensions used during statistical analysis are marked)")
|
EXTENSIONS_MARKED_TEXT = N_("(extensions used during statistical analysis are marked)")
|
||||||
|
|
||||||
|
|
||||||
class ExtensionsOutput(Outputable):
|
class ExtensionsOutput(Outputable):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_marked(extension):
|
def is_marked(extension):
|
||||||
@ -38,7 +38,7 @@ class ExtensionsOutput(Outputable):
|
|||||||
|
|
||||||
def output_html(self):
|
def output_html(self):
|
||||||
if extensions.__located_extensions__:
|
if extensions.__located_extensions__:
|
||||||
extensions_xml = "<div><div class=\"box\">"
|
extensions_xml = '<div><div class="box">'
|
||||||
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
|
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
|
||||||
|
|
||||||
for i in sorted(extensions.__located_extensions__):
|
for i in sorted(extensions.__located_extensions__):
|
||||||
@ -53,26 +53,39 @@ class ExtensionsOutput(Outputable):
|
|||||||
|
|
||||||
def output_json(self):
|
def output_json(self):
|
||||||
if extensions.__located_extensions__:
|
if extensions.__located_extensions__:
|
||||||
message_json = "\t\t\t\"message\": \"" + _(EXTENSIONS_INFO_TEXT) + "\",\n"
|
message_json = '\t\t\t"message": "' + _(EXTENSIONS_INFO_TEXT) + '",\n'
|
||||||
used_extensions_json = ""
|
used_extensions_json = ""
|
||||||
unused_extensions_json = ""
|
unused_extensions_json = ""
|
||||||
|
|
||||||
for i in sorted(extensions.__located_extensions__):
|
for i in sorted(extensions.__located_extensions__):
|
||||||
if ExtensionsOutput.is_marked(i):
|
if ExtensionsOutput.is_marked(i):
|
||||||
used_extensions_json += "\"" + i + "\", "
|
used_extensions_json += '"' + i + '", '
|
||||||
else:
|
else:
|
||||||
unused_extensions_json += "\"" + i + "\", "
|
unused_extensions_json += '"' + i + '", '
|
||||||
|
|
||||||
used_extensions_json = used_extensions_json[:-2]
|
used_extensions_json = used_extensions_json[:-2]
|
||||||
unused_extensions_json = unused_extensions_json[:-2]
|
unused_extensions_json = unused_extensions_json[:-2]
|
||||||
|
|
||||||
print(",\n\t\t\"extensions\": {\n" + message_json + "\t\t\t\"used\": [ " + used_extensions_json +
|
print(
|
||||||
" ],\n\t\t\t\"unused\": [ " + unused_extensions_json + " ]\n" + "\t\t}", end="")
|
',\n\t\t"extensions": {\n'
|
||||||
|
+ message_json
|
||||||
|
+ '\t\t\t"used": [ '
|
||||||
|
+ used_extensions_json
|
||||||
|
+ ' ],\n\t\t\t"unused": [ '
|
||||||
|
+ unused_extensions_json
|
||||||
|
+ " ]\n"
|
||||||
|
+ "\t\t}",
|
||||||
|
end="",
|
||||||
|
)
|
||||||
|
|
||||||
def output_text(self):
|
def output_text(self):
|
||||||
if extensions.__located_extensions__:
|
if extensions.__located_extensions__:
|
||||||
print("\n" + textwrap.fill("{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)),
|
print(
|
||||||
width=terminal.get_size()[0]))
|
"\n"
|
||||||
|
+ textwrap.fill(
|
||||||
|
"{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)), width=terminal.get_size()[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for i in sorted(extensions.__located_extensions__):
|
for i in sorted(extensions.__located_extensions__):
|
||||||
if ExtensionsOutput.is_marked(i):
|
if ExtensionsOutput.is_marked(i):
|
||||||
@ -93,5 +106,14 @@ class ExtensionsOutput(Outputable):
|
|||||||
else:
|
else:
|
||||||
unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
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" +
|
print(
|
||||||
"\t\t<unused>\n" + unused_extensions_xml + "\t\t</unused>\n" + "\t</extensions>")
|
"\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>"
|
||||||
|
)
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
from ..filtering import __filters__, has_filtered
|
from ..filtering import __filters__, has_filtered
|
||||||
@ -26,11 +25,16 @@ from .. import terminal
|
|||||||
from .outputable import Outputable
|
from .outputable import Outputable
|
||||||
|
|
||||||
FILTERING_INFO_TEXT = N_("The following files were excluded from the statistics due to the specified exclusion patterns")
|
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_AUTHOR_INFO_TEXT = N_(
|
||||||
FILTERING_EMAIL_INFO_TEXT = N_("The authors with the following emails were excluded from the statistics due to the specified " \
|
"The following authors were excluded from the statistics due to the specified exclusion patterns"
|
||||||
"exclusion patterns")
|
)
|
||||||
FILTERING_COMMIT_INFO_TEXT = N_("The following commit revisions were excluded from the statistics due to the specified " \
|
FILTERING_EMAIL_INFO_TEXT = N_(
|
||||||
"exclusion patterns")
|
"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):
|
class FilteringOutput(Outputable):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -47,7 +51,7 @@ class FilteringOutput(Outputable):
|
|||||||
|
|
||||||
def output_html(self):
|
def output_html(self):
|
||||||
if has_filtered():
|
if has_filtered():
|
||||||
filtering_xml = "<div><div class=\"box\">"
|
filtering_xml = '<div><div class="box">'
|
||||||
FilteringOutput.__output_html_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
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_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||||
FilteringOutput.__output_html_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
FilteringOutput.__output_html_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||||
@ -59,26 +63,35 @@ class FilteringOutput(Outputable):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def __output_json_section__(info_string, filtered, container_tagname):
|
def __output_json_section__(info_string, filtered, container_tagname):
|
||||||
if filtered:
|
if filtered:
|
||||||
message_json = "\t\t\t\t\"message\": \"" + info_string + "\",\n"
|
message_json = '\t\t\t\t"message": "' + info_string + '",\n'
|
||||||
filtering_json = ""
|
filtering_json = ""
|
||||||
|
|
||||||
for i in filtered:
|
for i in filtered:
|
||||||
filtering_json += "\t\t\t\t\t\"" + i + "\",\n"
|
filtering_json += '\t\t\t\t\t"' + i + '",\n'
|
||||||
else:
|
else:
|
||||||
filtering_json = filtering_json[:-3]
|
filtering_json = filtering_json[:-3]
|
||||||
|
|
||||||
return "\n\t\t\t\"{0}\": {{\n".format(container_tagname) + message_json + \
|
return (
|
||||||
"\t\t\t\t\"entries\": [\n" + filtering_json + "\"\n\t\t\t\t]\n\t\t\t},"
|
'\n\t\t\t"{0}": {{\n'.format(container_tagname)
|
||||||
|
+ message_json
|
||||||
|
+ '\t\t\t\t"entries": [\n'
|
||||||
|
+ filtering_json
|
||||||
|
+ '"\n\t\t\t\t]\n\t\t\t},'
|
||||||
|
)
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def output_json(self):
|
def output_json(self):
|
||||||
if has_filtered():
|
if has_filtered():
|
||||||
output = ",\n\t\t\"filtering\": {"
|
output = ',\n\t\t"filtering": {'
|
||||||
output += FilteringOutput.__output_json_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
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_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_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||||
output += FilteringOutput.__output_json_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
|
output += FilteringOutput.__output_json_section__(
|
||||||
|
_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision"
|
||||||
|
)
|
||||||
output = output[:-1]
|
output = output[:-1]
|
||||||
output += "\n\t\t}"
|
output += "\n\t\t}"
|
||||||
print(output, end="")
|
print(output, end="")
|
||||||
|
@ -18,25 +18,27 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from ..changes import FileDiff
|
from ..changes import FileDiff
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
from ..metrics import (__metric_eloc__, METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD)
|
from ..metrics import __metric_eloc__, METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD
|
||||||
from .outputable import Outputable
|
from .outputable import Outputable
|
||||||
|
|
||||||
ELOC_INFO_TEXT = N_("The following files are suspiciously big (in order of severity)")
|
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_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 " \
|
CYCLOMATIC_COMPLEXITY_DENSITY_TEXT = N_(
|
||||||
"(in order of severity)")
|
"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_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"]]
|
METRICS_VIOLATION_SCORES = [[1.0, "minimal"], [1.25, "minor"], [1.5, "medium"], [2.0, "bad"], [3.0, "severe"]]
|
||||||
|
|
||||||
|
|
||||||
def __get_metrics_score__(ceiling, value):
|
def __get_metrics_score__(ceiling, value):
|
||||||
for i in reversed(METRICS_VIOLATION_SCORES):
|
for i in reversed(METRICS_VIOLATION_SCORES):
|
||||||
if value > ceiling * i[0]:
|
if value > ceiling * i[0]:
|
||||||
return i[1]
|
return i[1]
|
||||||
|
|
||||||
|
|
||||||
class MetricsOutput(Outputable):
|
class MetricsOutput(Outputable):
|
||||||
def __init__(self, metrics):
|
def __init__(self, metrics):
|
||||||
self.metrics = metrics
|
self.metrics = metrics
|
||||||
@ -58,11 +60,13 @@ class MetricsOutput(Outputable):
|
|||||||
|
|
||||||
if self.metrics.cyclomatic_complexity_density:
|
if self.metrics.cyclomatic_complexity_density:
|
||||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + ":")
|
print("\n" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + ":")
|
||||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True):
|
for i in sorted(
|
||||||
|
set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True
|
||||||
|
):
|
||||||
print(_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]))
|
print(_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]))
|
||||||
|
|
||||||
def output_html(self):
|
def output_html(self):
|
||||||
metrics_xml = "<div><div class=\"box\" id=\"metrics\">"
|
metrics_xml = '<div><div class="box" id="metrics">'
|
||||||
|
|
||||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||||
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
|
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
|
||||||
@ -70,25 +74,41 @@ class MetricsOutput(Outputable):
|
|||||||
if self.metrics.eloc:
|
if self.metrics.eloc:
|
||||||
metrics_xml += "<div><h4>" + _(ELOC_INFO_TEXT) + ".</h4>"
|
metrics_xml += "<div><h4>" + _(ELOC_INFO_TEXT) + ".</h4>"
|
||||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True)):
|
for num, i in enumerate(sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True)):
|
||||||
metrics_xml += "<div class=\"" + __get_metrics_score__(__metric_eloc__[FileDiff.get_extension(i[1])], i[0]) + \
|
metrics_xml += (
|
||||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
'<div class="'
|
||||||
_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])) + "</div>"
|
+ __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>"
|
metrics_xml += "</div>"
|
||||||
|
|
||||||
if self.metrics.cyclomatic_complexity:
|
if self.metrics.cyclomatic_complexity:
|
||||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_TEXT) + "</h4>"
|
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_TEXT) + "</h4>"
|
||||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True)):
|
for num, i in enumerate(
|
||||||
metrics_xml += "<div class=\"" + __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, i[0]) + \
|
sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True)
|
||||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
):
|
||||||
_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])) + "</div>"
|
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>"
|
metrics_xml += "</div>"
|
||||||
|
|
||||||
if self.metrics.cyclomatic_complexity_density:
|
if self.metrics.cyclomatic_complexity_density:
|
||||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + "</h4>"
|
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + "</h4>"
|
||||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True)):
|
for num, i in enumerate(
|
||||||
metrics_xml += "<div class=\"" + __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD, i[0]) + \
|
sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True)
|
||||||
(" odd\">" if num % 2 == 1 else "\">") + \
|
):
|
||||||
_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]) + "</div>"
|
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>"
|
||||||
|
|
||||||
metrics_xml += "</div></div>"
|
metrics_xml += "</div></div>"
|
||||||
@ -96,15 +116,15 @@ class MetricsOutput(Outputable):
|
|||||||
|
|
||||||
def output_json(self):
|
def output_json(self):
|
||||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||||
print(",\n\t\t\"metrics\": {\n\t\t\t\"message\": \"" + _(METRICS_MISSING_INFO_TEXT) + "\"\n\t\t}", end="")
|
print(',\n\t\t"metrics": {\n\t\t\t"message": "' + _(METRICS_MISSING_INFO_TEXT) + '"\n\t\t}', end="")
|
||||||
else:
|
else:
|
||||||
eloc_json = ""
|
eloc_json = ""
|
||||||
|
|
||||||
if self.metrics.eloc:
|
if self.metrics.eloc:
|
||||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
||||||
eloc_json += "{\n\t\t\t\t\"type\": \"estimated-lines-of-code\",\n"
|
eloc_json += '{\n\t\t\t\t"type": "estimated-lines-of-code",\n'
|
||||||
eloc_json += "\t\t\t\t\"file_name\": \"" + i[1] + "\",\n"
|
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||||
eloc_json += "\t\t\t\t\"value\": " + str(i[0]) + "\n"
|
eloc_json += '\t\t\t\t"value": ' + str(i[0]) + "\n"
|
||||||
eloc_json += "\t\t\t},"
|
eloc_json += "\t\t\t},"
|
||||||
else:
|
else:
|
||||||
if not self.metrics.cyclomatic_complexity:
|
if not self.metrics.cyclomatic_complexity:
|
||||||
@ -112,24 +132,27 @@ class MetricsOutput(Outputable):
|
|||||||
|
|
||||||
if self.metrics.cyclomatic_complexity:
|
if self.metrics.cyclomatic_complexity:
|
||||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
||||||
eloc_json += "{\n\t\t\t\t\"type\": \"cyclomatic-complexity\",\n"
|
eloc_json += '{\n\t\t\t\t"type": "cyclomatic-complexity",\n'
|
||||||
eloc_json += "\t\t\t\t\"file_name\": \"" + i[1] + "\",\n"
|
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||||
eloc_json += "\t\t\t\t\"value\": " + str(i[0]) + "\n"
|
eloc_json += '\t\t\t\t"value": ' + str(i[0]) + "\n"
|
||||||
eloc_json += "\t\t\t},"
|
eloc_json += "\t\t\t},"
|
||||||
else:
|
else:
|
||||||
if not self.metrics.cyclomatic_complexity_density:
|
if not self.metrics.cyclomatic_complexity_density:
|
||||||
eloc_json = eloc_json[:-1]
|
eloc_json = eloc_json[:-1]
|
||||||
|
|
||||||
if self.metrics.cyclomatic_complexity_density:
|
if self.metrics.cyclomatic_complexity_density:
|
||||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True):
|
for i in sorted(
|
||||||
eloc_json += "{\n\t\t\t\t\"type\": \"cyclomatic-complexity-density\",\n"
|
set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True
|
||||||
eloc_json += "\t\t\t\t\"file_name\": \"" + i[1] + "\",\n"
|
):
|
||||||
eloc_json += "\t\t\t\t\"value\": {0:.3f}\n".format(i[0])
|
eloc_json += '{\n\t\t\t\t"type": "cyclomatic-complexity-density",\n'
|
||||||
|
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||||
|
eloc_json += '\t\t\t\t"value": {0:.3f}\n'.format(i[0])
|
||||||
eloc_json += "\t\t\t},"
|
eloc_json += "\t\t\t},"
|
||||||
else:
|
else:
|
||||||
eloc_json = eloc_json[:-1]
|
eloc_json = eloc_json[:-1]
|
||||||
|
|
||||||
print(",\n\t\t\"metrics\": {\n\t\t\t\"violations\": [\n\t\t\t" + eloc_json + "]\n\t\t}", end="")
|
print(',\n\t\t"metrics": {\n\t\t\t"violations": [\n\t\t\t' + eloc_json + "]\n\t\t}", end="")
|
||||||
|
|
||||||
def output_xml(self):
|
def output_xml(self):
|
||||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||||
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
|
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
|
||||||
@ -151,7 +174,9 @@ class MetricsOutput(Outputable):
|
|||||||
eloc_xml += "\t\t\t</cyclomatic-complexity>\n"
|
eloc_xml += "\t\t\t</cyclomatic-complexity>\n"
|
||||||
|
|
||||||
if self.metrics.cyclomatic_complexity_density:
|
if self.metrics.cyclomatic_complexity_density:
|
||||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True):
|
for i in sorted(
|
||||||
|
set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True
|
||||||
|
):
|
||||||
eloc_xml += "\t\t\t<cyclomatic-complexity-density>\n"
|
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<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\t<value>{0:.3f}</value>\n".format(i[0])
|
||||||
|
@ -18,21 +18,22 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from .. import format
|
from .. import format
|
||||||
|
|
||||||
|
|
||||||
class Outputable(object):
|
class Outputable(object):
|
||||||
def output_html(self):
|
def output_html(self):
|
||||||
raise NotImplementedError(_("HTML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
raise NotImplementedError(_("HTML output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||||
|
|
||||||
def output_json(self):
|
def output_json(self):
|
||||||
raise NotImplementedError(_("JSON output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
raise NotImplementedError(_("JSON output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||||
|
|
||||||
def output_text(self):
|
def output_text(self):
|
||||||
raise NotImplementedError(_("Text output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
raise NotImplementedError(_("Text output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||||
|
|
||||||
def output_xml(self):
|
def output_xml(self):
|
||||||
raise NotImplementedError(_("XML output not yet supported in") + " \"" + self.__class__.__name__ + "\".")
|
raise NotImplementedError(_("XML output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||||
|
|
||||||
|
|
||||||
def output(outputable):
|
def output(outputable):
|
||||||
if format.get_selected() == "html" or format.get_selected() == "htmlembedded":
|
if format.get_selected() == "html" or format.get_selected() == "htmlembedded":
|
||||||
|
@ -18,18 +18,20 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
from .. import format, gravatar, terminal
|
from .. import format, gravatar, terminal
|
||||||
from .. import responsibilities as resp
|
from .. import responsibilities as resp
|
||||||
from .outputable import Outputable
|
from .outputable import Outputable
|
||||||
|
|
||||||
RESPONSIBILITIES_INFO_TEXT = N_("The following responsibilities, by author, were found in the current "
|
RESPONSIBILITIES_INFO_TEXT = N_(
|
||||||
|
"The following responsibilities, by author, were found in the current "
|
||||||
"revision of the repository (comments are excluded from the line count, "
|
"revision of the repository (comments are excluded from the line count, "
|
||||||
"if possible)")
|
"if possible)"
|
||||||
|
)
|
||||||
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
|
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
|
||||||
|
|
||||||
|
|
||||||
class ResponsibilitiesOutput(Outputable):
|
class ResponsibilitiesOutput(Outputable):
|
||||||
def __init__(self, changes, blame):
|
def __init__(self, changes, blame):
|
||||||
self.changes = changes
|
self.changes = changes
|
||||||
@ -56,7 +58,7 @@ class ResponsibilitiesOutput(Outputable):
|
|||||||
break
|
break
|
||||||
|
|
||||||
def output_html(self):
|
def output_html(self):
|
||||||
resp_xml = "<div><div class=\"box\" id=\"responsibilities\">"
|
resp_xml = '<div><div class="box" id="responsibilities">'
|
||||||
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
||||||
|
|
||||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||||
@ -67,14 +69,16 @@ class ResponsibilitiesOutput(Outputable):
|
|||||||
|
|
||||||
if format.get_selected() == "html":
|
if format.get_selected() == "html":
|
||||||
author_email = self.changes.get_latest_email_by_author(i)
|
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),
|
resp_xml += '<h3><img src="{0}"/>{1} {2}</h3>'.format(
|
||||||
i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
gravatar.get_url(author_email, size=32), i, _(MOSTLY_RESPONSIBLE_FOR_TEXT)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
||||||
|
|
||||||
for j, entry in enumerate(responsibilities):
|
for j, entry in enumerate(responsibilities):
|
||||||
resp_xml += "<div" + (" class=\"odd\">" if j % 2 == 1 else ">") + entry[1] + \
|
resp_xml += (
|
||||||
" (" + str(entry[0]) + " eloc)</div>"
|
"<div" + (' class="odd">' if j % 2 == 1 else ">") + entry[1] + " (" + str(entry[0]) + " eloc)</div>"
|
||||||
|
)
|
||||||
if j >= 9:
|
if j >= 9:
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -83,7 +87,7 @@ class ResponsibilitiesOutput(Outputable):
|
|||||||
print(resp_xml)
|
print(resp_xml)
|
||||||
|
|
||||||
def output_json(self):
|
def output_json(self):
|
||||||
message_json = "\t\t\t\"message\": \"" + _(RESPONSIBILITIES_INFO_TEXT) + "\",\n"
|
message_json = '\t\t\t"message": "' + _(RESPONSIBILITIES_INFO_TEXT) + '",\n'
|
||||||
resp_json = ""
|
resp_json = ""
|
||||||
|
|
||||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||||
@ -93,15 +97,15 @@ class ResponsibilitiesOutput(Outputable):
|
|||||||
author_email = self.changes.get_latest_email_by_author(i)
|
author_email = self.changes.get_latest_email_by_author(i)
|
||||||
|
|
||||||
resp_json += "{\n"
|
resp_json += "{\n"
|
||||||
resp_json += "\t\t\t\t\"name\": \"" + i + "\",\n"
|
resp_json += '\t\t\t\t"name": "' + i + '",\n'
|
||||||
resp_json += "\t\t\t\t\"email\": \"" + author_email + "\",\n"
|
resp_json += '\t\t\t\t"email": "' + author_email + '",\n'
|
||||||
resp_json += "\t\t\t\t\"gravatar\": \"" + gravatar.get_url(author_email) + "\",\n"
|
resp_json += '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||||
resp_json += "\t\t\t\t\"files\": [\n\t\t\t\t"
|
resp_json += '\t\t\t\t"files": [\n\t\t\t\t'
|
||||||
|
|
||||||
for j, entry in enumerate(responsibilities):
|
for j, entry in enumerate(responsibilities):
|
||||||
resp_json += "{\n"
|
resp_json += "{\n"
|
||||||
resp_json += "\t\t\t\t\t\"name\": \"" + entry[1] + "\",\n"
|
resp_json += '\t\t\t\t\t"name": "' + entry[1] + '",\n'
|
||||||
resp_json += "\t\t\t\t\t\"rows\": " + str(entry[0]) + "\n"
|
resp_json += '\t\t\t\t\t"rows": ' + str(entry[0]) + "\n"
|
||||||
resp_json += "\t\t\t\t},"
|
resp_json += "\t\t\t\t},"
|
||||||
|
|
||||||
if j >= 9:
|
if j >= 9:
|
||||||
@ -111,7 +115,7 @@ class ResponsibilitiesOutput(Outputable):
|
|||||||
resp_json += "]\n\t\t\t},"
|
resp_json += "]\n\t\t\t},"
|
||||||
|
|
||||||
resp_json = resp_json[:-1]
|
resp_json = resp_json[:-1]
|
||||||
print(",\n\t\t\"responsibilities\": {\n" + message_json + "\t\t\t\"authors\": [\n\t\t\t" + resp_json + "]\n\t\t}", end="")
|
print(',\n\t\t"responsibilities": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + resp_json + "]\n\t\t}", end="")
|
||||||
|
|
||||||
def output_xml(self):
|
def output_xml(self):
|
||||||
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
|
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..localization import N_
|
from ..localization import N_
|
||||||
from .. import format, gravatar, terminal, timeline
|
from .. import format, gravatar, terminal, timeline
|
||||||
@ -27,6 +26,7 @@ from .outputable import Outputable
|
|||||||
TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from the repository")
|
TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from the repository")
|
||||||
MODIFIED_ROWS_TEXT = N_("Modified Rows:")
|
MODIFIED_ROWS_TEXT = N_("Modified Rows:")
|
||||||
|
|
||||||
|
|
||||||
def __output_row__text__(timeline_data, periods, names):
|
def __output_row__text__(timeline_data, periods, names):
|
||||||
print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
|
print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
|
||||||
|
|
||||||
@ -42,9 +42,13 @@ def __output_row__text__(timeline_data, periods, names):
|
|||||||
for period in periods:
|
for period in periods:
|
||||||
multiplier = timeline_data.get_multiplier(period, 9)
|
multiplier = timeline_data.get_multiplier(period, 9)
|
||||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||||
signs_str = (signs[1] * "-" + signs[0] * "+")
|
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||||
print (("." if timeline_data.is_author_in_period(period, name[0]) and
|
print(
|
||||||
len(signs_str) == 0 else signs_str).rjust(10), end=" ")
|
("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str).rjust(
|
||||||
|
10
|
||||||
|
),
|
||||||
|
end=" ",
|
||||||
|
)
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
print(terminal.__bold__ + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
|
print(terminal.__bold__ + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
|
||||||
@ -52,15 +56,16 @@ def __output_row__text__(timeline_data, periods, names):
|
|||||||
for period in periods:
|
for period in periods:
|
||||||
total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
|
total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
|
||||||
|
|
||||||
if hasattr(total_changes, 'decode'):
|
if hasattr(total_changes, "decode"):
|
||||||
total_changes = total_changes.decode("utf-8", "replace")
|
total_changes = total_changes.decode("utf-8", "replace")
|
||||||
|
|
||||||
print(terminal.rjust(total_changes, 10), end=" ")
|
print(terminal.rjust(total_changes, 10), end=" ")
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
||||||
def __output_row__html__(timeline_data, periods, names):
|
def __output_row__html__(timeline_data, periods, names):
|
||||||
timeline_xml = "<table class=\"git full\"><thead><tr><th>" + _("Author") + "</th>"
|
timeline_xml = '<table class="git full"><thead><tr><th>' + _("Author") + "</th>"
|
||||||
|
|
||||||
for period in periods:
|
for period in periods:
|
||||||
timeline_xml += "<th>" + str(period) + "</th>"
|
timeline_xml += "<th>" + str(period) + "</th>"
|
||||||
@ -70,19 +75,21 @@ def __output_row__html__(timeline_data, periods, names):
|
|||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||||
timeline_xml += "<tr" + (" class=\"odd\">" if i % 2 == 1 else ">")
|
timeline_xml += "<tr" + (' class="odd">' if i % 2 == 1 else ">")
|
||||||
|
|
||||||
if format.get_selected() == "html":
|
if format.get_selected() == "html":
|
||||||
timeline_xml += "<td><img src=\"{0}\"/>{1}</td>".format(gravatar.get_url(name[1]), name[0])
|
timeline_xml += '<td><img src="{0}"/>{1}</td>'.format(gravatar.get_url(name[1]), name[0])
|
||||||
else:
|
else:
|
||||||
timeline_xml += "<td>" + name[0] + "</td>"
|
timeline_xml += "<td>" + name[0] + "</td>"
|
||||||
|
|
||||||
for period in periods:
|
for period in periods:
|
||||||
multiplier = timeline_data.get_multiplier(period, 18)
|
multiplier = timeline_data.get_multiplier(period, 18)
|
||||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
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>")
|
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>" + (
|
||||||
|
"." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str
|
||||||
|
)
|
||||||
timeline_xml += "</td>"
|
timeline_xml += "</td>"
|
||||||
timeline_xml += "</tr>"
|
timeline_xml += "</tr>"
|
||||||
i = i + 1
|
i = i + 1
|
||||||
@ -96,6 +103,7 @@ def __output_row__html__(timeline_data, periods, names):
|
|||||||
timeline_xml += "</tr></tfoot></tbody></table>"
|
timeline_xml += "</tr></tfoot></tbody></table>"
|
||||||
print(timeline_xml)
|
print(timeline_xml)
|
||||||
|
|
||||||
|
|
||||||
class TimelineOutput(Outputable):
|
class TimelineOutput(Outputable):
|
||||||
def __init__(self, changes, useweeks):
|
def __init__(self, changes, useweeks):
|
||||||
self.changes = changes
|
self.changes = changes
|
||||||
@ -122,7 +130,7 @@ class TimelineOutput(Outputable):
|
|||||||
names = timeline_data.get_authors()
|
names = timeline_data.get_authors()
|
||||||
max_periods_per_row = 8
|
max_periods_per_row = 8
|
||||||
|
|
||||||
timeline_xml = "<div><div id=\"timeline\" class=\"box\">"
|
timeline_xml = '<div><div id="timeline" class="box">'
|
||||||
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
|
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
|
||||||
print(timeline_xml)
|
print(timeline_xml)
|
||||||
|
|
||||||
@ -134,49 +142,50 @@ class TimelineOutput(Outputable):
|
|||||||
|
|
||||||
def output_json(self):
|
def output_json(self):
|
||||||
if self.changes.get_commits():
|
if self.changes.get_commits():
|
||||||
message_json = "\t\t\t\"message\": \"" + _(TIMELINE_INFO_TEXT) + "\",\n"
|
message_json = '\t\t\t"message": "' + _(TIMELINE_INFO_TEXT) + '",\n'
|
||||||
timeline_json = ""
|
timeline_json = ""
|
||||||
periods_json = "\t\t\t\"period_length\": \"{0}\",\n".format("week" if self.useweeks else "month")
|
periods_json = '\t\t\t"period_length": "{0}",\n'.format("week" if self.useweeks else "month")
|
||||||
periods_json += "\t\t\t\"periods\": [\n\t\t\t"
|
periods_json += '\t\t\t"periods": [\n\t\t\t'
|
||||||
|
|
||||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||||
periods = timeline_data.get_periods()
|
periods = timeline_data.get_periods()
|
||||||
names = timeline_data.get_authors()
|
names = timeline_data.get_authors()
|
||||||
|
|
||||||
for period in periods:
|
for period in periods:
|
||||||
name_json = "\t\t\t\t\"name\": \"" + str(period) + "\",\n"
|
name_json = '\t\t\t\t"name": "' + str(period) + '",\n'
|
||||||
authors_json = "\t\t\t\t\"authors\": [\n\t\t\t\t"
|
authors_json = '\t\t\t\t"authors": [\n\t\t\t\t'
|
||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
if timeline_data.is_author_in_period(period, name[0]):
|
if timeline_data.is_author_in_period(period, name[0]):
|
||||||
multiplier = timeline_data.get_multiplier(period, 24)
|
multiplier = timeline_data.get_multiplier(period, 24)
|
||||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||||
signs_str = (signs[1] * "-" + signs[0] * "+")
|
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||||
|
|
||||||
if len(signs_str) == 0:
|
if len(signs_str) == 0:
|
||||||
signs_str = "."
|
signs_str = "."
|
||||||
|
|
||||||
authors_json += "{\n\t\t\t\t\t\"name\": \"" + name[0] + "\",\n"
|
authors_json += '{\n\t\t\t\t\t"name": "' + name[0] + '",\n'
|
||||||
authors_json += "\t\t\t\t\t\"email\": \"" + name[1] + "\",\n"
|
authors_json += '\t\t\t\t\t"email": "' + name[1] + '",\n'
|
||||||
authors_json += "\t\t\t\t\t\"gravatar\": \"" + gravatar.get_url(name[1]) + "\",\n"
|
authors_json += '\t\t\t\t\t"gravatar": "' + gravatar.get_url(name[1]) + '",\n'
|
||||||
authors_json += "\t\t\t\t\t\"work\": \"" + signs_str + "\"\n\t\t\t\t},"
|
authors_json += '\t\t\t\t\t"work": "' + signs_str + '"\n\t\t\t\t},'
|
||||||
else:
|
else:
|
||||||
authors_json = authors_json[:-1]
|
authors_json = authors_json[:-1]
|
||||||
|
|
||||||
authors_json += "],\n"
|
authors_json += "],\n"
|
||||||
modified_rows_json = "\t\t\t\t\"modified_rows\": " + \
|
modified_rows_json = (
|
||||||
str(timeline_data.get_total_changes_in_period(period)[2]) + "\n"
|
'\t\t\t\t"modified_rows": ' + str(timeline_data.get_total_changes_in_period(period)[2]) + "\n"
|
||||||
|
)
|
||||||
timeline_json += "{\n" + name_json + authors_json + modified_rows_json + "\t\t\t},"
|
timeline_json += "{\n" + name_json + authors_json + modified_rows_json + "\t\t\t},"
|
||||||
else:
|
else:
|
||||||
timeline_json = timeline_json[:-1]
|
timeline_json = timeline_json[:-1]
|
||||||
|
|
||||||
print(",\n\t\t\"timeline\": {\n" + message_json + periods_json + timeline_json + "]\n\t\t}", end="")
|
print(',\n\t\t"timeline": {\n' + message_json + periods_json + timeline_json + "]\n\t\t}", end="")
|
||||||
|
|
||||||
def output_xml(self):
|
def output_xml(self):
|
||||||
if self.changes.get_commits():
|
if self.changes.get_commits():
|
||||||
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
|
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
|
||||||
timeline_xml = ""
|
timeline_xml = ""
|
||||||
periods_xml = "\t\t<periods length=\"{0}\">\n".format("week" if self.useweeks else "month")
|
periods_xml = '\t\t<periods length="{0}">\n'.format("week" if self.useweeks else "month")
|
||||||
|
|
||||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||||
periods = timeline_data.get_periods()
|
periods = timeline_data.get_periods()
|
||||||
@ -190,7 +199,7 @@ class TimelineOutput(Outputable):
|
|||||||
if timeline_data.is_author_in_period(period, name[0]):
|
if timeline_data.is_author_in_period(period, name[0]):
|
||||||
multiplier = timeline_data.get_multiplier(period, 24)
|
multiplier = timeline_data.get_multiplier(period, 24)
|
||||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||||
signs_str = (signs[1] * "-" + signs[0] * "+")
|
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||||
|
|
||||||
if len(signs_str) == 0:
|
if len(signs_str) == 0:
|
||||||
signs_str = "."
|
signs_str = "."
|
||||||
@ -201,8 +210,11 @@ class TimelineOutput(Outputable):
|
|||||||
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\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
|
||||||
|
|
||||||
authors_xml += "\t\t\t\t</authors>\n"
|
authors_xml += "\t\t\t\t</authors>\n"
|
||||||
modified_rows_xml = "\t\t\t\t<modified_rows>" + \
|
modified_rows_xml = (
|
||||||
str(timeline_data.get_total_changes_in_period(period)[2]) + "</modified_rows>\n"
|
"\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"
|
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>")
|
print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")
|
||||||
|
@ -18,11 +18,10 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ResponsibiltyEntry(object):
|
class ResponsibiltyEntry(object):
|
||||||
blames = {}
|
blames = {}
|
||||||
|
|
||||||
|
|
||||||
class Responsibilities(object):
|
class Responsibilities(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(blame, author_name):
|
def get(blame, author_name):
|
||||||
|
@ -29,6 +29,7 @@ __normal__ = "\033[0;0m"
|
|||||||
|
|
||||||
DEFAULT_TERMINAL_SIZE = (80, 25)
|
DEFAULT_TERMINAL_SIZE = (80, 25)
|
||||||
|
|
||||||
|
|
||||||
def __get_size_windows__():
|
def __get_size_windows__():
|
||||||
res = None
|
res = None
|
||||||
try:
|
try:
|
||||||
@ -42,6 +43,7 @@ def __get_size_windows__():
|
|||||||
|
|
||||||
if res:
|
if res:
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
(_, _, _, _, _, left, top, right, bottom, _, _) = struct.unpack("hhhhHhhhhhh", csbi.raw)
|
(_, _, _, _, _, left, top, right, bottom, _, _) = struct.unpack("hhhhHhhhhhh", csbi.raw)
|
||||||
sizex = right - left + 1
|
sizex = right - left + 1
|
||||||
sizey = bottom - top + 1
|
sizey = bottom - top + 1
|
||||||
@ -49,11 +51,13 @@ def __get_size_windows__():
|
|||||||
else:
|
else:
|
||||||
return DEFAULT_TERMINAL_SIZE
|
return DEFAULT_TERMINAL_SIZE
|
||||||
|
|
||||||
|
|
||||||
def __get_size_linux__():
|
def __get_size_linux__():
|
||||||
def ioctl_get_window_size(file_descriptor):
|
def ioctl_get_window_size(file_descriptor):
|
||||||
try:
|
try:
|
||||||
import fcntl, termios, struct
|
import fcntl, termios, struct
|
||||||
size = struct.unpack('hh', fcntl.ioctl(file_descriptor, termios.TIOCGWINSZ, "1234"))
|
|
||||||
|
size = struct.unpack("hh", fcntl.ioctl(file_descriptor, termios.TIOCGWINSZ, "1234"))
|
||||||
except:
|
except:
|
||||||
return DEFAULT_TERMINAL_SIZE
|
return DEFAULT_TERMINAL_SIZE
|
||||||
|
|
||||||
@ -76,9 +80,11 @@ def __get_size_linux__():
|
|||||||
|
|
||||||
return int(size[1]), int(size[0])
|
return int(size[1]), int(size[0])
|
||||||
|
|
||||||
|
|
||||||
def clear_row():
|
def clear_row():
|
||||||
print("\r", end="")
|
print("\r", end="")
|
||||||
|
|
||||||
|
|
||||||
def skip_escapes(skip):
|
def skip_escapes(skip):
|
||||||
if skip:
|
if skip:
|
||||||
global __bold__
|
global __bold__
|
||||||
@ -86,9 +92,11 @@ def skip_escapes(skip):
|
|||||||
__bold__ = ""
|
__bold__ = ""
|
||||||
__normal__ = ""
|
__normal__ = ""
|
||||||
|
|
||||||
|
|
||||||
def printb(string):
|
def printb(string):
|
||||||
print(__bold__ + string + __normal__)
|
print(__bold__ + string + __normal__)
|
||||||
|
|
||||||
|
|
||||||
def get_size():
|
def get_size():
|
||||||
width = 0
|
width = 0
|
||||||
height = 0
|
height = 0
|
||||||
@ -106,14 +114,17 @@ def get_size():
|
|||||||
|
|
||||||
return DEFAULT_TERMINAL_SIZE
|
return DEFAULT_TERMINAL_SIZE
|
||||||
|
|
||||||
|
|
||||||
def set_stdout_encoding():
|
def set_stdout_encoding():
|
||||||
if not sys.stdout.isatty() and sys.version_info < (3,):
|
if not sys.stdout.isatty() and sys.version_info < (3,):
|
||||||
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
|
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
|
||||||
|
|
||||||
|
|
||||||
def set_stdin_encoding():
|
def set_stdin_encoding():
|
||||||
if not sys.stdin.isatty() and sys.version_info < (3,):
|
if not sys.stdin.isatty() and sys.version_info < (3,):
|
||||||
sys.stdin = codecs.getreader("utf-8")(sys.stdin)
|
sys.stdin = codecs.getreader("utf-8")(sys.stdin)
|
||||||
|
|
||||||
|
|
||||||
def convert_command_line_to_utf8():
|
def convert_command_line_to_utf8():
|
||||||
try:
|
try:
|
||||||
argv = []
|
argv = []
|
||||||
@ -125,13 +136,20 @@ def convert_command_line_to_utf8():
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return sys.argv
|
return sys.argv
|
||||||
|
|
||||||
|
|
||||||
def check_terminal_encoding():
|
def check_terminal_encoding():
|
||||||
if sys.stdout.isatty() and (sys.stdout.encoding == None or sys.stdin.encoding == None):
|
if sys.stdout.isatty() and (sys.stdout.encoding is None or sys.stdin.encoding is None):
|
||||||
print(_("WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. "
|
print(
|
||||||
"The encoding can be configured with the environment variable 'PYTHONIOENCODING'."), file=sys.stderr)
|
_(
|
||||||
|
"WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. "
|
||||||
|
"The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
|
||||||
|
),
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_excess_column_count(string):
|
def get_excess_column_count(string):
|
||||||
width_mapping = {'F': 2, 'H': 1, 'W': 2, 'Na': 1, 'N': 1, 'A': 1}
|
width_mapping = {"F": 2, "H": 1, "W": 2, "Na": 1, "N": 1, "A": 1}
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
for i in string:
|
for i in string:
|
||||||
@ -140,12 +158,15 @@ def get_excess_column_count(string):
|
|||||||
|
|
||||||
return result - len(string)
|
return result - len(string)
|
||||||
|
|
||||||
|
|
||||||
def ljust(string, pad):
|
def ljust(string, pad):
|
||||||
return string.ljust(pad - get_excess_column_count(string))
|
return string.ljust(pad - get_excess_column_count(string))
|
||||||
|
|
||||||
|
|
||||||
def rjust(string, pad):
|
def rjust(string, pad):
|
||||||
return string.rjust(pad - get_excess_column_count(string))
|
return string.rjust(pad - get_excess_column_count(string))
|
||||||
|
|
||||||
|
|
||||||
def output_progress(text, pos, length):
|
def output_progress(text, pos, length):
|
||||||
if sys.stdout.isatty():
|
if sys.stdout.isatty():
|
||||||
(width, _unused) = get_size()
|
(width, _unused) = get_size()
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class TimelineData(object):
|
class TimelineData(object):
|
||||||
def __init__(self, changes, useweeks):
|
def __init__(self, changes, useweeks):
|
||||||
authordateinfo_list = sorted(changes.get_authordateinfo_list().items())
|
authordateinfo_list = sorted(changes.get_authordateinfo_list().items())
|
||||||
@ -37,7 +38,7 @@ class TimelineData(object):
|
|||||||
else:
|
else:
|
||||||
key = (i[0][1], i[0][0][0:7])
|
key = (i[0][1], i[0][0][0:7])
|
||||||
|
|
||||||
if self.entries.get(key, None) == None:
|
if self.entries.get(key, None) is None:
|
||||||
self.entries[key] = i[1]
|
self.entries[key] = i[1]
|
||||||
else:
|
else:
|
||||||
self.entries[key].insertions += i[1].insertions
|
self.entries[key].insertions += i[1].insertions
|
||||||
@ -49,12 +50,11 @@ class TimelineData(object):
|
|||||||
|
|
||||||
for author in self.get_authors():
|
for author in self.get_authors():
|
||||||
entry = self.entries.get((author[0], period), None)
|
entry = self.entries.get((author[0], period), None)
|
||||||
if entry != None:
|
if entry is not None:
|
||||||
total_insertions += entry.insertions
|
total_insertions += entry.insertions
|
||||||
total_deletions += entry.deletions
|
total_deletions += entry.deletions
|
||||||
|
|
||||||
self.total_changes_by_period[period] = (total_insertions, total_deletions,
|
self.total_changes_by_period[period] = (total_insertions, total_deletions, total_insertions + total_deletions)
|
||||||
total_insertions + total_deletions)
|
|
||||||
|
|
||||||
def get_periods(self):
|
def get_periods(self):
|
||||||
return sorted(set([i[1] for i in self.entries]))
|
return sorted(set([i[1] for i in self.entries]))
|
||||||
@ -91,7 +91,7 @@ class TimelineData(object):
|
|||||||
multiplier += 0.25
|
multiplier += 0.25
|
||||||
|
|
||||||
def is_author_in_period(self, period, author):
|
def is_author_in_period(self, period, author):
|
||||||
return self.entries.get((author, period), None) != None
|
return self.entries.get((author, period), None) is not None
|
||||||
|
|
||||||
def is_author_in_periods(self, periods, author):
|
def is_author_in_periods(self, periods, author):
|
||||||
for period in periods:
|
for period in periods:
|
||||||
|
@ -18,17 +18,21 @@
|
|||||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from . import localization
|
from . import localization
|
||||||
|
|
||||||
localization.init()
|
localization.init()
|
||||||
|
|
||||||
__version__ = "0.5.0dev"
|
__version__ = "0.5.0dev"
|
||||||
|
|
||||||
__doc__ = _("""Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
__doc__ = _(
|
||||||
|
"""Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
|
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
|
||||||
This is free software: you are free to change and redistribute it.
|
This is free software: you are free to change and redistribute it.
|
||||||
There is NO WARRANTY, to the extent permitted by law.
|
There is NO WARRANTY, to the extent permitted by law.
|
||||||
|
|
||||||
Written by Adam Waldenberg.""")
|
Written by Adam Waldenberg."""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def output():
|
def output():
|
||||||
print("gitinspector {0}\n".format(__version__) + __doc__)
|
print("gitinspector {0}\n".format(__version__) + __doc__)
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import unittest
|
import unittest
|
||||||
import gitinspector.comment
|
import gitinspector.comment
|
||||||
|
|
||||||
|
|
||||||
def __test_extension__(commented_file, extension):
|
def __test_extension__(commented_file, extension):
|
||||||
base = os.path.dirname(os.path.realpath(__file__))
|
base = os.path.dirname(os.path.realpath(__file__))
|
||||||
tex_file = open(base + commented_file, "r")
|
tex_file = open(base + commented_file, "r")
|
||||||
@ -38,11 +38,13 @@ def __test_extension__(commented_file, extension):
|
|||||||
|
|
||||||
return comment_counter
|
return comment_counter
|
||||||
|
|
||||||
|
|
||||||
class TexFileTest(unittest.TestCase):
|
class TexFileTest(unittest.TestCase):
|
||||||
def test(self):
|
def test(self):
|
||||||
comment_counter = __test_extension__("/resources/commented_file.tex", "tex")
|
comment_counter = __test_extension__("/resources/commented_file.tex", "tex")
|
||||||
self.assertEqual(comment_counter, 30)
|
self.assertEqual(comment_counter, 30)
|
||||||
|
|
||||||
|
|
||||||
class CppFileTest(unittest.TestCase):
|
class CppFileTest(unittest.TestCase):
|
||||||
def test(self):
|
def test(self):
|
||||||
comment_counter = __test_extension__("/resources/commented_file.cpp", "cpp")
|
comment_counter = __test_extension__("/resources/commented_file.cpp", "cpp")
|
||||||
|
Loading…
Reference in New Issue
Block a user