Only internal modification, no diff with the generated HTML of the previous version.
If you don't trust me (I you shouldn't), the demos will be [there](http://gresil.org/jenkins/job/CI-nitdoc/ws/doc/stdlib/index.html) and [there](http://gresil.org/jenkins/job/CI-nitdoc/ws/doc/nitc/index.html) in a few minutes.
> Brace yourself, next PR on that subject will be big.
Pull-Request: #1293
Reviewed-by: Jean Privat <jean@pryen.org>
var key = self.key / achievement.key
if game.store.has_key(key) then return
stats.inc("achievements")
- achievement.save_in(self)
+ achievement.save_in(self.key)
save
end
redef class GameEntity
# Saves `event` in `self`.
- fun add_event(event: GameEvent) do event.save_in(self)
+ fun add_event(event: GameEvent) do event.save_in(self.key)
# List all events registered in this entity.
#
# Date are stored under `self.key`.
fun save do game.store.store_object(key, to_json)
- # Saves `self` state into `target` key data.
+ # Saves `self` state under `key` data.
#
- # Data are stored under `target.key / self.key`.
- fun save_in(target: GameEntity) do
- game.store.store_object(target.key / key, to_json)
+ # Data are stored under `key / self.key`.
+ fun save_in(key: String) do
+ game.store.store_object(key / self.key, to_json)
end
# Json representation of `self`.
redef class GameEntity
- # Statistics for this entity.
- fun stats: GameStats is abstract
-
- # Load statistics for this `MEntity` if any.
- fun load_statistics: nullable GameStats do
- var key = self.key / "statistics"
- if not game.store.has_key(key) then return null
- var json = game.store.load_object(key)
- return new GameStats.from_json(game, json)
- end
+ # Statistics manager for this entity.
+ fun stats: GameStatsManager is abstract
end
redef class Game
- redef var stats is lazy do
- return load_statistics or else new GameStats(game)
- end
+ redef var stats is lazy do return new GameStatsManager(game, self)
redef fun save do
super
- stats.save_in(self)
+ stats.save_in(self.key)
end
redef fun pretty do
redef class Player
- redef var stats is lazy do
- return load_statistics or else new GameStats(game)
- end
+ redef var stats is lazy do return new GameStatsManager(game, self)
redef fun save do
super
- stats.save_in(self)
+ stats.save_in(self.key)
end
+ redef fun nitcoins do return stats["nitcoins"]
+ redef fun nitcoins=(nc) do stats["nitcoins"] = nc
+
redef fun pretty do
var res = new FlatBuffer
res.append super
end
end
+# Store game stats for defined period.
+class GameStatsManager
+ super GameEntity
+ super Counter[String]
+
+ redef var game
+
+ # The GameEntity monitored by these statistics.
+ var owner: GameEntity
+
+ redef var key = "stats"
+
+ # Returns the `GameStats` instance for the overall statistics.
+ var overall: GameStats is lazy do
+ return load_stats_for("all")
+ end
+
+ # Returns the `GameStats` instance for the current year statistics.
+ var yearly: GameStats is lazy do
+ var date = new Tm.gmtime
+ var key = date.strftime("%Y")
+ return load_stats_for(key)
+ end
+
+ # Returns the `GameStats` instance for the current month statistics.
+ var monthly: GameStats is lazy do
+ var date = new Tm.gmtime
+ var key = date.strftime("%Y-%m")
+ return load_stats_for(key)
+ end
+
+ # Returns the `GameStats` instance for the current day statistics.
+ var daily: GameStats is lazy do
+ var date = new Tm.gmtime
+ var key = date.strftime("%Y-%m-%d")
+ return load_stats_for(key)
+ end
+
+ # Returns the `GameStats` instance for the current week statistics.
+ var weekly: GameStats is lazy do
+ var date = new Tm.gmtime
+ var key = date.strftime("%Y-W%U")
+ return load_stats_for(key)
+ end
+
+ # Load statistics for a `period` key.
+ fun load_stats_for(period: String): GameStats do
+ var key = owner.key / self.key / period
+ if not game.store.has_key(key) then
+ return new GameStats(game, period)
+ end
+ var json = game.store.load_object(key)
+ return new GameStats.from_json(game, period, json)
+ end
+
+ redef fun [](key) do return overall[key]
+
+ redef fun []=(key, value) do
+ overall[key] = value
+ yearly[key] = value
+ monthly[key] = value
+ daily[key] = value
+ weekly[key] = value
+ end
+
+ redef fun inc(e) do
+ overall.inc(e)
+ yearly.inc(e)
+ monthly.inc(e)
+ daily.inc(e)
+ weekly.inc(e)
+ end
+
+ redef fun dec(e) do
+ overall.dec(e)
+ yearly.dec(e)
+ monthly.dec(e)
+ daily.dec(e)
+ weekly.dec(e)
+ end
+
+ redef fun save_in(key) do
+ overall.save_in(key / self.key)
+ yearly.save_in(key / self.key)
+ monthly.save_in(key / self.key)
+ daily.save_in(key / self.key)
+ weekly.save_in(key / self.key)
+ end
+
+ redef fun pretty do return overall.pretty
+end
+
# Game statistics structure that can be saved as a `GameEntity`.
class GameStats
super GameEntity
redef var game
- redef var key = "statistics"
+ # The pedriod these stats are about.
+ var period: String
+
+ redef fun key do return period
- # Load `self` from saved data
- init from_json(game: Game, json: JsonObject) do
- self.game = game
+ # Load `self` from saved data.
+ init from_json(game: Game, period: String, json: JsonObject) do
for k, v in json do self[k] = v.as(Int)
end
return obj
end
- # Decrements the value of `key` statistic entry by 1.
- fun dec(key: String) do
- if not has_key(key) then
- self[key] = 0
- else
- self[key] -= 1
- end
- end
-
redef fun pretty do
var res = new FlatBuffer
for k, v in self do
redef fun render_title do
add "<span class=\"glyphicon glyphicon-check\"></span> "
- add "Review pull requests to gain nitcoins!"
+ add "Review pull requests and comment issues to gain nitcoins!"
end
redef fun render_body do
var q = "is:open label:need_review sort:updated-asc " +
"-involves:{player.name}"
- var issues = game.repo.search_issues(q)
+ var q2 = "is:open label:request_for_comments sort:updated-asc " +
+ "-involves:{player.name}"
+
+ var issues = new ArraySet[Issue]
+ issues.add_all game.repo.search_issues(q).as(not null)
+ issues.add_all game.repo.search_issues(q2).as(not null)
+ if issues.is_empty then
+ add "<em>No pull request or issue to review yet...</em>"
+ return
+ end
+ for issue in issues do
+ var user = issue.user
+ var uplay = user.player(game)
+ add """<div class="media">
+ <a class="media-left" href="{{{uplay.url}}}">
+ <img class=\"img-circle\" style="width:50px"
+ src="{{{user.avatar_url}}}" alt="{{{uplay.name}}}">
+ </a>
+ <div class="media-body">
+ <h4 class="media-heading">
+ {{{issue.link}}} {{{issue.title}}}
+ </h4>
+ <span class="text-muted">opened by </span>
+ {{{uplay.link}}}
+ </div>
+ </div>"""
+ end
+ end
+end
+
+# A `Panel` that displays the work assigned or tagged.
+class PlayerWorkPanel
+ super Panel
+
+ # Repo to display.
+ var game: Game
+
+ # Player to display customized list for.
+ var player: Player
+
+ redef fun render_title do
+ add "<span class=\"glyphicon glyphicon-check\"></span> "
+ add "Do your tasks to gain nitcoins!"
+ end
+
+ redef fun render_body do
+ var q = "is:open label:need_work sort:updated-asc author:{player.name}"
+ var q2 = "is:open sort:updated-asc assignee:{player.name}"
+
+ var issues = new ArraySet[Issue]
+ issues.add_all game.repo.search_issues(q).as(not null)
+ issues.add_all game.repo.search_issues(q2).as(not null)
if issues.is_empty then
- add "<em>No pull request to review yet...</em>"
+ add "<em>No work to do yet...</em>"
return
end
for issue in issues do
page.side_panels.clear
page.side_panels.add new PlayerStatusPanel(game, player)
page.flow_panels.add new PlayerReviewsPanel(game, player)
+ page.flow_panels.add new PlayerWorkPanel(game, player)
page.flow_panels.add new AchievementsListPanel(player)
page.flow_panels.add new EventListPanel(player, list_limit, list_from)
rsp.body = page.write_to_string
for e in es do inc(e)
end
+ # Decrement the value of `e` by 1
+ fun dec(e: E) do
+ if not has_key(e) then
+ self.map[e] = 0
+ else
+ self.map[e] = self[e] - 1
+ sum += - 1
+ end
+ end
+
+ # Decrement the value for each element of `es`
+ fun dec_all(es: Collection[E])
+ do
+ for e in es do dec(e)
+ end
+
# A new Counter initialized with `inc_all`.
init from(es: Collection[E])
do
redef fun compare(a, b) do return a <=> b
end
+# This comparator uses the operator `<=>` to compare objects in a reverse order.
+#
+# See `default_reverse_comparator` for an easy-to-use general stateless reverse
+# default comparator.
+class DefaultReverseComparator
+ super Comparator
+
+ redef type COMPARED: Comparable
+
+ # Returns `b <=> a`.
+ redef fun compare(a, b) do return b <=> a
+end
+
# Easy-to-use general stateless default comparator that uses `<=>` to compare things.
fun default_comparator: DefaultComparator do return once new DefaultComparator
+
+# Easy-to-use general stateless default reverse comparator.
+#
+# Does the same as `default_comparator` but in reverse order.
+fun default_reverse_comparator: DefaultReverseComparator do
+ return once new DefaultReverseComparator
+end
import modelize
redef class ToolContext
+ # Compute MENDEL metrics.
+ #
+ # See `mendel_metrics` module documentation.
var mendel_metrics_phase: Phase = new MendelMetricsPhase(self, null)
end
metrics.collect(mclasses)
if csv then metrics.to_csv.save("{out}/mendel.csv")
- print toolcontext.format_h4("\tlarge mclasses (threshold: {cnblp.threshold})")
- for mclass in cnblp.above_threshold do
- print toolcontext.format_p("\t {mclass.name}: {cnblp.values[mclass]}")
+ var threshold = cnblp.threshold
+ print toolcontext.format_h4("\tlarge mclasses (threshold: {threshold})")
+ for mclass in cnblp.sort do
+ var val = cnblp.values[mclass]
+ if val.to_f < threshold then break
+ print toolcontext.format_p("\t {mclass.name}: {val}")
end
- print toolcontext.format_h4("\tbudding mclasses (threshold: {cnvi.threshold})")
- for mclass in cnvi.above_threshold do
- print toolcontext.format_p("\t {mclass.name}: {cnvi.values[mclass]}")
+ threshold = cnvi.threshold
+ print toolcontext.format_h4("\tbudding mclasses (threshold: {threshold})")
+ for mclass in cnvi.sort do
+ var val = cnvi.values[mclass]
+ if val.to_f < threshold then break
+ print toolcontext.format_p("\t {mclass.name}: {val}")
end
- print toolcontext.format_h4("\tblooming mclasses (threshold: {cnvs.threshold})")
- for mclass in cnvs.above_threshold do
- print toolcontext.format_p("\t {mclass.name}: {cnvs.values[mclass]}")
- end
-
- print toolcontext.format_h4("\tblooming mclasses (threshold: {cnvs.threshold})")
- for mclass in cnvs.above_threshold do
- print toolcontext.format_p("\t {mclass.name}: {cnvs.values[mclass]}")
+ threshold = cnvs.threshold
+ print toolcontext.format_h4("\tblooming mclasses (threshold: {threshold})")
+ for mclass in cnvs.sort do
+ var val = cnvs.values[mclass]
+ if val.to_f < threshold then break
+ print toolcontext.format_p("\t {mclass.name}: {val}")
end
if csv then
redef fun name do return "cbms"
redef fun desc do return "branch mean size, mean number of introduction available among ancestors"
+ # Mainmodule used to compute class hierarchy.
var mainmodule: MModule
- init(mainmodule: MModule) do self.mainmodule = mainmodule
redef fun collect(mclasses) do
for mclass in mclasses do
redef fun name do return "cnvi"
redef fun desc do return "class novelty index, contribution of the class to its branch in term of introductions"
+ # Mainmodule used to compute class hierarchy.
var mainmodule: MModule
- init(mainmodule: MModule) do self.mainmodule = mainmodule
redef fun collect(mclasses) do
var cbms = new CBMS(mainmodule)
redef fun name do return "cnvs"
redef fun desc do return "class novelty score, importance of the contribution of the class to its branch"
+ # Mainmodule used to compute class hierarchy.
var mainmodule: MModule
- init(mainmodule: MModule) do self.mainmodule = mainmodule
redef fun collect(mclasses) do
var cnvi = new CNVI(mainmodule)
var opt_generate_hyperdoc = new OptionBool("Generate Hyperdoc", "--generate_hyperdoc")
# --poset
var opt_poset = new OptionBool("Complete metrics on posets", "--poset")
-
# --no-colors
var opt_nocolors = new OptionBool("Disable colors in console outputs", "--no-colors")
-
-
+ # --dir
var opt_dir = new OptionString("Directory where some statistics files are generated", "-d", "--dir")
+
+ # Output directory for metrics files.
var output_dir: String = "."
redef init
end
end
- # colorize heading 1 for console output
+ # Format and colorize a string heading of level 1 for console output.
+ #
+ # Default style is yellow and bold.
fun format_h1(str: String): String do
if opt_nocolors.value then return str
return str.yellow.bold
end
+ # Format and colorize a string heading of level 2 for console output.
+ #
+ # Default style is white and bold.
fun format_h2(str: String): String do
if opt_nocolors.value then return str
return str.bold
end
+ # Format and colorize a string heading of level 3 for console output.
+ #
+ # Default style is white and nobold.
fun format_h3(str: String): String do
if opt_nocolors.value then return str
return str
end
+ # Format and colorize a string heading of level 4 for console output.
+ #
+ # Default style is green.
fun format_h4(str: String): String do
if opt_nocolors.value then return str
return str.green
end
+ # Format and colorize a string heading of level 5 for console output.
+ #
+ # Default style is light gray.
fun format_p(str: String): String do
if opt_nocolors.value then return str
return str.light_gray
#
# The concept is reified here for a better organization and documentation
interface Metric
+
+ # Type of elements measured by this metric.
type ELM: Object
+
+ # Type of values used to measure elements.
type VAL: Object
+
+ # Type of data representation used to associate elements and values.
type RES: Map[ELM, VAL]
+ # The name of this metric (generally an acronym about the metric).
fun name: String is abstract
+
+ # A long and understandable description about what is measured by this metric.
fun desc: String is abstract
# Clear all results for this metric
end
end
+ # The sum of all the values.
+ fun sum: VAL is abstract
+
# The values standard derivation
fun std_dev: Float is abstract
# The set of element above the threshold
fun above_threshold: Set[ELM] is abstract
+
+ # Sort the metric keys by values
+ fun sort: Array[ELM] do
+ return values.keys_sorted_by_values(default_reverse_comparator)
+ end
end
# A Metric that collects integer data
redef type VAL: Int
redef type RES: Counter[ELM]
+ # `IntMetric` uses a Counter to store values in intern.
protected var values_cache = new Counter[ELM]
+
redef fun values do return values_cache
redef fun clear do values_cache.clear
- fun sum: Int do return values_cache.sum
+ redef fun sum do return values_cache.sum
redef fun max do
assert not values_cache.is_empty
end
# Values average
- redef fun avg: Float do return values_cache.avg
+ redef fun avg do return values_cache.avg
- redef fun std_dev: Float do return values_cache.std_dev
+ redef fun std_dev do return values_cache.std_dev
redef fun above_threshold do
var above = new HashSet[ELM]
redef type VAL: Float
+ # `FloatMetric` uses a Map to store values in intern.
protected var values_cache = new HashMap[ELM, VAL]
+
redef fun values do return values_cache
redef fun clear do values_cache.clear
- fun sum: Float do
+
+ redef fun sum do
var sum = 0.0
for v in values.values do sum += v
return sum
return sum / values.length.to_f
end
- redef fun std_dev: Float do
+ redef fun std_dev do
var sum = 0.0
for value in values.values do
sum += (value - avg).pow(2.to_f)
print "{"\t" * indent} sum: {sum}"
end
end
-
end
# A MetricSet is a metric holder
#
# It purpose is to be extended with a metric collect service
class MetricSet
+
+ # Type of element measured by this `MetricSet`.
type ELM: Object
# Metrics to compute
model.mmodule_importation_hierarchy.sort(mmodules)
var nmodules = new Array[AModule]
for mm in mmodules do
+ if mm.is_fictive then continue
nmodules.add(mmodule2node(mm).as(not null))
end
toolcontext.run_phases(nmodules)
# Registration of the nclassdef associated to each mclassdef
private var mclassdef2nclassdef = new HashMap[MClassDef, AClassdef]
+
+ # Retrieve the associated AST node of a mclassdef.
+ #
+ # This method is used to associate model entity with syntactic entities.
+ # If the class definition is not associated with a node, returns `null`.
+ fun mclassdef2node(mclassdef: MClassDef): nullable AClassdef do
+ return mclassdef2nclassdef.get_or_null(mclassdef)
+ end
end
redef class AModule
if vararg_rank >= 0 then
var paramtype = msignature.mparameters[vararg_rank].mtype
var first = args[vararg_rank]
- if vararg_decl == 0 and first isa AVarargExpr then
+ if vararg_decl == 0 then
var mclass = get_mclass(node, "Array")
if mclass == null then return null # Forward error
var array_mtype = mclass.get_mtype([paramtype])
- self.visit_expr_subtype(first.n_expr, array_mtype)
- first.mtype = first.n_expr.mtype
+ if first isa AVarargExpr then
+ self.visit_expr_subtype(first.n_expr, array_mtype)
+ first.mtype = first.n_expr.mtype
+ else
+ # only one vararg, maybe `...` was forgot, so be gentle!
+ var t = visit_expr(first)
+ if t == null then return null # Forward error
+ if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then
+ # Not acceptable but could be a `...`
+ error(first, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?")
+ return null
+ end
+ # Standard valid vararg, finish the job
+ map.vararg_decl = 1
+ self.visit_expr_subtype(first, paramtype)
+ end
else
map.vararg_decl = vararg_decl + 1
for i in [vararg_rank..vararg_rank+vararg_decl] do
-alt/base_vararg3_alt1.nit:41,7--13: Type Error: expected `Int`, got `Array[Int]`.
+alt/base_vararg3_alt1.nit:41,7--13: Type Error: expected `Int`, got `Array[Int]`. Is an ellipsis `...` missing on the argument?
-alt/base_vararg3_alt3.nit:33,9: Type Error: expected `Int`, got `Array[Int]`.
+alt/base_vararg3_alt3.nit:33,9: Type Error: expected `Int`, got `Array[Int]`. Is an ellipsis `...` missing on the argument?
Sys: 4.0
blooming mclasses (threshold: 12.0)
Sys: 16.0
- blooming mclasses (threshold: 12.0)
- Sys: 16.0
--- Detection of the usage of covariance static type conformance ---
-- Total --
- Kinds of the subtype -