show student basics score in class teacher dashboard - for #6414

pull/6464/head
Thibault Duplessis 2020-04-24 12:37:09 -06:00
parent 993ce86407
commit 03fc4d242c
5 changed files with 130 additions and 32 deletions

View File

@ -179,6 +179,18 @@ final class Clas(
}
}
def learn(id: String) = Secure(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { clas =>
env.clas.api.student.activeWithUsers(clas) flatMap { students =>
Reasonable(clas, students, "progress") {
env.learn.api.completionPercent(students.map(_.user.id)) map { learn =>
views.html.clas.teacherDashboard.learn(clas, students, learn)
}
}
}
}
}
def edit(id: String) = Secure(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { clas =>
env.clas.api.student.activeWithUsers(clas) map { students =>

View File

@ -102,37 +102,7 @@ object teacherDashboard {
progress: ClasProgress
)(implicit ctx: Context) =
layout(c, students, "progress")(
div(cls := "progress")(
div(cls := "progress-perf")(
label(trans.variant()),
div(cls := "progress-choices")(
List(
PerfType.Bullet,
PerfType.Blitz,
PerfType.Rapid,
PerfType.Classical,
PerfType.Correspondence,
PerfType.Puzzle
).map { pt =>
a(
cls := progress.perfType.key.active(pt.key),
href := routes.Clas.progress(c.id.value, pt.key, progress.days)
)(pt.trans),
}
)
),
div(cls := "progress-days")(
label(trans.clas.overDays()),
div(cls := "progress-choices")(
List(1, 2, 3, 7, 10, 14, 21, 30, 60, 90).map { days =>
a(
cls := progress.days.toString.active(days.toString),
href := routes.Clas.progress(c.id.value, progress.perfType.key, days)
)(days)
}
)
)
),
progressHeader(c, progress.some),
div(cls := "students")(
table(cls := "slist slist-pad sortable")(
thead(
@ -169,6 +139,77 @@ object teacherDashboard {
)
)
def learn(
c: Clas,
students: List[Student.WithUser],
learnCompletion: Map[lila.user.User.ID, Int]
)(implicit ctx: Context) =
layout(c, students, "progress")(
progressHeader(c, none),
div(cls := "students")(
table(cls := "slist slist-pad sortable")(
thead(
tr(
th(attr("data-sort-default") := "1")(
trans.clas.nbStudents.pluralSame(students.size),
sortNumberTh("Basics")
)
),
tbody(
students.sortBy(_.user.username).map {
case s @ Student.WithUser(_, user) =>
tr(
studentTd(c, s),
td(dataSort := learnCompletion.getOrElse(user.id, 0))(
learnCompletion.getOrElse(user.id, 0).toString,
"%"
)
)
}
)
)
)
)
)
private def progressHeader(c: Clas, progress: Option[ClasProgress])(implicit ctx: Context) =
div(cls := "progress")(
div(cls := "progress-perf")(
label(trans.variant()),
div(cls := "progress-choices")(
List(
PerfType.Bullet,
PerfType.Blitz,
PerfType.Rapid,
PerfType.Classical,
PerfType.Correspondence,
PerfType.Puzzle
).map { pt =>
a(
cls := progress.map(_.perfType.key.active(pt.key)),
href := routes.Clas.progress(c.id.value, pt.key, progress.fold(7)(_.days))
)(pt.trans),
},
a(cls := progress.isEmpty.option("active"), href := routes.Clas.learn(c.id.value))(
trans.learnMenu()
)
)
),
progress.map { p =>
div(cls := "progress-days")(
label(trans.clas.overDays()),
div(cls := "progress-choices")(
List(1, 2, 3, 7, 10, 14, 21, 30, 60, 90).map { days =>
a(
cls := p.days.toString.active(days.toString),
href := routes.Clas.progress(c.id.value, p.perfType.key, days)
)(days)
}
)
)
}
)
private def studentList(c: Clas, students: List[Student.WithUser])(implicit ctx: Context) =
div(cls := "students")(
table(cls := "slist slist-pad sortable")(

View File

@ -474,6 +474,7 @@ GET /class/$id<\w{8}>/notify controllers.Clas.notifyStudents(id: Strin
POST /class/$id<\w{8}>/notifyPost controllers.Clas.notifyPost(id: String)
POST /class/$id<\w{8}>/archive controllers.Clas.archive(id: String, v: Boolean)
GET /class/$id<\w{8}>/archived controllers.Clas.archived(id: String)
GET /class/$id<\w{8}>/progress/learn controllers.Clas.learn(id: String)
GET /class/$id<\w{8}>/progress/:pt/:days controllers.Clas.progress(id: String, pt: String, days: Int)
GET /class/$id<\w{8}>/student/add controllers.Clas.studentForm(id: String)
POST /class/$id<\w{8}>/student/new controllers.Clas.studentCreate(id: String)

View File

@ -1,5 +1,7 @@
package lila.learn
import reactivemongo.api.ReadPreference
import lila.db.dsl._
import lila.user.User
@ -20,4 +22,46 @@ final class LearnApi(coll: Coll)(implicit ec: scala.concurrent.ExecutionContext)
def reset(user: User) =
coll.delete.one($id(user.id)).void
private val maxCompletion = 110
def completionPercent(userIds: List[User.ID]): Fu[Map[User.ID, Int]] =
coll
.aggregateList(
maxDocs = Int.MaxValue,
readPreference = ReadPreference.secondaryPreferred
) { framework =>
import framework._
Match($doc("_id" $in userIds)) -> List(
Project($doc("stages" -> $doc("$objectToArray" -> "$stages"))),
UnwindField("stages"),
Project(
$doc(
"stages" -> $doc(
"$size" -> $doc(
"$filter" -> $doc(
"input" -> "$stages.v",
"as" -> "s",
"cond" -> $doc(
"$ne" -> $arr("$$s", 0)
)
)
)
)
)
),
GroupField("_id")("nb" -> SumField("stages"))
)
}
.map {
_.view
.flatMap { obj =>
(obj.string("_id") |@| obj.int("nb")).tupled
}
.map {
case (k, v) => k -> (v * 100f / maxCompletion).toInt
}
.toMap
}
}

View File

@ -38,7 +38,7 @@ function getStageMaxScore(s) {
function getStageRank(s, score) {
var max = getStageMaxScore(s);
if (typeof score !== 'number') score = score.reduce(function(a, b) { return a + b; }, 0);
if (typeof score !== 'number') score = score.reduce((a, b) => a + b, 0);
if (score >= max) return 1;
if (score >= max - Math.max(200, s.levels.length * 150)) return 2;
return 3;