module api_catalog
-import web_base
-import catalog
+import api_model
+import catalog::catalog_json
redef class NitwebConfig
redef init do
super
use("/catalog/packages/", new APICatalogPackages(config))
-
- use("/catalog/highlighted", new APICatalogHighLighted(config))
- use("/catalog/required", new APICatalogMostRequired(config))
- use("/catalog/bytags", new APICatalogByTags(config))
- use("/catalog/contributors", new APICatalogContributors(config))
use("/catalog/stats", new APICatalogStats(config))
use("/catalog/tags", new APICatalogTags(config))
use("/catalog/tag/:tid", new APICatalogTag(config))
+
+ use("/catalog/person/:pid", new APICatalogPerson(config))
+ use("/catalog/person/:pid/maintaining", new APICatalogMaintaining(config))
+ use("/catalog/person/:pid/contributing", new APICatalogContributing(config))
end
end
#
# Sorting is based on mpackage score.
var mpackages_sorter = new CatalogScoreSorter(config.catalog) is lazy
-
- # List the 10 best packages from `cpt`
- fun list_best(cpt: Counter[MPackage]): JsonArray do
- var res = new JsonArray
- var best = cpt.sort
- for i in [1..10] do
- if i > best.length then break
- res.add best[best.length-i]
- end
- return res
- end
-
- # List packages by group.
- fun list_by(map: MultiHashMap[Object, MPackage]): JsonObject do
- var res = new JsonObject
- var keys = map.keys.to_a
- alpha_comparator.sort(keys)
- for k in keys do
- var projs = map[k].to_a
- alpha_comparator.sort(projs)
- res[k.to_s.html_escape] = new JsonArray.from(projs)
- end
- return res
- end
end
# Get all the packages from the catalog using pagination
end
end
-class APICatalogHighLighted
- super APICatalogHandler
-
- redef fun get(req, res) do res.json list_best(config.catalog.score)
-end
-
-class APICatalogMostRequired
- super APICatalogHandler
-
- redef fun get(req, res) do
- if config.catalog.deps.not_empty then
- var reqs = new Counter[MPackage]
- for p in config.model.mpackages do
- reqs[p] = config.catalog.deps[p].smallers.length - 1
- end
- res.json list_best(reqs)
- return
- end
- res.json new JsonArray
- end
-end
-
-class APICatalogByTags
- super APICatalogHandler
-
- redef fun get(req, res) do res.json list_by(config.catalog.tag2proj)
-end
-
-class APICatalogContributors
- super APICatalogHandler
-
- redef fun get(req, res) do
- var obj = new JsonObject
- obj["maintainers"] = new JsonArray.from(config.catalog.maint2proj.keys)
- obj["contributors"] = new JsonArray.from(config.catalog.contrib2proj.keys)
- res.json obj
- end
-end
-
# Get the catalog statistics
#
# `GET /stats`: return the catalog statistics
res.json obj
end
end
+
+# 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
+
redef class Catalog
# Build the catalog from `mpackages`
end
end
-redef class MPackageMetadata
- serialize
-
- redef fun core_serialize_to(v) do
- super
- v.serialize_attribute("license", license)
- v.serialize_attribute("maintainers", maintainers)
- v.serialize_attribute("contributors", contributors)
- v.serialize_attribute("tags", tags)
- v.serialize_attribute("tryit", tryit)
- v.serialize_attribute("apk", apk)
- v.serialize_attribute("homepage", homepage)
- v.serialize_attribute("browse", browse)
- v.serialize_attribute("git", git)
- v.serialize_attribute("issues", issues)
- v.serialize_attribute("first_date", first_date)
- v.serialize_attribute("last_date", last_date)
- end
-end
-
-# Catalog statistics
-redef class CatalogStats
- serialize
-
- redef fun core_serialize_to(v) do
- super
- v.serialize_attribute("packages", packages)
- v.serialize_attribute("maintainers", maintainers)
- v.serialize_attribute("contributors", contributors)
- v.serialize_attribute("tags", tags)
- v.serialize_attribute("modules", modules)
- v.serialize_attribute("classes", classes)
- v.serialize_attribute("methods", methods)
- v.serialize_attribute("loc", loc)
- end
-end
-
-# MPackage statistics for the catalog
-redef class MPackageStats
- serialize
-
- redef fun core_serialize_to(v) do
- super
- v.serialize_attribute("mmodules", mmodules)
- v.serialize_attribute("mclasses", mclasses)
- v.serialize_attribute("mmethods", mmethods)
- v.serialize_attribute("loc", loc)
- v.serialize_attribute("errors", errors)
- v.serialize_attribute("warnings", warnings)
- v.serialize_attribute("warnings_per_kloc", warnings_per_kloc)
- v.serialize_attribute("documentation_score", documentation_score)
- v.serialize_attribute("commits", commits)
- v.serialize_attribute("score", score)
- end
-end
-
-redef class Person
- serialize
-
- redef fun core_serialize_to(v) do
- super
- v.serialize_attribute("name", name)
- v.serialize_attribute("email", email)
- v.serialize_attribute("gravatar", gravatar)
- end
-end
-
redef class MPackage
# Serialize the full catalog version of `self` to JSON
#
# See: `FullCatalogSerializer`
- fun to_full_catalog_json(catalog: Catalog, plain, pretty: nullable Bool): String do
+ fun to_full_catalog_json(mainmodule: MModule, catalog: Catalog, plain, pretty: nullable Bool): String do
var stream = new StringWriter
- var serializer = new FullCatalogSerializer(stream, catalog)
+ var serializer = new FullCatalogSerializer(stream, mainmodule, catalog)
serializer.plain_json = plain or else false
serializer.pretty_json = pretty or else false
serializer.serialize self