-
Notifications
You must be signed in to change notification settings - Fork 178
/
Copy pathHierarchy.ts
92 lines (79 loc) · 3.07 KB
/
Hierarchy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import path from 'path';
import { listDirectories } from 'utils/hierarchy';
import { GitHubApi } from 'utils/apis';
import { Algorithm, Category, File } from 'models';
import { Author } from 'models/File';
import { algorithmsDir } from 'config/paths';
import { execute, pull } from 'utils/misc';
type CommitAuthors = {
[sha: string]: Author,
}
export class Hierarchy {
private categories!: Category[];
readonly path: string = algorithmsDir;
constructor() {
this.refresh();
this.update().catch(console.error);
}
refresh() {
this.categories = listDirectories(this.path)
.map(categoryName => new Category(path.resolve(this.path, categoryName), categoryName));
const files: File[] = [];
this.categories.forEach(category => category.algorithms.forEach(algorithm => files.push(...algorithm.files)));
this.cacheCommitAuthors().then(commitAuthors => this.cacheContributors(files, commitAuthors));
}
async update(commit?: string) {
await pull(this.path, 'algorithms', commit);
this.refresh();
};
async cacheCommitAuthors(page = 1, commitAuthors: CommitAuthors = {}): Promise<CommitAuthors> {
const per_page = 100;
const {data} = await GitHubApi.listCommits('algorithm-visualizer', 'algorithms', {per_page, page});
const commits: any[] = data;
for (const {sha, author} of commits) {
if (!author) continue;
const {login, avatar_url} = author;
commitAuthors[sha] = {login, avatar_url};
}
if (commits.length < per_page) {
return commitAuthors;
} else {
return this.cacheCommitAuthors(page + 1, commitAuthors);
}
}
async cacheContributors(files: File[], commitAuthors: CommitAuthors) {
for (const file of files) {
const stdout = await execute(`git --no-pager log --follow --no-merges --format="%H" -- "${path.relative(this.path, file.path)}"`, {
cwd: this.path,
});
const output = stdout.toString().replace(/\n$/, '');
const shas = output.split('\n').reverse();
const contributors: Author[] = [];
for (const sha of shas) {
const author = commitAuthors[sha];
if (author && !contributors.find(contributor => contributor.login === author.login)) {
contributors.push(author);
}
}
file.contributors = contributors;
}
}
find(categoryKey: string, algorithmKey: string) {
const category = this.categories.find(category => category.key === categoryKey);
if (!category) return;
const algorithm = category.algorithms.find(algorithm => algorithm.key === algorithmKey);
if (!algorithm) return;
const categoryName = category.name;
const algorithmName = algorithm.name;
const files = algorithm.files;
const description = algorithm.description;
return {categoryKey, categoryName, algorithmKey, algorithmName, files, description};
}
iterate(callback: (category: Category, algorithm: Algorithm) => void) {
this.categories.forEach(category => category.algorithms.forEach(algorithm => callback(category, algorithm)));
}
toJSON() {
const {categories} = this;
return {categories};
}
}