1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Feedback related features
19 import popcorn
::pop_auth
21 redef class NitwebConfig
23 # MongoDB collection used to store stars.
24 var stars
= new StarRatingRepo(db
.collection
("stars")) is lazy
32 use
("/feedback/grades/most", new APIStarsMost(config
))
33 use
("/feedback/grades/best", new APIStarsBest(config
))
34 use
("/feedback/grades/worst", new APIStarsWorst(config
))
35 use
("/feedback/grades/users", new APIStarsUsers(config
))
37 use
("/feedback/user/stars", new APIUserStars(config
))
39 use
("/feedback/stars/:id", new APIStars(config
))
40 use
("/feedback/stars/:id/dimension/:dimension", new APIStarsDimension(config
))
44 # Base handler for feedback features.
45 abstract class APIFeedBack
48 # Get the user logged in or null if no session
49 fun get_session_user
(req
: HttpRequest): nullable User do
50 var session
= req
.session
51 if session
== null then return null
55 # Get the login of the session user or null if no session
56 fun get_session_login
(req
: HttpRequest): nullable String do
57 var user
= get_session_user
(req
)
58 if user
== null then return null
67 redef fun get
(req
, res
) do
68 res
.json
new JsonArray.from
(config
.stars
.most_rated
)
76 redef fun get
(req
, res
) do
77 res
.json
new JsonArray.from
(config
.stars
.best_rated
)
85 redef fun get
(req
, res
) do
86 res
.json
new JsonArray.from
(config
.stars
.worst_rated
)
94 redef fun get
(req
, res
) do
95 res
.json
new JsonArray.from
(config
.stars
.users_ratings
)
99 # Stars attributed to mentities by user
103 redef fun get
(req
, res
) do
104 var user
= get_session_user
(req
)
105 if user
== null then return
106 res
.json
new JsonArray.from
(user
.ratings
(config
))
110 # Stars attributed to mentities
114 redef fun get
(req
, res
) do
115 var login
= get_session_login
(req
)
116 var mentity
= mentity_from_uri
(req
, res
)
117 if mentity
== null then return
118 res
.json mentity
.ratings
(config
, login
)
122 # Stars attributed to mentities by dimension
123 class APIStarsDimension
126 redef fun get
(req
, res
) do
127 var login
= get_session_login
(req
)
128 var mentity
= mentity_from_uri
(req
, res
)
129 if mentity
== null then return
130 var dimension
= req
.param
("dimension")
131 if dimension
== null then return
132 res
.json mentity
.ratings_by_dimension
(config
, dimension
, login
)
135 redef fun post
(req
, res
) do
136 var user
= get_session_user
(req
)
138 if user
!= null then login
= user
.login
140 var mentity
= mentity_from_uri
(req
, res
)
141 if mentity
== null then return
142 var dimension
= req
.param
("dimension")
143 if dimension
== null then return
145 # Retrieve user previous rating
148 previous
= user
.find_previous_rating
(config
, mentity
, dimension
)
151 var obj
= req
.body
.parse_json
152 if not obj
isa JsonObject then
153 res
.api_error
(400, "Expected a JSON object")
156 var rating
= obj
["rating"]
157 if not rating
isa Int then
158 res
.api_error
(400, "Expected a key `rating`")
162 if previous
!= null then
163 previous
.rating
= rating
164 previous
.timestamp
= get_time
165 config
.stars
.save previous
167 config
.stars
.save
new StarRating(login
, mentity
.full_name
, dimension
, rating
)
169 res
.json mentity
.ratings_by_dimension
(config
, dimension
, login
)
173 # Star ratings allow users to rate mentities with a 5-stars system.
175 # Each rating can consider only one `dimension` of the mentity.
176 # Dimensions are arbitrary strings used to group ratings.
181 # The user login that made that rating (or null if anon)
182 var user
: nullable String
184 # Rated `MEntity::full_name`
187 # The dimension rated (arbritrary key)
188 var dimension
: nullable String
190 # The rating (traditionally a score between 0 and 5)
191 var rating
: Int is writable
193 # Timestamp when this rating was created
194 var timestamp
= 0 is writable
199 # Find a previous rating of `self` for `mentity` and `dimension`
200 fun find_previous_rating
(config
: NitwebConfig, mentity
: MEntity, dimension
: nullable String): nullable StarRating do
201 var match
= new MongoMatch
202 match
.eq
("mentity", mentity
.full_name
)
203 match
.eq
("dimension", dimension
)
204 match
.eq
("user", login
)
205 return config
.stars
.find
(match
)
208 # Find all ratings by `self`
209 fun ratings
(config
: NitwebConfig): Array[StarRating] do
210 return config
.stars
.find_all
((new MongoMatch).eq
("user", login
))
216 # Get the ratings of a `dimension`
217 fun ratings_by_dimension
(config
: NitwebConfig, dimension
: String, user
: nullable String): JsonObject do
218 var match
= (new MongoMatch).eq
("mentity", full_name
).eq
("dimension", dimension
)
219 var pipeline
= new MongoPipeline
220 pipeline
.match
(match
)
221 pipeline
.group
((new MongoGroup("mean_group")).avg
("mean", "$rating"))
223 var res
= config
.stars
.collection
.aggregate
(pipeline
)
224 var obj
= new JsonObject
225 obj
["mean"] = if res
.is_empty
then 0.0 else res
.first
["mean"]
226 obj
["ratings"] = new JsonArray.from
(config
.stars
.find_all
(match
))
230 obj
["user"] = config
.stars
.find
(match
)
235 # Get the ratings of a `mentity`
236 fun ratings
(config
: NitwebConfig, user
: nullable String): JsonObject do
237 var match
= new JsonObject
238 match
["mentity"] = full_name
239 match
["ratings"] = new JsonArray.from
(config
.stars
.find_all
(match
))
240 match
["feature"] = ratings_by_dimension
(config
, "feature", user
)
241 match
["doc"] = ratings_by_dimension
(config
, "doc", user
)
242 match
["examples"] = ratings_by_dimension
(config
, "examples", user
)
243 match
["code"] = ratings_by_dimension
(config
, "code", user
)
248 # StarRating Mongo Repository
250 super MongoRepository[StarRating]
252 # Find most rated mentities
253 fun most_rated
: Array[JsonObject] do
254 var pipeline
= new MongoPipeline
255 pipeline
.group
((new MongoGroup("$mentity")).sum
("count", 1))
256 pipeline
.sort
((new MongoMatch).eq
("count", -1))
258 return collection
.aggregate
(pipeline
)
261 # Find best rated mentities
262 fun best_rated
: Array[JsonObject] do
263 var pipeline
= new MongoPipeline
264 pipeline
.group
((new MongoGroup("$mentity")).avg
("avg", "$rating"))
265 pipeline
.sort
((new MongoMatch).eq
("avg", -1))
267 return collection
.aggregate
(pipeline
)
270 # Find worst rated mentities
271 fun worst_rated
: Array[JsonObject] do
272 var pipeline
= new MongoPipeline
273 pipeline
.group
((new MongoGroup("$mentity")).avg
("avg", "$rating"))
274 pipeline
.sort
((new MongoMatch).eq
("avg", 1))
276 return collection
.aggregate
(pipeline
)
279 # Find worst rated mentities
280 fun users_ratings
: Array[JsonObject] do
281 var pipeline
= new MongoPipeline
282 pipeline
.group
((new MongoGroup("$user")).sum
("count", 1))
283 pipeline
.sort
((new MongoMatch).eq
("count", -1))
285 return collection
.aggregate
(pipeline
)