# * [ ] gather git information from the repository
# * [ ] gather package information from github
# * [ ] gather people information from github
-# * [ ] reify people
+# * [X] reify people
# * [X] separate information gathering from rendering
# * [ ] move up information gathering in (existing or new) service modules
# * [X] add command line options
var tags = new Array[String]
# The list of maintainers
- var maintainers = new Array[String]
+ var maintainers = new Array[Person]
# The list of contributors
- var contributors = new Array[String]
+ var contributors = new Array[Person]
# The date of the most recent commit
var last_date: nullable String = null
fun score: Float do return (self+1).to_f.log
end
+# A contributor/author/etc.
+#
+# It comes from git or the metadata
+#
+# TODO get more things from github by using the email as a key
+# "https://api.github.com/search/users?q={email}+in:email"
+class Person
+ # The name. Eg "John Doe"
+ var name: String is writable
+
+ # The email, Eg "john.doe@example.com"
+ var email: nullable String is writable
+
+ # Some homepage. Eg "http://example.com/~jdoe"
+ var page: nullable String is writable
+
+ # Return a full-featured link to a person
+ fun to_html: String
+ do
+ var res = ""
+ var e = name.html_escape
+ var page = self.page
+ if page != null then
+ res += "<a href=\"{page.html_escape}\">"
+ end
+ var email = self.email
+ if email != null then
+ var md5 = email.md5.to_lower
+ res += "<img src=\"https://secure.gravatar.com/avatar/{md5}?size=20&default=retro\"> "
+ end
+ res += e
+ if page != null then res += "</a>"
+ return res
+ end
+
+ # The standard representation of a person.
+ #
+ # ~~~
+ # var jd = new Person("John Doe", "john.doe@example.com", "http://example.com/~jdoe")
+ # assert jd.to_s == "John Doe <john.doe@example.com> (http://example.com/~jdoe)"
+ # ~~~
+ #
+ # It can be used as the input of `parse`.
+ #
+ # ~~~
+ # var jd2 = new Person.parse(jd.to_s)
+ # assert jd2.to_s == jd.to_s
+ # ~~~
+ redef fun to_s
+ do
+ var res = name
+ var email = self.email
+ if email != null then res += " <{email}>"
+ var page = self.page
+ if page != null then res += " ({page})"
+ return res
+ end
+
+ # Crete a new person from its standard textual representation.
+ #
+ # ~~~
+ # var jd = new Person.parse("John Doe <john.doe@example.com> (http://example.com/~jdoe)")
+ # assert jd.name == "John Doe"
+ # assert jd.email == "john.doe@example.com"
+ # assert jd.page == "http://example.com/~jdoe"
+ # ~~~
+ #
+ # Emails and page are optional.
+ #
+ # ~~~
+ # var jd2 = new Person.parse("John Doe")
+ # assert jd2.name == "John Doe"
+ # assert jd2.email == null
+ # assert jd2.page == null
+ # ~~~
+ init parse(person: String)
+ do
+ var name = person
+ var email = null
+ var page = null
+ # Regular expressions are broken, need to investigate.
+ # So split manually.
+ #
+ #var re = "([^<(]*?)(<([^>]*?)>)?(\\((.*)\\))?".to_re
+ #var m = (person+" ").search(re)
+ #print "{person}: `{m or else "?"}` `{m[1] or else "?"}` `{m[3] or else "?"}` `{m[5] or else "?"}`"
+ do
+ var sp1 = person.split_once_on("<")
+ if sp1.length < 2 then
+ break
+ end
+ var sp2 = sp1.last.split_once_on(">")
+ if sp2.length < 2 then
+ break
+ end
+ name = sp1.first.trim
+ email = sp2.first.trim
+ var sp3 = sp2.last.split_once_on("(")
+ if sp3.length < 2 then
+ break
+ end
+ var sp4 = sp3.last.split_once_on(")")
+ if sp4.length < 2 then
+ break
+ end
+ page = sp4.first.trim
+ end
+
+ init(name, email, page)
+ end
+end
+
+
# The main class of the calatog generator that has the knowledge
class Catalog
var cat2proj = new MultiHashMap[String, MPackage]
# Packages by maintainer
- var maint2proj = new MultiHashMap[String, MPackage]
+ var maint2proj = new MultiHashMap[Person, MPackage]
# Packages by contributors
- var contrib2proj = new MultiHashMap[String, MPackage]
+ var contrib2proj = new MultiHashMap[Person, MPackage]
# Dependency between packages
var deps = new POSet[MPackage]
# The score is loosely computed using other metrics
var score = new Counter[MPackage]
+ # List of known people
+ var persons = new HashMap[String, Person]
+
# Scan, register and add a contributor to a package
- fun register_contrib(person: String, mpackage: MPackage)
+ fun register_contrib(person: String, mpackage: MPackage): Person
do
- var projs = contrib2proj[person]
- if not projs.has(mpackage) then projs.add mpackage
+ var p = persons.get_or_null(person)
+ if p == null then
+ p = new Person.parse(person)
+ persons[person] = p
+ end
+ var projs = contrib2proj[p]
+ if not projs.has(mpackage) then
+ projs.add mpackage
+ mpackage.contributors.add p
+ end
+ return p
end
# Compute information for a package
var maintainer = mpackage.metadata("package.maintainer")
if maintainer != null then
score += 5.0
- register_contrib(maintainer, mpackage)
- mpackage.maintainers.add maintainer
- var projs = maint2proj[maintainer]
+ var person = register_contrib(maintainer, mpackage)
+ mpackage.maintainers.add person
+ var projs = maint2proj[person]
if not projs.has(mpackage) then projs.add mpackage
end
var license = mpackage.metadata("package.license")
var more_contributors = mpackage.metadata("package.more_contributors")
if more_contributors != null then
for c in more_contributors.split(",") do
- contributors.add c.trim
- end
- end
- if not contributors.is_empty then
- for c in contributors do
- register_contrib(c, mpackage)
+ register_contrib(c.trim, mpackage)
end
end
score += contributors.length.to_f
contributors.inc(s.last)
end
for c in contributors.sort.reverse_iterator do
- mpackage.contributors.add c
+ register_contrib(c, mpackage)
end
end
return new CatalogPage(self, rootpath)
end
- # Add a contributor to a package
- fun add_contrib(person: String, mpackage: MPackage, res: Template)
- do
- var name = person
- var email = null
- var page = null
-
- # Regular expressions are broken, need to investigate.
- # So split manually.
- #
- #var re = "([^<(]*?)(<([^>]*?)>)?(\\((.*)\\))?".to_re
- #var m = (person+" ").search(re)
- #print "{person}: `{m or else "?"}` `{m[1] or else "?"}` `{m[3] or else "?"}` `{m[5] or else "?"}`"
- do
- var sp1 = person.split_once_on("<")
- if sp1.length < 2 then
- break
- end
- var sp2 = sp1.last.split_once_on(">")
- if sp2.length < 2 then
- break
- end
- name = sp1.first.trim
- email = sp2.first.trim
- var sp3 = sp2.last.split_once_on("(")
- if sp3.length < 2 then
- break
- end
- var sp4 = sp3.last.split_once_on(")")
- if sp4.length < 2 then
- break
- end
- page = sp4.first.trim
- end
-
- var e = name.html_escape
- res.add "<li>"
- if page != null then
- res.add "<a href=\"{page.html_escape}\">"
- end
- if email != null then
- # TODO get more things from github by using the email as a key
- # "https://api.github.com/search/users?q={email}+in:email"
- var md5 = email.md5.to_lower
- res.add "<img src=\"https://secure.gravatar.com/avatar/{md5}?size=20&default=retro\"> "
- end
- res.add "{e}"
- if page != null then res.add "</a>"
- res.add "</li>"
- end
-
# Recursively generate a level in the file tree of the *content* section
private fun gen_content_level(ot: OrderedTree[MConcern], os: Array[Object], res: Template)
do
var e = homepage.html_escape
res.add "<li><a href=\"{e}\">{e}</a></li>\n"
end
- var maintainer = mpackage.metadata("package.maintainer")
- if maintainer != null then
- add_contrib(maintainer, mpackage, res)
+ for maintainer in mpackage.maintainers do
+ res.add "<li>{maintainer.to_html}</li>"
end
var license = mpackage.metadata("package.license")
if license != null then
if not contributors.is_empty then
res.add "<h3>Contributors</h3>\n<ul class=\"box\">"
for c in contributors do
- add_contrib(c, mpackage, res)
+ res.add "<li>{c.to_html}</li>"
end
res.add "</ul>"
end
#
# The list of keys is generated first to allow fast access to the correct `<h3>`.
# `id_prefix` is used to give an id to the `<h3>` element.
- fun list_by(map: MultiHashMap[String, MPackage], id_prefix: String): Template
+ fun list_by(map: MultiHashMap[Object, MPackage], id_prefix: String): Template
do
var res = new Template
var keys = map.keys.to_a
alpha_comparator.sort(keys)
- var list = [for x in keys do "<a href=\"#{id_prefix}{x.html_escape}\">{x.html_escape}</a>"]
+ var list = [for x in keys do "<a href=\"#{id_prefix}{x.to_s.html_escape}\">{x.to_s.html_escape}</a>"]
res.add_list(list, ", ", " and ")
for k in keys do
var projs = map[k].to_a
alpha_comparator.sort(projs)
- var e = k.html_escape
+ var e = k.to_s.html_escape
res.add "<h3 id=\"{id_prefix}{e}\">{e} ({projs.length})</h3>\n<ul>\n"
for p in projs do
res.add "<li>"
res.add "<tr>"
res.add "<td><a href=\"p/{p.name}.html\">{p.name}</a></td>"
var maint = "?"
- if p.maintainers.not_empty then maint = p.maintainers.first
+ if p.maintainers.not_empty then maint = p.maintainers.first.name.html_escape
res.add "<td>{maint}</td>"
res.add "<td>{p.contributors.length}</td>"
if deps.not_empty then