show student basics score in class teacher dashboard - for #6414
parent
993ce86407
commit
03fc4d242c
|
@ -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 =>
|
||||
|
|
|
@ -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")(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue