X-Git-Url: http://nitlanguage.org?ds=sidebyside diff --git a/src/nitcatalog.nit b/src/nitcatalog.nit index a42bd64..d2aff2d 100644 --- a/src/nitcatalog.nit +++ b/src/nitcatalog.nit @@ -18,63 +18,12 @@ # # The tool scans packages and generates the HTML files of a catalog. # -# ## Features -# -# * [X] scan packages and their `.ini` -# * [X] generate lists of packages -# * [X] generate a page per package with the readme and most metadata -# * [ ] link/include/be included in the documentation -# * [ ] propose `related packages` -# * [X] show directory content (a la nitls) -# * [X] gather git information from the working directory -# * [ ] gather git information from the repository -# * [ ] gather package information from github -# * [ ] gather people information from github -# * [ ] reify people -# * [ ] separate information gathering from rendering -# * [ ] move up information gathering in (existing or new) service modules -# * [X] add command line options -# * [ ] harden HTML (escaping, path injection, etc) -# * [ ] nitcorn server with RESTful API -# -# ## Issues and limitations -# -# The tool works likee the other tools and expects to find valid Nit source code in the directories -# -# * cruft and temporary files will be collected -# * missing source file (e.g. not yet generated by nitcc) will make information -# incomplete (e.g. invalid module thus partial dependency and metrics) -# -# How to use the tool as the basis of a Nit code archive on the web usable with a package manager is not clear. +# See `catalog` for details module nitcatalog import loader # Scan&load packages, groups and modules import doc::doc_down # Display mdoc -import md5 # To get gravatar images -import counter # For statistics -import modelize # To process and count classes and methods - -redef class MPackage - # Return the associated metadata from the `ini`, if any - fun metadata(key: String): nullable String - do - var ini = self.ini - if ini == null then return null - return ini[key] - end - - # The list of maintainers - var maintainers = new Array[String] - - # The list of contributors - var contributors = new Array[String] - - # The date of the most recent commit - var last_date: nullable String = null - - # The date of the oldest commit - var first_date: nullable String = null -end +import catalog # A HTML page in a catalog # @@ -134,6 +83,37 @@ class CatalogPage """ end + # Inject piwik HTML code if required + private fun add_piwik + do + var tracker_url = catalog.piwik_tracker + if tracker_url == null then return + + var site_id = catalog.piwik_site_id + + tracker_url = tracker_url.trim + if tracker_url.chars.last != '/' then tracker_url += "/" + add """ + + + + +""" + + end + redef fun rendering do add """ @@ -141,70 +121,26 @@ class CatalogPage +""" + add_piwik + add """ + """ end end -redef class Int - # Returns `log(self+1)`. Used to compute score of packages - fun score: Float do return (self+1).to_f.log -end - -# The main class of the calatog generator that has the knowledge -class Catalog - - # The modelbuilder - # used to access the files and count source lines of code - var modelbuilder: ModelBuilder - - # Packages by tag - var tag2proj = new MultiHashMap[String, MPackage] - - # Packages by category - var cat2proj = new MultiHashMap[String, MPackage] - - # Packages by maintainer - var maint2proj = new MultiHashMap[String, MPackage] - - # Packages by contributors - var contrib2proj = new MultiHashMap[String, MPackage] - - # Dependency between packages - var deps = new POSet[MPackage] - - # Number of modules by package - var mmodules = new Counter[MPackage] - - # Number of classes by package - var mclasses = new Counter[MPackage] - - # Number of methods by package - var mmethods = new Counter[MPackage] - - # Number of line of code by package - var loc = new Counter[MPackage] - - # Number of commits by package - var commits = new Counter[MPackage] - - # Score by package - # - # The score is loosely computed using other metrics - var score = new Counter[MPackage] - +redef class Catalog # Return a empty `CatalogPage`. fun new_page(rootpath: String): CatalogPage do return new CatalogPage(self, rootpath) end - # Scan, register and add a contributor to a package + # Add a contributor to a package fun add_contrib(person: String, mpackage: MPackage, res: Template) do - var projs = contrib2proj[person] - if not projs.has(mpackage) then projs.add mpackage var name = person var email = null var page = null @@ -279,11 +215,10 @@ class Catalog res.add "\n" end - # Compute information and generate a full HTML page for a package - fun package_page(mpackage: MPackage): Writable + # Generate a full HTML page for a package + fun generate_page(mpackage: MPackage): Writable do var res = new_page("..") - var score = score[mpackage].to_f var name = mpackage.name.html_escape res.more_head.add """{{{name}}}""" @@ -292,11 +227,7 @@ class Catalog

{{{name}}}

""" var mdoc = mpackage.mdoc_or_fallback - if mdoc != null then - score += 100.0 - res.add mdoc.html_documentation - score += mdoc.content.length.score - end + if mdoc != null then res.add mdoc.html_documentation res.add "

Content

" var ot = new OrderedTree[MConcern] @@ -321,13 +252,11 @@ class Catalog """ var tryit = mpackage.metadata("upstream.tryit") if tryit != null then - score += 1.0 var e = tryit.html_escape res.add "
  • Trynit!
  • \n" end var apk = mpackage.metadata("upstream.apk") if apk != null then - score += 1.0 var e = apk.html_escape res.add "
  • Android apk
  • \n" end @@ -336,21 +265,15 @@ class Catalog var homepage = mpackage.metadata("upstream.homepage") if homepage != null then - score += 5.0 var e = homepage.html_escape res.add "
  • {e}
  • \n" end var maintainer = mpackage.metadata("package.maintainer") if maintainer != null then - score += 5.0 add_contrib(maintainer, mpackage, res) - mpackage.maintainers.add maintainer - var projs = maint2proj[maintainer] - if not projs.has(mpackage) then projs.add mpackage end var license = mpackage.metadata("package.license") if license != null then - score += 5.0 var e = license.html_escape res.add "
  • {e} license
  • \n" end @@ -359,7 +282,6 @@ class Catalog res.add "

    Source Code

    \n\n" res.add "

    Tags

    \n" - var tags = mpackage.metadata("package.tags") - var ts = new Array[String] - if tags != null then - for t in tags.split(",") do - t = t.trim - if t == "" then continue - ts.add t - end - end - if ts.is_empty then ts.add "none" - if tryit != null then ts.add "tryit" - if apk != null then ts.add "apk" var ts2 = new Array[String] - for t in ts do - tag2proj[t].add mpackage + for t in mpackage.tags do t = t.html_escape ts2.add "{t}" end res.add_list(ts2, ", ", ", ") - var cat = ts.first - cat2proj[cat].add mpackage - score += ts.length.score if deps.has(mpackage) then var reqs = deps[mpackage].greaters.to_a @@ -448,11 +354,6 @@ class Catalog end res.add_list(list, ", ", " and ") end - - score += deps[mpackage].greaters.length.score - score += deps[mpackage].direct_greaters.length.score - score += deps[mpackage].smallers.length.score - score += deps[mpackage].direct_smallers.length.score end var contributors = mpackage.contributors @@ -463,56 +364,20 @@ class Catalog end res.add "" end - score += contributors.length.to_f - - var mmodules = 0 - var mclasses = 0 - var mmethods = 0 - var loc = 0 - for g in mpackage.mgroups do - mmodules += g.mmodules.length - for m in g.mmodules do - var am = modelbuilder.mmodule2node(m) - if am != null then - var file = am.location.file - if file != null then - loc += file.line_starts.length - 1 - end - end - for cd in m.mclassdefs do - mclasses += 1 - for pd in cd.mpropdefs do - if not pd isa MMethodDef then continue - mmethods += 1 - end - end - end - end - self.mmodules[mpackage] = mmodules - self.mclasses[mpackage] = mclasses - self.mmethods[mpackage] = mmethods - self.loc[mpackage] = loc - - #score += mmodules.score - score += mclasses.score - score += mmethods.score - score += loc.score res.add """

    Stats

    """ res.add """ """ - self.score[mpackage] = score.to_i - return res end @@ -577,43 +442,6 @@ class Catalog return res end - # Collect more information on a package using the `git` tool. - fun git_info(mpackage: MPackage) - do - var ini = mpackage.ini - if ini == null then return - - # TODO use real git info - #var repo = ini.get_or_null("upstream.git") - #var branch = ini.get_or_null("upstream.git.branch") - #var directory = ini.get_or_null("upstream.git.directory") - - var dirpath = mpackage.root.filepath - if dirpath == null then return - - # Collect commits info - var res = git_run("log", "--no-merges", "--follow", "--pretty=tformat:%ad;%aN <%aE>", "--", dirpath) - var contributors = new Counter[String] - var commits = res.split("\n") - if commits.not_empty and commits.last == "" then commits.pop - self.commits[mpackage] = commits.length - for l in commits do - var s = l.split_once_on(';') - if s.length != 2 or s.last == "" then continue - - # Collect date of last and first commit - if mpackage.last_date == null then mpackage.last_date = s.first - mpackage.first_date = s.first - - # Count contributors - contributors.inc(s.last) - end - for c in contributors.sort.reverse_iterator do - mpackage.contributors.add c - end - - end - # Produce a HTML table containig information on the packages # # `package_page` must have been called before so that information is computed. @@ -661,17 +489,13 @@ class Catalog res.add "\n" return res end -end -# Execute a git command and return the result -fun git_run(command: String...): String -do - # print "git {command.join(" ")}" - var p = new ProcessReader("git", command...) - var res = p.read_all - p.close - p.wait - return res + # Piwik tracker URL, if any + var piwik_tracker: nullable String = null + + # Piwik site ID + # Used when `piwik_tracker` is set + var piwik_site_id: Int = 1 end var model = new Model @@ -682,7 +506,13 @@ var opt_no_git = new OptionBool("Do not gather git information from the working var opt_no_parse = new OptionBool("Do not parse nit files (no importation information)", "--no-parse") var opt_no_model = new OptionBool("Do not analyse nit files (no class/method information)", "--no-model") -tc.option_context.add_option(opt_dir, opt_no_git, opt_no_parse, opt_no_model) +# Piwik tracker URL. +# If you want to monitor your visitors. +var opt_piwik_tracker = new OptionString("Piwik tracker URL (ex: `nitlanguage.org/piwik/`)", "--piwik-tracker") +# Piwik tracker site id. +var opt_piwik_site_id = new OptionString("Piwik site ID", "--piwik-site-id") + +tc.option_context.add_option(opt_dir, opt_no_git, opt_no_parse, opt_no_model, opt_piwik_tracker, opt_piwik_site_id) tc.process_options(sys.args) tc.keep_going = true @@ -690,6 +520,19 @@ tc.keep_going = true var modelbuilder = new ModelBuilder(model, tc) var catalog = new Catalog(modelbuilder) +catalog.piwik_tracker = opt_piwik_tracker.value +var piwik_site_id = opt_piwik_site_id.value +if piwik_site_id != null then + if catalog.piwik_tracker == null then + print_error "Warning: ignored `{opt_piwik_site_id}` because `{opt_piwik_tracker}` is not set." + else if piwik_site_id.is_int then + print_error "Warning: ignored `{opt_piwik_site_id}`, an integer is required." + else + catalog.piwik_site_id = piwik_site_id.to_i + end +end + + # Get files or groups var args = tc.option_context.rest if opt_no_parse.value then @@ -834,7 +677,8 @@ css.write_to_file(out/"style.css") for p in model.mpackages do # print p var f = "p/{p.name}.html" - catalog.package_page(p).write_to_file(out/f) + catalog.package_page(p) + catalog.generate_page(p).write_to_file(out/f) end # INDEX