Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oops, disregard this PR, never used or released #4944

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
dbda04a
Don't load facilities into session object.
rtibbles Feb 24, 2016
8f29d83
Simplify the way we render FacilityGroup forms.
rtibbles Feb 25, 2016
db0f4f8
Catch pops from an empty list.
rtibbles Feb 26, 2016
c5274d4
removed overlapping text of content recomendation explore
manipalsingh013 Feb 26, 2016
4570bc7
Add catches for missing data or None returns in content data.
rtibbles Feb 26, 2016
63bfd09
Adds test for resume recommendations returning empty list.
rtibbles Feb 26, 2016
a804fb7
Merge pull request #4912 from rtibbles/dont_load_all_facilities
benjaoming Feb 26, 2016
1bb9206
changed classes to avoid content text overlapping
manipalsingh013 Feb 26, 2016
e32f47e
Merge pull request #4921 from rtibbles/prevent_pop_error
aronasorman Feb 26, 2016
7309b49
Ensures proper indexing in contentrec.
rtibbles Feb 26, 2016
4858408
Make sure 'children' has contents.
rtibbles Feb 26, 2016
0cd1ec2
Only set the left of the sidebar tab if it is open already.
rtibbles Feb 26, 2016
f8dc220
Add more makemessage default ignore patterns.
aronasorman Feb 26, 2016
a29f147
shows a message when search returns no content
shubhsingh594 Feb 27, 2016
0b2057a
Merge branch '0.16.x' of https://github.com/learningequality/ka-lite …
shubhsingh594 Feb 27, 2016
1101886
Change message for the user while they wait for server response
radinamatic Feb 29, 2016
0fdf904
Only mess around with sidebar attributes if it is already open (unles…
rtibbles Feb 29, 2016
b850786
Merge pull request #4936 from radinamatic/waiting-for-server
rtibbles Feb 29, 2016
0050620
Merge pull request #4922 from manipalsingh013/bug_3863
rtibbles Feb 29, 2016
44b9696
Throttle, don't debounce.
rtibbles Feb 29, 2016
6560fab
made suggested changes using if not matches.
shubhsingh594 Feb 29, 2016
fd825e2
Merge pull request #4928 from shubhsingh594/new_message
rtibbles Feb 29, 2016
c0fbeb0
Merge pull request #4923 from rtibbles/robustify_content_rec
aronasorman Feb 29, 2016
3a712ad
Actually pass, parse, and then pass again, the language argument for …
rtibbles Feb 29, 2016
9f89ddc
Select child nodes once lazy loaded.
rtibbles Feb 29, 2016
9eb9b83
Set languages properly for getting list of youtube_ids for download, …
rtibbles Feb 29, 2016
22792f8
Prevent name shadowing in view func
MCGallaspy Feb 19, 2016
e9a73cd
Translate exercises correctly
MCGallaspy Feb 19, 2016
63eac5c
Add some tests of content_item api_view
MCGallaspy Feb 19, 2016
bf2785d
Amend test
MCGallaspy Feb 24, 2016
0117067
Merge pull request #4925 from aronasorman/update-makemessages
MCGallaspy Feb 29, 2016
aa72e1b
Adds regression test to ensure that language is always properly passe…
rtibbles Feb 29, 2016
128966c
Propagates topic selection to tabular coach report.
rtibbles Feb 24, 2016
7f8dee3
Adds uniquifying and sorting to content headers for tabular reports.
rtibbles Feb 26, 2016
efccc0e
Adds API endpoint test for coach report topic filtering.
rtibbles Feb 26, 2016
02d1bd3
Adds updated tests for coachreport learnerlog api endpoint.
rtibbles Feb 27, 2016
82fe6fc
Merge pull request #4872 from MCGallaspy/random-fixes
rtibbles Feb 29, 2016
9ba161d
Allow positional arguments with dashes inside
MCGallaspy Feb 29, 2016
b5ca734
Do not reference nonexistent file
MCGallaspy Feb 29, 2016
f09aa9a
Allow status updating of non-English videos in video downloading inte…
rtibbles Mar 1, 2016
ff3bb02
Merge pull request #4938 from rtibbles/video_download_fixes
aronasorman Mar 1, 2016
41bfb04
Remove that custom pt-BR -> pt mapping.
aronasorman Mar 1, 2016
1fa271a
Merge pull request #4940 from aronasorman/br-br
aronasorman Mar 1, 2016
081deee
Merge pull request #4919 from rtibbles/tabular_report_topics
MCGallaspy Mar 1, 2016
1d79c20
Merge pull request #4939 from MCGallaspy/4935-arg-parse-bug
rtibbles Mar 1, 2016
85136b1
Fixes display issues for icomoon icons used in content recommendation.
rtibbles Mar 1, 2016
8a1214a
Merge pull request #4941 from rtibbles/fix_icomoon_font_content_rec
aronasorman Mar 1, 2016
cf18d46
Prevent TypeErrors getting thrown on partial downloads.
rtibbles Mar 2, 2016
4796174
bump version
benjaoming Mar 2, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ If not all TODOs are marked, this PR is considered WIP (work in progress)
- [ ] Have **tests** been written for the new code? If you're fixing a bug, write a regression test (or have a really good reason for not writing one... and I mean **really** good!)
- [ ] Has documentation been written/updated?
- [ ] New dependencies (if any) added to requirements file
- [ ] Add an entry to CHANGELOG.rst

## Reviewer guidance

Expand Down
28 changes: 27 additions & 1 deletion kalite/coachreports/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@
from kalite.shared.decorators.auth import require_admin
from kalite.topic_tools.content_models import get_topic_contents, get_topic_nodes, get_leafed_topics, get_content_parents


def unique_by_id_and_kind_sort(seq):
"""
Due to the fact that we have duplicate content items for the same content id in our topic tree, as the way that
we have implemented duplication of content across the topic tree.
:param seq: an iterator of content items.
:return: A unique, sorted list of content items.
"""

seq.sort(key=lambda x: x.get("sort_order", 0))
seen = {}
result = []
for item in seq:
marker = item.get("id") + item.get("kind")

if marker in seen:
continue
seen[marker] = 1
result.append(item)
return result


def get_learners_from_GET(request):
learner_ids = request.GET.getlist("user_id")

Expand Down Expand Up @@ -74,7 +96,7 @@ def learner_logs(request):

end_date = request.GET.get("end_date")

topic_ids = request.GET.getlist("topic_id", [])
topic_ids = json.loads(request.GET.get("topic_ids", "[]"))

learners = get_learners_from_GET(request)

Expand Down Expand Up @@ -107,8 +129,12 @@ def learner_logs(request):
output_objects.extend(objects)
output_logs.extend(log_objects)

output_objects = unique_by_id_and_kind_sort(output_objects)

return JsonResponse({
# All learner log objects for each content item.
"logs": output_logs,
# All content items for which logs are being returned.
"contents": output_objects,
# Sometimes 'learners' gets collapsed to a list from the Queryset. This insures against that eventuality.
"learners": [{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ var StateModel = Backbone.Model.extend({
group_name: undefined
});
}
if (key === "facility" || key.facility || key === "group" || key.group) {
this.set({
topic_ids: undefined
});
}

Backbone.Model.prototype.set.call(this, key, val, options);
}
Expand All @@ -34,14 +39,16 @@ var CoachReportModel = Backbone.Model.extend({
this.group = options.group;
this.start_date = options.start_date;
this.end_date = options.end_date;
this.topic_ids = options.topic_ids;
},

url: function() {
return setGetParamDict(Urls.learner_logs(), {
facility_id: this.facility,
group_id: this.group,
start_date: this.start_date,
end_date: this.end_date
end_date: this.end_date,
topic_ids: this.topic_ids
});
}
});
Expand Down
26 changes: 17 additions & 9 deletions kalite/coachreports/static/js/coachreports/coach_reports/views.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ var CoachSummaryView = BaseView.extend({

events: {
"click #show_tabular_report": "toggle_tabular_view",
"click #topic-list-submit": "set_data_model"
"click #topic-list-submit": "set_topics"
},


Expand All @@ -100,7 +100,7 @@ var CoachSummaryView = BaseView.extend({
var targetElem = $("#topic-list").get(0);
var frag = document.createDocumentFragment();

var tids = $.parseJSON(this.data_model.get("topic_ids"));
var tids = $.parseJSON(this.data_model.get("topic_ids") || "[]");
var ctr = -1;

parseData.forEach(function(datum, index) {
Expand Down Expand Up @@ -196,14 +196,19 @@ var CoachSummaryView = BaseView.extend({
_.bindAll(this, "set_data_model", "render");
this.listenTo(this.model, "change:facility", this.set_data_model);
this.listenTo(this.model, "change:group", this.set_data_model);
this.listenTo(this.model, "change:topic_ids", this.set_data_model);
this.listenTo(this.model, "set_time", this.set_data_model);
this.set_data_model();
},

set_topics: function() {
var topic_ids = _.map(this.$("#topic-list option:checked"), function (node) {return node.value;});
this.model.set("topic_ids", JSON.stringify(topic_ids));
},

set_data_model: function (){
if (this.data_model) {
var check_fields = ["facility", "group", "start_date", "end_date"];
var check_fields = ["facility", "group", "start_date", "end_date", "topic_ids"];
var data_fields = _.pick(this.data_model.attributes, check_fields);
var status_fields = _.pick(this.model.attributes, check_fields);
if (!_.isEqual(data_fields, status_fields)) {
Expand All @@ -212,13 +217,12 @@ var CoachSummaryView = BaseView.extend({
}

if (!this.data_model) {
var topic_ids = _.map(this.$("#topic-list option:checked"), function (node) {return node.value;});
this.data_model = new Models.CoachReportAggregateModel({
facility: this.model.get("facility"),
group: this.model.get("group"),
start_date: date_string(this.model.get("start_date")),
end_date: date_string(this.model.get("end_date")),
topic_ids: JSON.stringify(topic_ids)
topic_ids: this.model.get("topic_ids")
});
if (this.model.get("facility")) {
this.listenTo(this.data_model, "sync", this.render);
Expand All @@ -242,14 +246,16 @@ var CoachSummaryView = BaseView.extend({
var w = 500;
var dataset = [struggling/total, complete/total, in_progress/total];

console.log(dataset);

var svg = d3.select("div.progressbar").append("svg").attr("width", w).attr("height", h).
attr("class", "col-md-8 innerbar");

svg.selectAll("rect").data(dataset).enter().append("rect").attr("x", function(d, i){
return _.reduce(dataset.slice(0, i), function(memo, num) { return memo + num; }, 0) * w;
var out = _.reduce(dataset.slice(0, i), function(memo, num) { return memo + num; }, 0) * w;
out = isNaN(out) ? 0 : out;
return out;
}).attr("y", 0).attr("width", function(d) {
return d * w;
return isNaN(d * w) ? 0 : d*w;
}).attr("height", h).attr("class", "rect").attr("class", function(d, i){
switch(i) {
case(0):
Expand All @@ -272,7 +278,9 @@ var CoachSummaryView = BaseView.extend({
return in_progress;
}
}).attr("fill", "black").attr("x", function(d, i){
return (_.reduce(dataset.slice(0, i), function(memo, num) { return memo + num; }, 0) + d/2) * w;
var out = (_.reduce(dataset.slice(0, i), function(memo, num) { return memo + num; }, 0) + d/2) * w;
out = isNaN(out) ? 0 : out;
return out;
}).attr("y", h/2).attr("font-size", "12px").style("text-anchor", "middle");

},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ var TabularReportView = BaseView.extend({
facility: this.model.get("facility"),
group: this.model.get("group"),
start_date: date_string(this.model.get("start_date")),
end_date: date_string(this.model.get("end_date"))
end_date: date_string(this.model.get("end_date")),
topic_ids: this.model.get("topic_ids")
});
if (this.model.get("facility")) {
this.data_model.fetch().then(function() {
Expand Down
115 changes: 114 additions & 1 deletion kalite/coachreports/tests/coachreport_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from kalite.testing.mixins.securesync_mixins import CreateZoneMixin
from kalite.testing.mixins.facility_mixins import FacilityMixins
from kalite.testing.mixins.student_progress_mixins import StudentProgressMixin
from kalite.topic_tools.content_models import get_content_parents, Item ,set_database
from kalite.topic_tools.content_models import get_content_parents, Item, set_database, create


class ExternalAPITests(FacilityMixins,
Expand Down Expand Up @@ -78,6 +78,8 @@ def setUp(self):
self.group = self.create_group(name='group1', facility=self.facility)
self.empty_group = self.create_group(name='empty_group', facility=self.facility)

self.facility_user = self.create_student()

def test_learner_log_endpoint(self):
response_keys = ["logs","contents","learners","page","pages","limit"]
self.client.login(username='admin', password='admin')
Expand Down Expand Up @@ -139,3 +141,114 @@ def test_playlist_progress(self):
self.assertEqual(len(exercises),1)
#checking if the returned list is accurate
self.assertEqual(exercises[0]["title"],"Comparing two-digit numbers")


class LearnerLogAPITests(FacilityMixins,
StudentProgressMixin,
CreateZoneMixin,
CreateAdminMixin,
KALiteTestCase):
"""
These tests test the learner log API endpoint in the coachreports app.
"""

def setUp(self):
super(LearnerLogAPITests, self).setUp()

self.admin_data = {"username": "admin", "password": "admin"}
self.admin = self.create_admin(**self.admin_data)

self.zone = self.create_zone()
self.device_zone = self.create_device_zone(self.zone)
self.facility = self.create_facility()

self.group = self.create_group(name='group1', facility=self.facility)
self.empty_group = self.create_group(name='empty_group', facility=self.facility)

self.facility_user = self.create_student()

self.topic1 = create({
"id": "stuff_here",
"slug": "stuff_here",
"path": "qwrqweqweqwe",
"kind": "Topic",
"title": "",
"description": "",
"available": True,
})
self.topic2 = create({
"id": "stuff_there",
"slug": "stuff_there",
"path": "qwrqweqweqw",
"kind": "Topic",
"title": "",
"description": "",
"available": True,
})

self.exercise1 = create({
"id": "stuff_here_ex",
"slug": "stuff_here_ex",
"path": "qwrqweqweqwe/qwrqweqweq",
"kind": "Exercise",
"title": "",
"description": "",
"available": True,
"parent": self.topic1,
})
self.exercise2 = create({
"id": "stuff_there_ex",
"slug": "stuff_there_ex",
"path": "qwrqweqweqw/qwrqweqw",
"kind": "Exercise",
"title": "",
"description": "",
"available": True,
"parent": self.topic2,
})

self.create_exercise_log(user=self.facility_user, exercise_id=self.exercise1.id)
self.create_exercise_log(user=self.facility_user, exercise_id=self.exercise2.id)

def tearDown(self):
self.exercise1.delete()
self.exercise2.delete()
self.topic1.delete()
self.topic2.delete()

def test_learner_log_topic_filters(self):

self.client.login(username='admin', password='admin')
api_resp_1 = json.loads(self.client.get("%s?facility_id=%s" % (self.reverse("learner_logs"), self.facility.id)).content)
api_resp_2 = json.loads(self.client.get("%s?facility_id=%s&topic_ids=%s" % (self.reverse("learner_logs"), self.facility.id, json.dumps([self.topic1.id]))).content)
assert len(api_resp_2["contents"]) < len(api_resp_1["contents"])

def test_learner_log_topic_filters_contents_length(self):

self.client.login(username='admin', password='admin')
api_resp_2 = json.loads(self.client.get("%s?facility_id=%s&topic_ids=%s" % (self.reverse("learner_logs"), self.facility.id, json.dumps([self.topic1.id]))).content)
assert len(api_resp_2["contents"]) == 1

def test_learner_log_topic_filters_contents_id(self):

self.client.login(username='admin', password='admin')
api_resp_2 = json.loads(self.client.get("%s?facility_id=%s&topic_ids=%s" % (self.reverse("learner_logs"), self.facility.id, json.dumps([self.topic1.id]))).content)
assert api_resp_2["contents"][0]["id"] == self.exercise1.id

def test_learner_log_contents(self):

self.client.login(username='admin', password='admin')
api_resp_1 = json.loads(self.client.get("%s?facility_id=%s" % (self.reverse("learner_logs"), self.facility.id)).content)
assert len(api_resp_1["contents"]) == 2

def test_learner_log_logs(self):

self.client.login(username='admin', password='admin')
api_resp_1 = json.loads(self.client.get("%s?facility_id=%s" % (self.reverse("learner_logs"), self.facility.id)).content)
assert len(api_resp_1["logs"]) == 2

def test_learner_log_log_contents(self):

self.client.login(username='admin', password='admin')
api_resp_2 = json.loads(self.client.get("%s?facility_id=%s&topic_ids=%s" % (self.reverse("learner_logs"), self.facility.id, json.dumps([self.topic1.id]))).content)
assert api_resp_2["logs"][0]["exercise_id"] == self.exercise1.id
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
.content-nextsteps-topic-link div {
background-color: #5aa685;
color: white;
}
}

i.content-rec-icon {
font-size:4em;
top: 0.2em;
position: relative;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
</div>
</div>
<div class="row">
<div class="col-xs-5">
<div class="col-xs-12">
<h2 class="h3">{{ suggested_topic.title }}:</h2>
</div>
{{#if suggested_topic.description }}
<div class="col-xs-7">
<div class="col-xs-12">
<p class="h4">{{_ "Description" }}:</p>
<p aria-hidden="true">{{ truncate suggested_topic.description 70 }}</p>
<span class="sr-only">{{ suggested_topic.description }}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
alt="{{ truncate description 150 }}"
{{/if}}>
{{else}}
<span aria-hidden="true" role="presentation"><i class="icon-{{kind}} glyphicon glyphicon-expand gly-h1"></i></span>
<span aria-hidden="true" role="presentation"><i class="icon-{{kind}} content-rec-icon"></i></span>
{{/if}}
</center>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<div class="row">
<div class="col-xs-3">
<center>
<span aria-hidden="true" role="presentation"><i id="exercise-icon" class="icon-{{kind}} glyphicon glyphicon-pencil gly-h1"></i></span>
<span aria-hidden="true" role="presentation"><i class="icon-{{kind}} content-rec-icon"></i></span>
</center>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ var ExerciseDataModel = ContentModels.ContentDataModel.extend({
"secondsPerFastProblem": this.get("seconds_per_fast_problem"),
"authorName": this.get("author_name"),
"relatedVideos": this.get("related_videos"),
"fileName": this.get("template")
"fileName": this.get("file_name")
},
"exerciseProgress": {
"level": "" // needed to keep khan-exercises from blowing up
Expand Down
Loading