зеркало из https://github.com/mozilla/bugbug.git
Gather more features about experiences (#467)
* Refactor things to avoid multiple sums and set updates * Store max and min values too for experiences * Store touched files and directories too as part of the commit data * Remove useless default value for files_modified_num * Use f-string instead of string concatenation for feature names * Add more features about experiences (average, maximum, minimum, number of elements) Fixes #370
This commit is contained in:
Родитель
24c805e64e
Коммит
72dab1a6d8
|
@ -438,25 +438,27 @@ class BugExtractor(BaseEstimator, TransformerMixin):
|
|||
else:
|
||||
bug["commits"] = []
|
||||
|
||||
for f in self.feature_extractors:
|
||||
res = f(
|
||||
for feature_extractor in self.feature_extractors:
|
||||
res = feature_extractor(
|
||||
bug,
|
||||
reporter_experience=reporter_experience_map[bug["creator"]],
|
||||
author_ids=author_ids,
|
||||
)
|
||||
|
||||
feature_extractor_name = feature_extractor.__class__.__name__
|
||||
|
||||
if res is None:
|
||||
continue
|
||||
|
||||
if isinstance(res, list):
|
||||
for item in res:
|
||||
data[f.__class__.__name__ + "-" + item] = "True"
|
||||
data[f"{feature_extractor_name}-{item}"] = "True"
|
||||
continue
|
||||
|
||||
if isinstance(res, bool):
|
||||
res = str(res)
|
||||
|
||||
data[f.__class__.__name__] = res
|
||||
data[feature_extractor_name] = res
|
||||
|
||||
reporter_experience_map[bug["creator"]] += 1
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
import pandas as pd
|
||||
from sklearn.base import BaseEstimator, TransformerMixin
|
||||
|
||||
EXPERIENCE_TIMESPAN = 90
|
||||
EXPERIENCE_TIMESPAN_TEXT = f"{EXPERIENCE_TIMESPAN}_days"
|
||||
|
||||
|
||||
class files_modified_num(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
|
@ -32,49 +35,48 @@ class test_deleted(object):
|
|||
return commit["test_deleted"]
|
||||
|
||||
|
||||
class author_experience(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["author_experience"]
|
||||
|
||||
|
||||
class author_experience_90_days(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["author_experience_90_days"]
|
||||
|
||||
|
||||
def get_exps(exp_type, commit):
|
||||
suffix = "experience" if exp_type == "reviewer" else "touched_prev"
|
||||
|
||||
val_total = commit[f"{exp_type}_{suffix}"]
|
||||
val_timespan = commit[f"{exp_type}_{suffix}_{EXPERIENCE_TIMESPAN_TEXT}"]
|
||||
|
||||
items_key = f"{exp_type}s" if exp_type != "directory" else "directories"
|
||||
items_num = len(commit[items_key])
|
||||
|
||||
return {
|
||||
"num": items_num,
|
||||
"sum": val_total["sum"],
|
||||
"max": val_total["max"],
|
||||
"min": val_total["min"],
|
||||
"avg": val_total["sum"] / items_num if items_num > 0 else 0,
|
||||
f"sum_{EXPERIENCE_TIMESPAN_TEXT}": val_timespan["sum"],
|
||||
f"max_{EXPERIENCE_TIMESPAN_TEXT}": val_timespan["max"],
|
||||
f"min_{EXPERIENCE_TIMESPAN_TEXT}": val_timespan["min"],
|
||||
f"avg_{EXPERIENCE_TIMESPAN_TEXT}": val_timespan["sum"] / items_num
|
||||
if items_num > 0
|
||||
else 0,
|
||||
}
|
||||
|
||||
|
||||
class author_experience(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return {
|
||||
"total": commit["author_experience"],
|
||||
EXPERIENCE_TIMESPAN_TEXT: commit[
|
||||
f"author_experience_{EXPERIENCE_TIMESPAN_TEXT}"
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class reviewer_experience(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["reviewer_experience"]
|
||||
|
||||
|
||||
class reviewer_experience_90_days(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["reviewer_experience_90_days"]
|
||||
|
||||
|
||||
class components_touched_prev(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["components_touched_prev"]
|
||||
|
||||
|
||||
class components_touched_prev_90_days(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["components_touched_prev_90_days"]
|
||||
|
||||
|
||||
class files_touched_prev(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["files_touched_prev"]
|
||||
|
||||
|
||||
class files_touched_prev_90_days(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["files_touched_prev_90_days"]
|
||||
|
||||
|
||||
class types(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["types"]
|
||||
return get_exps("reviewer", commit)
|
||||
|
||||
|
||||
class components(object):
|
||||
|
@ -82,9 +84,29 @@ class components(object):
|
|||
return commit["components"]
|
||||
|
||||
|
||||
class number_of_reviewers(object):
|
||||
class component_touched_prev(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return len(commit["reviewers"])
|
||||
return get_exps("component", commit)
|
||||
|
||||
|
||||
class directories(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["directories"]
|
||||
|
||||
|
||||
class directory_touched_prev(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return get_exps("directory", commit)
|
||||
|
||||
|
||||
class file_touched_prev(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return get_exps("file", commit)
|
||||
|
||||
|
||||
class types(object):
|
||||
def __call__(self, commit, **kwargs):
|
||||
return commit["types"]
|
||||
|
||||
|
||||
class CommitExtractor(BaseEstimator, TransformerMixin):
|
||||
|
@ -101,21 +123,28 @@ class CommitExtractor(BaseEstimator, TransformerMixin):
|
|||
for commit in commits:
|
||||
data = {}
|
||||
|
||||
for f in self.feature_extractors:
|
||||
res = f(commit)
|
||||
for feature_extractor in self.feature_extractors:
|
||||
res = feature_extractor(commit)
|
||||
|
||||
feature_extractor_name = feature_extractor.__class__.__name__
|
||||
|
||||
if res is None:
|
||||
continue
|
||||
|
||||
if isinstance(res, dict):
|
||||
for key, value in res.items():
|
||||
data[f"{feature_extractor_name}_{key}"] = value
|
||||
continue
|
||||
|
||||
if isinstance(res, list):
|
||||
for item in res:
|
||||
data[f.__class__.__name__ + "-" + item] = "True"
|
||||
data[f"{feature_extractor_name}-{item}"] = "True"
|
||||
continue
|
||||
|
||||
if isinstance(res, bool):
|
||||
res = str(res)
|
||||
|
||||
data[f.__class__.__name__] = res
|
||||
data[feature_extractor_name] = res
|
||||
|
||||
# TODO: Try simply using all possible fields instead of extracting features manually.
|
||||
|
||||
|
|
|
@ -28,16 +28,12 @@ class BackoutModel(CommitModel):
|
|||
commit_features.deleted(),
|
||||
commit_features.test_deleted(),
|
||||
commit_features.author_experience(),
|
||||
commit_features.author_experience_90_days(),
|
||||
commit_features.reviewer_experience(),
|
||||
commit_features.reviewer_experience_90_days(),
|
||||
commit_features.components_touched_prev(),
|
||||
commit_features.components_touched_prev_90_days(),
|
||||
commit_features.files_touched_prev(),
|
||||
commit_features.files_touched_prev_90_days(),
|
||||
commit_features.component_touched_prev(),
|
||||
commit_features.directory_touched_prev(),
|
||||
commit_features.file_touched_prev(),
|
||||
commit_features.types(),
|
||||
commit_features.components(),
|
||||
commit_features.number_of_reviewers(),
|
||||
]
|
||||
|
||||
cleanup_functions = [
|
||||
|
|
|
@ -96,6 +96,20 @@ def get_reviewers(commit_description, flag_re=None):
|
|||
return res
|
||||
|
||||
|
||||
def get_directories(files):
|
||||
if isinstance(files, str):
|
||||
files = [files]
|
||||
|
||||
directories = set()
|
||||
for path in files:
|
||||
path_dirs = (
|
||||
os.path.dirname(path).split("/", 2)[:2] if os.path.dirname(path) else []
|
||||
)
|
||||
if path_dirs:
|
||||
directories.update([path_dirs[0], "/".join(path_dirs)])
|
||||
return list(directories)
|
||||
|
||||
|
||||
def get_commits():
|
||||
return db.read(COMMITS_DB)
|
||||
|
||||
|
@ -141,50 +155,36 @@ def _transform(commit):
|
|||
"test_added": 0,
|
||||
"deleted": 0,
|
||||
"test_deleted": 0,
|
||||
"files_modified_num": 0,
|
||||
"types": set(),
|
||||
"components": list(),
|
||||
"author_experience": experiences_by_commit["total"]["author"][commit.node],
|
||||
f"author_experience_{EXPERIENCE_TIMESPAN_TEXT}": experiences_by_commit[
|
||||
EXPERIENCE_TIMESPAN_TEXT
|
||||
]["author"][commit.node],
|
||||
"reviewer_experience": experiences_by_commit["total"]["reviewer"][commit.node],
|
||||
f"reviewer_experience_{EXPERIENCE_TIMESPAN_TEXT}": experiences_by_commit[
|
||||
EXPERIENCE_TIMESPAN_TEXT
|
||||
]["reviewer"][commit.node],
|
||||
"author_email": commit.author_email.decode("utf-8"),
|
||||
"components_touched_prev": experiences_by_commit["total"]["component"][
|
||||
commit.node
|
||||
],
|
||||
f"components_touched_prev_{EXPERIENCE_TIMESPAN_TEXT}": experiences_by_commit[
|
||||
EXPERIENCE_TIMESPAN_TEXT
|
||||
]["component"][commit.node],
|
||||
"files_touched_prev": experiences_by_commit["total"]["file"][commit.node],
|
||||
f"files_touched_prev_{EXPERIENCE_TIMESPAN_TEXT}": experiences_by_commit[
|
||||
EXPERIENCE_TIMESPAN_TEXT
|
||||
]["file"][commit.node],
|
||||
"directories_touched_prev": experiences_by_commit["total"]["directory"][
|
||||
commit.node
|
||||
],
|
||||
f"directories_touched_prev_{EXPERIENCE_TIMESPAN_TEXT}": experiences_by_commit[
|
||||
EXPERIENCE_TIMESPAN_TEXT
|
||||
]["directory"][commit.node],
|
||||
}
|
||||
|
||||
for experience_type in ["author", "reviewer", "file", "directory", "component"]:
|
||||
suffix = (
|
||||
"experience"
|
||||
if experience_type in ["author", "reviewer"]
|
||||
else "touched_prev"
|
||||
)
|
||||
|
||||
obj[f"{experience_type}_{suffix}"] = experiences_by_commit["total"][
|
||||
experience_type
|
||||
][commit.node]
|
||||
obj[
|
||||
f"{experience_type}_{suffix}_{EXPERIENCE_TIMESPAN_TEXT}"
|
||||
] = experiences_by_commit[EXPERIENCE_TIMESPAN_TEXT][experience_type][
|
||||
commit.node
|
||||
]
|
||||
|
||||
sizes = []
|
||||
|
||||
patch = HG.export(revs=[commit.node.encode("ascii")], git=True)
|
||||
patch_data = rs_parsepatch.get_counts(patch)
|
||||
components = set()
|
||||
for stats in patch_data:
|
||||
if stats["binary"]:
|
||||
obj["types"].add("binary")
|
||||
continue
|
||||
|
||||
path = stats["filename"]
|
||||
component = path_to_component.get(path)
|
||||
if component:
|
||||
components.add(component)
|
||||
|
||||
if is_test(path):
|
||||
obj["test_added"] += stats["added_lines"]
|
||||
|
@ -239,7 +239,15 @@ def _transform(commit):
|
|||
# Covert to a list, as a set is not JSON-serializable.
|
||||
obj["types"] = list(obj["types"])
|
||||
|
||||
obj["components"] = list(components)
|
||||
obj["components"] = list(
|
||||
set(
|
||||
path_to_component[path]
|
||||
for path in commit.files
|
||||
if path in path_to_component
|
||||
)
|
||||
)
|
||||
obj["directories"] = get_directories(commit.files)
|
||||
obj["files"] = commit.files
|
||||
|
||||
return obj
|
||||
|
||||
|
@ -319,20 +327,6 @@ def get_revs(hg, date_from=None):
|
|||
return x.splitlines()
|
||||
|
||||
|
||||
def get_directories(files):
|
||||
if isinstance(files, str):
|
||||
files = [files]
|
||||
|
||||
directories = set()
|
||||
for path in files:
|
||||
path_dirs = (
|
||||
os.path.dirname(path).split("/", 2)[:2] if os.path.dirname(path) else []
|
||||
)
|
||||
if path_dirs:
|
||||
directories.update([path_dirs[0], "/".join(path_dirs)])
|
||||
return list(directories)
|
||||
|
||||
|
||||
def calculate_experiences(commits):
|
||||
print(f"Analyzing experiences from {len(commits)} commits...")
|
||||
|
||||
|
@ -348,39 +342,90 @@ def calculate_experiences(commits):
|
|||
complex_experiences = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
|
||||
|
||||
def update_experiences(experience_type, day, items):
|
||||
for item in items:
|
||||
exp = experiences[day][experience_type][item]
|
||||
total_exps = [experiences[day][experience_type][item] for item in items]
|
||||
timespan_exps = [
|
||||
exp - experiences[day - EXPERIENCE_TIMESPAN][experience_type][item]
|
||||
for exp, item in zip(total_exps, items)
|
||||
]
|
||||
|
||||
experiences_by_commit["total"][experience_type][commit.node] += exp
|
||||
total_exps_sum = sum(total_exps)
|
||||
timespan_exps_sum = sum(timespan_exps)
|
||||
|
||||
if experience_type == "author":
|
||||
experiences_by_commit["total"][experience_type][
|
||||
commit.node
|
||||
] = total_exps_sum
|
||||
experiences_by_commit[EXPERIENCE_TIMESPAN_TEXT][experience_type][
|
||||
commit.node
|
||||
] += (exp - experiences[day - EXPERIENCE_TIMESPAN][experience_type][item])
|
||||
] = timespan_exps_sum
|
||||
else:
|
||||
experiences_by_commit["total"][experience_type][commit.node] = {
|
||||
"sum": total_exps_sum,
|
||||
"max": max(total_exps) if len(total_exps) else 0,
|
||||
"min": min(total_exps) if len(total_exps) else 0,
|
||||
}
|
||||
experiences_by_commit[EXPERIENCE_TIMESPAN_TEXT][experience_type][
|
||||
commit.node
|
||||
] = {
|
||||
"sum": timespan_exps_sum,
|
||||
"max": max(timespan_exps) if len(timespan_exps) else 0,
|
||||
"min": min(timespan_exps) if len(timespan_exps) else 0,
|
||||
}
|
||||
|
||||
# We don't want to consider backed out commits when calculating experiences.
|
||||
if not commit.backedoutby:
|
||||
# We don't want to consider backed out commits when calculating experiences.
|
||||
if not commit.backedoutby:
|
||||
for item in items:
|
||||
experiences[day][experience_type][item] += 1
|
||||
|
||||
def update_complex_experiences(experience_type, day, items, self_node):
|
||||
all_commits = set()
|
||||
before_timespan_commits = set()
|
||||
for item in items:
|
||||
all_commits.update(complex_experiences[day][experience_type][item])
|
||||
|
||||
before_timespan_commits.update(
|
||||
complex_experiences[day - EXPERIENCE_TIMESPAN][experience_type][item]
|
||||
def update_complex_experiences(experience_type, day, items):
|
||||
all_commit_lists = [
|
||||
complex_experiences[day][experience_type][item] for item in items
|
||||
]
|
||||
before_commit_lists = [
|
||||
complex_experiences[day - EXPERIENCE_TIMESPAN][experience_type][item]
|
||||
for item in items
|
||||
]
|
||||
timespan_commit_lists = [
|
||||
commit_list[len(before_commit_list) :]
|
||||
for commit_list, before_commit_list in zip(
|
||||
all_commit_lists, before_commit_lists
|
||||
)
|
||||
]
|
||||
|
||||
# We don't want to consider backed out commits when calculating experiences.
|
||||
if not commit.backedoutby:
|
||||
complex_experiences[day][experience_type][item].append(commit.node)
|
||||
all_commits = set(sum(all_commit_lists, []))
|
||||
timespan_commits = set(sum(timespan_commit_lists, []))
|
||||
|
||||
# If a commit changes two files in the same component, we shouldn't increase the exp by two.
|
||||
all_commits.discard(self_node)
|
||||
|
||||
experiences_by_commit["total"][experience_type][commit.node] = len(all_commits)
|
||||
experiences_by_commit["total"][experience_type][commit.node] = {
|
||||
"sum": len(all_commits),
|
||||
"max": max(len(all_commit_list) for all_commit_list in all_commit_lists)
|
||||
if len(all_commit_lists)
|
||||
else 0,
|
||||
"min": min(len(all_commit_list) for all_commit_list in all_commit_lists)
|
||||
if len(all_commit_lists)
|
||||
else 0,
|
||||
}
|
||||
experiences_by_commit[EXPERIENCE_TIMESPAN_TEXT][experience_type][
|
||||
commit.node
|
||||
] = len(all_commits - before_timespan_commits)
|
||||
] = {
|
||||
"sum": len(timespan_commits),
|
||||
"max": max(
|
||||
len(timespan_commit_list)
|
||||
for timespan_commit_list in timespan_commit_lists
|
||||
)
|
||||
if len(timespan_commit_lists)
|
||||
else 0,
|
||||
"min": min(
|
||||
len(timespan_commit_list)
|
||||
for timespan_commit_list in timespan_commit_lists
|
||||
)
|
||||
if len(timespan_commit_lists)
|
||||
else 0,
|
||||
}
|
||||
|
||||
# We don't want to consider backed out commits when calculating experiences.
|
||||
if not commit.backedoutby:
|
||||
for item in items:
|
||||
complex_experiences[day][experience_type][item].append(commit.node)
|
||||
|
||||
prev_days = 0
|
||||
|
||||
|
@ -428,11 +473,9 @@ def calculate_experiences(commits):
|
|||
copied_directory
|
||||
] = complex_experiences[prev_day]["directory"][orig_directory]
|
||||
|
||||
update_complex_experiences("file", days, commit.files, commit.node)
|
||||
update_complex_experiences("file", days, commit.files)
|
||||
|
||||
update_complex_experiences(
|
||||
"directory", days, get_directories(commit.files), commit.node
|
||||
)
|
||||
update_complex_experiences("directory", days, get_directories(commit.files))
|
||||
|
||||
components = list(
|
||||
set(
|
||||
|
@ -442,7 +485,7 @@ def calculate_experiences(commits):
|
|||
)
|
||||
)
|
||||
|
||||
update_complex_experiences("component", days, components, commit.node)
|
||||
update_complex_experiences("component", days, components)
|
||||
|
||||
old_days = [
|
||||
day for day in experiences.keys() if day < days - EXPERIENCE_TIMESPAN
|
||||
|
|
|
@ -207,58 +207,250 @@ def test_calculate_experiences():
|
|||
assert repository.experiences_by_commit["90_days"]["author"]["commit5"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["author"]["commit6"] == 1
|
||||
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit3"] == 1
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit4"] == 4
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit5"] == 0
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit6"] == 1
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit3"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit4"] == {
|
||||
"sum": 4,
|
||||
"max": 2,
|
||||
"min": 2,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit5"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["reviewer"]["commit6"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit3"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit4"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit5"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit6"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit3"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit4"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit5"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["reviewer"]["commit6"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit3"] == 1
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit4"] == 2
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit5"] == 3
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit6"] == 4
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit3"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit4"] == {
|
||||
"sum": 2,
|
||||
"max": 2,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit5"] == {
|
||||
"sum": 3,
|
||||
"max": 3,
|
||||
"min": 3,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["file"]["commit6"] == {
|
||||
"sum": 4,
|
||||
"max": 4,
|
||||
"min": 4,
|
||||
}
|
||||
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit3"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit4"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit5"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit6"] == 2
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit3"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit4"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit5"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["file"]["commit6"] == {
|
||||
"sum": 2,
|
||||
"max": 2,
|
||||
"min": 2,
|
||||
}
|
||||
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit3"] == 2
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit4"] == 3
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit5"] == 4
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit6"] == 5
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit3"] == {
|
||||
"sum": 2,
|
||||
"max": 2,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit4"] == {
|
||||
"sum": 3,
|
||||
"max": 3,
|
||||
"min": 2,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit5"] == {
|
||||
"sum": 4,
|
||||
"max": 4,
|
||||
"min": 4,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["directory"]["commit6"] == {
|
||||
"sum": 5,
|
||||
"max": 5,
|
||||
"min": 5,
|
||||
}
|
||||
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit3"] == 2
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit4"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit5"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit6"] == 2
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit3"] == {
|
||||
"sum": 2,
|
||||
"max": 2,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit4"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit5"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["directory"]["commit6"] == {
|
||||
"sum": 2,
|
||||
"max": 2,
|
||||
"min": 2,
|
||||
}
|
||||
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit3"] == 1
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit4"] == 3
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit5"] == 3
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit6"] == 4
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit3"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit4"] == {
|
||||
"sum": 3,
|
||||
"max": 2,
|
||||
"min": 2,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit5"] == {
|
||||
"sum": 3,
|
||||
"max": 3,
|
||||
"min": 3,
|
||||
}
|
||||
assert repository.experiences_by_commit["total"]["component"]["commit6"] == {
|
||||
"sum": 4,
|
||||
"max": 4,
|
||||
"min": 4,
|
||||
}
|
||||
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit1"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit2"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit3"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit4"] == 0
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit5"] == 1
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit6"] == 2
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit1"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit2"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit3"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit4"] == {
|
||||
"sum": 0,
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit5"] == {
|
||||
"sum": 1,
|
||||
"max": 1,
|
||||
"min": 1,
|
||||
}
|
||||
assert repository.experiences_by_commit["90_days"]["component"]["commit6"] == {
|
||||
"sum": 2,
|
||||
"max": 2,
|
||||
"min": 2,
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче