Skip to content

Commit 6108055

Browse files
committed
Added hall of fame (unstyled).
1 parent 5a20149 commit 6108055

File tree

6 files changed

+607
-1
lines changed

6 files changed

+607
-1
lines changed

_includes/render-scala-fame.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{% comment %}
2+
Input:
3+
famedata: Scala fame data to be rendered
4+
{% endcomment %}
5+
6+
<h2>{{ famedata.title }}</h2>
7+
8+
{% for cat in famedata.fame-categories %}
9+
<div class="fame-category" id="fame-category-{{ cat.category }}">
10+
<h3>{{ cat.category }}</h3>
11+
12+
{% for author in cat.authors limit:10 %}
13+
<div class="fame-author fame-author-rank-{{ author.rank }}">
14+
<div class="fame-author-gravatar">
15+
<img src="{{ author.gravatar|escape }}" alt="{{ author.username }}" width="80px" height="80px" />
16+
</div>
17+
<div class="fame-author-username">
18+
<a href="https://github.com/{{ author.username }}">{{ author.username }}</a>
19+
</div>
20+
<div class="fame-author-stats">
21+
<div class="fame-author-stats-commits">{{ author.commits }} commits</div>
22+
<div class="fame-author-stats-added">Lines added: {{ author.linesAdded }}</div>
23+
<div class="fame-author-stats-deleted">Lines deleted: {{ author.linesDeleted }}</div>
24+
</div>
25+
</div>
26+
{% endfor %}
27+
28+
</div>
29+
{% endfor %}
30+
31+
{% assign titledone = false %}
32+
{% for cat in famedata.fame-categories %}
33+
{% for author in cat.authors %}
34+
{% if author.newContributor %}
35+
36+
{% unless titledone %}
37+
<h2>New contributors of the month</h2>
38+
<ul>
39+
{% assign titledone = true %}
40+
{% endunless %}
41+
42+
<li><a href="https://github.com/{{ author.username }}">{{ author.username }}</a></li>
43+
44+
{% endif %}
45+
{% endfor %}
46+
{% endfor %}
47+
48+
{% if titledone %}
49+
</ul>
50+
{% endif %}

_layouts/famearchive.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
layout: page
3+
---
4+
5+
{% assign famedata = page %}
6+
{% include render-scala-fame.html %}
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/** This script makes the Hall of Fame for the elapsed month.
2+
* It should be run every 1st day of the month, in the working tree, and
3+
* then a commit with the changes should be pushed to scala-lang.
4+
*/
5+
6+
import java.net.URL
7+
8+
import scala.annotation.switch
9+
10+
import scala.io.Source
11+
import scala.util.parsing.json._
12+
13+
object MakeHallOfFame {
14+
object Category extends Enumeration {
15+
val Typesafe, EPFL, Community = Value
16+
}
17+
type Category = Category.Value
18+
19+
// TODO Expand (and maintain) that list - or fetch it from some source
20+
val TypesafePeople = Set(
21+
"adriaanm",
22+
"dragos",
23+
"gkossakowski",
24+
"JamesIry",
25+
"jsuereth",
26+
"lexspoon",
27+
"paulp",
28+
"phaller",
29+
"retronym"
30+
)
31+
32+
// TODO Expand (and maintain) that list - or fetch it from some source
33+
val EPFLPeople = Set(
34+
"axel22",
35+
"heathermiller",
36+
"hubertp",
37+
"lrytz",
38+
"magarciaEPFL",
39+
"odersky",
40+
"TiarkRompf",
41+
"VladUreche",
42+
"xeno-by"
43+
)
44+
45+
class Author(val username: String, val gravatar: String) {
46+
val category: Category =
47+
if (TypesafePeople(username)) Category.Typesafe
48+
else if (EPFLPeople(username)) Category.EPFL
49+
else Category.Community
50+
51+
var commits: Int = 0
52+
var linesAdded: Int = 0
53+
var linesDeleted: Int = 0
54+
55+
var isNewContributor: Boolean = false
56+
}
57+
58+
var thisYear: Int = 0
59+
var thisMonth: Int = 0
60+
var thisMonthStr: String = ""
61+
62+
def isWeekOfThisMonth(week: String): Boolean =
63+
week startsWith thisMonthStr
64+
65+
def main(args: Array[String]) {
66+
val (year, month) = {
67+
if (args.size >= 2) (args(0).toInt, args(1).toInt)
68+
else getYearAndMonth()
69+
}
70+
thisYear = year
71+
thisMonth = month
72+
thisMonthStr = "%04d-%02d" format (year, month)
73+
74+
progress(s"Building data for $thisMonthStr")
75+
76+
val sourceDataString = loadSourceDataString()
77+
val sourceData = parseSourceData(sourceDataString)
78+
val authors = buildDataFromJSON(sourceData)
79+
80+
progress("Sorting by category")
81+
val byCategory = authors.groupBy(_.category)
82+
val sorted = byCategory.mapValues(_.sortBy(-_.commits))
83+
84+
val output = buildOutput(sorted)
85+
writeOutputToFile(output)
86+
}
87+
88+
def progress(msg: String) {
89+
Console.err.println(msg)
90+
}
91+
92+
def getYearAndMonth(): (Int, Int) = {
93+
import java.util.Calendar._
94+
val cal = new java.util.GregorianCalendar
95+
cal.set(DAY_OF_MONTH, 0) // this will wrap to the Month (and Year if necessary)
96+
(cal.get(YEAR), cal.get(MONTH)+1)
97+
}
98+
99+
def loadSourceDataString(): String = {
100+
progress("Downloading source data")
101+
val source = Source.fromURL(new URL(
102+
"https://github.com/scala/scala/graphs/contributors-data"))
103+
try source.mkString
104+
finally source.close()
105+
}
106+
107+
def parseSourceData(str: String): Any = {
108+
progress("Parsing JSON in source data")
109+
JSON.parseFull(str) getOrElse {
110+
throw new Exception("Parse error")
111+
}
112+
}
113+
114+
def buildDataFromJSON(jsonAuthors: Any): List[Author] = {
115+
progress("Building my data")
116+
val L(authors) = jsonAuthors
117+
val all = for {
118+
M(author0) <- authors
119+
M(authorData) = author0("author")
120+
S(username) = authorData("login")
121+
S(gravatar) = authorData("gravatar")
122+
I(totalCommits) = author0("total")
123+
L(jsonWeeks) = author0("weeks")
124+
} yield {
125+
val author = new Author(username, gravatar)
126+
127+
for {
128+
M(week) <- jsonWeeks
129+
S(date) = week("w")
130+
if isWeekOfThisMonth(date)
131+
I(commits) = week("c")
132+
I(added) = week("a")
133+
I(deleted) = week("d")
134+
} yield {
135+
author.commits += commits
136+
author.linesAdded += added
137+
author.linesDeleted += deleted
138+
}
139+
140+
author.isNewContributor = author.commits == totalCommits
141+
author
142+
}
143+
all filter (_.commits != 0)
144+
}
145+
146+
def buildOutput(authorsByCategory: Map[Category, List[Author]]) = {
147+
progress("Outputting")
148+
149+
val result = new scala.collection.mutable.ListBuffer[String]
150+
def outln(line: String) = result += line
151+
152+
val thisMonthText = getMonthName(thisMonth)
153+
154+
outln("---")
155+
outln("layout: famearchive")
156+
outln("title: Contributors of " + thisMonthText + " " + thisYear)
157+
outln("fame-year: " + thisYear)
158+
outln("fame-month: " + thisMonth)
159+
outln("fame-month-str: " + thisMonthText)
160+
outln("fame-categories:")
161+
162+
for {
163+
category <- Seq(Category.Typesafe, Category.EPFL, Category.Community)
164+
} {
165+
outln(" - category: " + category.toString())
166+
outln(" authors:")
167+
var rank = 0
168+
var rankCommits = -1
169+
for (author <- authorsByCategory(category)) {
170+
if (author.commits != rankCommits) {
171+
rank += 1
172+
rankCommits = author.commits
173+
}
174+
175+
outln(" - username: " + author.username)
176+
outln(" gravatar: " + author.gravatar)
177+
outln(" commits: " + author.commits)
178+
outln(" linesAdded: " + author.linesAdded)
179+
outln(" linesDeleted: " + author.linesDeleted)
180+
outln(" rank: " + rank)
181+
outln(" newContributor: " + author.isNewContributor)
182+
}
183+
}
184+
185+
outln("---")
186+
187+
result.toList
188+
}
189+
190+
def writeOutputToFile(output: List[String]) {
191+
val (postYear, postMonth) = {
192+
import java.util.Calendar._
193+
val cal = new java.util.GregorianCalendar(thisYear, thisMonth-1, 1)
194+
cal.add(MONTH, 1)
195+
(cal.get(YEAR), cal.get(MONTH)+1)
196+
}
197+
val postDateStr = "%04d-%02d-01" format (postYear, postMonth)
198+
199+
val fileName = s"../../get-involved/scala-fame-data/_posts/${postDateStr}-scala-fame-${thisMonthStr}.md"
200+
201+
progress("Writing output to " + fileName)
202+
203+
val writer = new java.io.PrintWriter(fileName)
204+
try output foreach writer.println
205+
finally writer.close()
206+
}
207+
208+
def getMonthName(month: Int): String = (month: @switch) match {
209+
case 1 => "January"
210+
case 2 => "February"
211+
case 3 => "March"
212+
case 4 => "April"
213+
case 5 => "May"
214+
case 6 => "June"
215+
case 7 => "July"
216+
case 8 => "August"
217+
case 9 => "September"
218+
case 10 => "October"
219+
case 11 => "November"
220+
case 12 => "December"
221+
}
222+
223+
// JSON extractors
224+
class CC[T] {
225+
def unapply(a: Any): Option[T] = Some(a.asInstanceOf[T])
226+
}
227+
228+
object M extends CC[Map[String, Any]]
229+
object L extends CC[List[Any]]
230+
object S extends CC[String]
231+
object D extends CC[Double]
232+
object B extends CC[Boolean]
233+
234+
object I {
235+
def unapply(a: Any): Option[Int] = Some(a.asInstanceOf[Double].toInt)
236+
}
237+
}

0 commit comments

Comments
 (0)