+
+# Get a person existing in the catalog
+#
+# `GET /person/:pid`: get the person with `pid`
+class APICatalogPerson
+ super APICatalogHandler
+
+ # Get the person with `:pid` or throw a 404 error
+ fun get_person(req: HttpRequest, res: HttpResponse): nullable Person do
+ var id = req.param("pid")
+ if id == null then
+ res.api_error(400, "Missing package full_name")
+ return null
+ end
+ id = id.from_percent_encoding
+ if not config.catalog.name2person.has_key(id) then
+ res.api_error(404, "Person not found")
+ return null
+ end
+ return config.catalog.name2person[id]
+ end
+
+ redef fun get(req, res) do
+ var person = get_person(req, res)
+ if person == null then return
+ res.json person
+ end
+end
+
+# Get the list of mpackages maintained by a person
+#
+# `GET /person/:pid/maintaining?p=1&n=10`: return a paginated list of packages
+class APICatalogMaintaining
+ super APICatalogPerson
+
+ redef fun get(req, res) do
+ var person = get_person(req, res)
+ if person == null then return
+
+ var page = req.int_arg("p")
+ var limit = req.int_arg("n")
+ var array = new Array[MPackage]
+ if config.catalog.maint2proj.has_key(person) then
+ array = config.catalog.maint2proj[person].to_a
+ end
+ mpackages_sorter.sort(array)
+ var response = new JsonArray.from(array)
+ res.json paginate(response, response.length, page, limit)
+ end
+end
+
+# Get the list of mpackages contributed by a person
+#
+# `GET /person/:pid/contributing?p=1&n=10`: return a paginated list of packages
+class APICatalogContributing
+ super APICatalogPerson
+
+ redef fun get(req, res) do
+ var person = get_person(req, res)
+ if person == null then return
+
+ var page = req.int_arg("p")
+ var limit = req.int_arg("n")
+ var array = new Array[MPackage]
+ if config.catalog.contrib2proj.has_key(person) then
+ array = config.catalog.contrib2proj[person].to_a
+ end
+ mpackages_sorter.sort(array)
+ var response = new JsonArray.from(array)
+ res.json paginate(response, response.length, page, limit)
+ end
+end
+
+redef class APIEntity
+ redef fun get(req, res) do
+ var mentity = mentity_from_uri(req, res)
+ if mentity == null then return
+
+ # Special case for packages (catalog view)
+ if mentity isa MPackage then
+ res.raw_json mentity.to_full_catalog_json(plain=true, config.mainmodule, config.catalog)
+ else
+ res.raw_json mentity.to_full_json(config.mainmodule)
+ end
+ end
+end
+
+redef class APISearch
+ super APICatalogHandler
+
+ redef fun search(query, limit) do
+ var index = config.view.index
+
+ # lookup by name prefix
+ var matches = index.find_by_name_prefix(query).uniq.
+ sort(lname_sorter, name_sorter, kind_sorter)
+ matches = matches.rerank.sort(vis_sorter, score_sorter)
+
+ # lookup by tags
+ var malus = matches.length
+ if config.catalog.tag2proj.has_key(query) then
+ for mpackage in config.catalog.tag2proj[query] do
+ matches.add new IndexMatch(mpackage, malus)
+ malus += 1
+ end
+ matches = matches.uniq.rerank.sort(vis_sorter, score_sorter)
+ end
+
+ # lookup by full_name prefix
+ malus = matches.length
+ var full_matches = new IndexMatches
+ for match in index.find_by_full_name_prefix(query).
+ sort(lfname_sorter, fname_sorter) do
+ match.score += 1
+ full_matches.add match
+ end
+ matches = matches.uniq
+
+ # lookup by similarity
+ malus = matches.length
+ var sim_matches = new IndexMatches
+ for match in index.find_by_similarity(query).sort(score_sorter, lname_sorter, name_sorter) do
+ if match.score > query.length then break
+ match.score += 1
+ sim_matches.add match
+ end
+ matches.add_all sim_matches
+ matches = matches.uniq
+ return matches.rerank.sort(vis_sorter, score_sorter).mentities
+ end
+
+ private var score_sorter = new ScoreComparator
+ private var vis_sorter = new VisibilityComparator
+ private var name_sorter = new NameComparator
+ private var lname_sorter = new NameLengthComparator
+ private var fname_sorter = new FullNameComparator
+ private var lfname_sorter = new FullNameLengthComparator
+ private var kind_sorter = new MEntityComparator
+end
+