Merge pull request #21194 from edx/feanil/remove_parsepy

DEPR-41 Remove parsepy
This commit is contained in:
Feanil Patel
2019-07-25 10:25:08 -04:00
committed by GitHub
20 changed files with 25 additions and 264 deletions

View File

@@ -7,7 +7,6 @@ from __future__ import absolute_import
from config_models.admin import ConfigurationModelAdmin
from django.contrib import admin
from contentstore.models import PushNotificationConfig, VideoUploadConfig
from contentstore.models import VideoUploadConfig
admin.site.register(VideoUploadConfig, ConfigurationModelAdmin)
admin.site.register(PushNotificationConfig, ConfigurationModelAdmin)

View File

@@ -20,7 +20,6 @@ import re
from django.http import HttpResponseBadRequest
from django.utils.translation import ugettext as _
from cms.djangoapps.contentstore.push_notification import enqueue_push_course_update
from openedx.core.lib.xblock_utils import get_course_update_items
from xmodule.html_module import CourseInfoModule
from xmodule.modulestore.django import modulestore
@@ -49,7 +48,6 @@ def update_course_updates(location, update, passed_id=None, user=None):
Either add or update the given course update.
Add:
If the passed_id is absent or None, the course update is added.
If push_notification_selected is set in the update, a celery task for the push notification is created.
Update:
It will update it if it has a passed_id which has a valid value.
Until updates have distinct values, the passed_id is the location url + an index into the html structure.
@@ -79,7 +77,6 @@ def update_course_updates(location, update, passed_id=None, user=None):
"status": CourseInfoModule.STATUS_VISIBLE
}
course_update_items.append(course_update_dict)
enqueue_push_course_update(update, location.course_key)
# update db record
save_course_update_items(location, course_updates, course_update_items, user)

View File

@@ -23,11 +23,3 @@ class VideoUploadConfig(ConfigurationModel):
def get_profile_whitelist(cls):
"""Get the list of profiles to include in the encoding download"""
return [profile for profile in cls.current().profile_whitelist.split(",") if profile]
class PushNotificationConfig(ConfigurationModel):
"""
Configuration for mobile push notifications.
.. no_pii:
"""

View File

@@ -1,87 +0,0 @@
"""
Helper methods for push notifications from Studio.
"""
from __future__ import absolute_import
from logging import exception as log_exception
from uuid import uuid4
import six
from django.conf import settings
from parse_rest.connection import register
from parse_rest.core import ParseError
from parse_rest.installation import Push
from six import text_type
from contentstore.models import PushNotificationConfig
from contentstore.tasks import push_course_update_task
from xmodule.modulestore.django import modulestore
def push_notification_enabled():
"""
Returns whether the push notification feature is enabled.
"""
return PushNotificationConfig.is_enabled()
def enqueue_push_course_update(update, course_key):
"""
Enqueues a task for push notification for the given update for the given course if
(1) the feature is enabled and
(2) push_notification is selected for the update
"""
if push_notification_enabled() and update.get("push_notification_selected"):
course = modulestore().get_course(course_key)
if course:
push_course_update_task.delay(
six.text_type(course_key),
course.clean_id(padding_char='_'),
course.display_name
)
def send_push_course_update(course_key_string, course_subscription_id, course_display_name):
"""
Sends a push notification for a course update, given the course's subscription_id and display_name.
"""
if settings.PARSE_KEYS:
try:
register(
settings.PARSE_KEYS["APPLICATION_ID"],
settings.PARSE_KEYS["REST_API_KEY"],
)
push_payload = {
"action": "course.announcement",
"notification-id": six.text_type(uuid4()),
"course-id": course_key_string,
"course-name": course_display_name,
}
push_channels = [course_subscription_id]
# Push to all Android devices
Push.alert(
data=push_payload,
channels={"$in": push_channels},
where={"deviceType": "android"},
)
# Push to all iOS devices
# With additional payload so that
# 1. The push is displayed automatically
# 2. The app gets it even in the background.
# See http://stackoverflow.com/questions/19239737/silent-push-notification-in-ios-7-does-not-work
push_payload.update({
"alert": "",
"content-available": 1
})
Push.alert(
data=push_payload,
channels={"$in": push_channels},
where={"deviceType": "ios"},
)
except ParseError as error:
log_exception(text_type(error))

View File

@@ -555,9 +555,8 @@ def push_course_update_task(course_key_string, course_subscription_id, course_di
"""
Sends a push notification for a course update.
"""
# TODO Use edx-notifications library instead (MA-638).
from .push_notification import send_push_course_update
send_push_course_update(course_key_string, course_subscription_id, course_display_name)
# TODO Delete once we've done a deploy where nothing is using this. DEPR-41
pass
class CourseExportTask(UserTask): # pylint: disable=abstract-method

View File

@@ -8,7 +8,6 @@ import time
import mock
import pytest
from contentstore.models import PushNotificationConfig
from contentstore.tests.test_course_settings import CourseTestCase
from contentstore.tests.utils import AjaxEnabledTestClient, parse_json, registration, user
from ddt import data, ddt, unpack
@@ -417,16 +416,3 @@ class CourseKeyVerificationTestCase(CourseTestCase):
)
resp = self.client.get_html(url)
self.assertEqual(resp.status_code, status_code)
class PushNotificationConfigTestCase(TestCase):
"""
Tests PushNotificationConfig.
"""
def test_notifications_defaults(self):
self.assertFalse(PushNotificationConfig.is_enabled())
def test_notifications_enabled(self):
PushNotificationConfig(enabled=True).save()
self.assertTrue(PushNotificationConfig.is_enabled())

View File

@@ -39,7 +39,6 @@ from contentstore.course_group_config import (
)
from contentstore.course_info_model import delete_course_update, get_course_updates, update_course_updates
from contentstore.courseware_index import CoursewareSearchIndexer, SearchIndexingError
from contentstore.push_notification import push_notification_enabled
from contentstore.tasks import rerun_course as rerun_course_task
from contentstore.utils import (
add_instructor,
@@ -966,7 +965,6 @@ def course_info_handler(request, course_key_string):
'updates_url': reverse_course_url('course_info_update_handler', course_key),
'handouts_locator': course_key.make_usage_key('course_info', 'handouts'),
'base_asset_url': StaticContent.get_base_url_path_for_course_assets(course_module.id),
'push_notification_enabled': push_notification_enabled()
}
)
else:

View File

@@ -9,7 +9,6 @@ from django.test.utils import override_settings
from mock import patch
from opaque_keys.edx.keys import UsageKey
from contentstore.models import PushNotificationConfig
from contentstore.tests.test_course_settings import CourseTestCase
from contentstore.utils import reverse_course_url, reverse_usage_url
from xmodule.modulestore.django import modulestore
@@ -239,7 +238,7 @@ class CourseUpdateTest(CourseTestCase):
payload = json.loads(resp.content)
self.assertEqual(len(payload), 1)
def post_course_update(self, send_push_notification=False):
def post_course_update(self):
"""
Posts an update to the course
"""
@@ -250,8 +249,6 @@ class CourseUpdateTest(CourseTestCase):
content = u"Sample update"
payload = {'content': content, 'date': 'January 8, 2013'}
if send_push_notification:
payload['push_notification_selected'] = True
resp = self.client.ajax_post(course_update_url, payload)
# check that response status is 200 not 400
@@ -260,16 +257,12 @@ class CourseUpdateTest(CourseTestCase):
payload = json.loads(resp.content)
self.assertHTMLEqual(payload['content'], content)
@patch("contentstore.push_notification.send_push_course_update")
def test_post_course_update(self, mock_push_update):
def test_post_course_update(self):
"""
Test that a user can successfully post on course updates and handouts of a course
"""
self.post_course_update()
# check that push notifications are not sent
self.assertFalse(mock_push_update.called)
updates_location = self.course.id.make_usage_key('course_info', 'updates')
self.assertTrue(isinstance(updates_location, UsageKey))
self.assertEqual(updates_location.block_id, u'updates')
@@ -287,32 +280,3 @@ class CourseUpdateTest(CourseTestCase):
payload = json.loads(resp.content)
self.assertHTMLEqual(payload['data'], content)
@patch("contentstore.push_notification.send_push_course_update")
def test_notifications_enabled_but_not_requested(self, mock_push_update):
PushNotificationConfig(enabled=True).save()
self.post_course_update()
self.assertFalse(mock_push_update.called)
@patch("contentstore.push_notification.send_push_course_update")
def test_notifications_enabled_and_sent(self, mock_push_update):
PushNotificationConfig(enabled=True).save()
self.post_course_update(send_push_notification=True)
self.assertTrue(mock_push_update.called)
@override_settings(PARSE_KEYS={"APPLICATION_ID": "TEST_APPLICATION_ID", "REST_API_KEY": "TEST_REST_API_KEY"})
@patch("contentstore.push_notification.Push")
def test_notifications_sent_to_parse(self, mock_parse_push):
PushNotificationConfig(enabled=True).save()
self.post_course_update(send_push_notification=True)
self.assertEquals(mock_parse_push.alert.call_count, 2)
@override_settings(PARSE_KEYS={"APPLICATION_ID": "TEST_APPLICATION_ID", "REST_API_KEY": "TEST_REST_API_KEY"})
@patch("contentstore.push_notification.log_exception")
@patch("contentstore.push_notification.Push")
def test_notifications_error_from_parse(self, mock_parse_push, mock_log_exception):
PushNotificationConfig(enabled=True).save()
from parse_rest.core import ParseError
mock_parse_push.alert.side_effect = ParseError
self.post_course_update(send_push_notification=True)
self.assertTrue(mock_log_exception.called)

View File

@@ -3,7 +3,7 @@ define([
'js/models/course_info', 'js/views/course_info_edit'
], function($, CourseUpdateCollection, ModuleInfoModel, CourseInfoModel, CourseInfoEditView) {
'use strict';
return function(updatesUrl, handoutsLocator, baseAssetUrl, push_notification_enabled) {
return function(updatesUrl, handoutsLocator, baseAssetUrl) {
var course_updates = new CourseUpdateCollection(),
course_handouts, editor;
@@ -18,8 +18,7 @@ define([
updates: course_updates,
base_asset_url: baseAssetUrl,
handouts: course_handouts
}),
push_notification_enabled: push_notification_enabled
})
});
editor.render();
};

View File

@@ -3,9 +3,7 @@ define(['backbone', 'jquery', 'jquery.ui'], function(Backbone, $) {
var CourseUpdate = Backbone.Model.extend({
defaults: {
date: $.datepicker.formatDate('MM d, yy', new Date()),
content: '',
push_notification_enabled: false,
push_notification_selected: false
content: ''
},
validate: function(attrs) {
var date_exists = (attrs.date !== null && attrs.date !== '');

View File

@@ -25,7 +25,7 @@ define(["js/views/course_info_handout", "js/views/course_info_update", "js/model
delete window.course_location_analytics;
});
describe("Course Updates without Push notification", function() {
describe("Course Updates", function() {
const courseInfoTemplate = readFixtures('course_info_update.underscore');
beforeEach(function() {
@@ -136,10 +136,7 @@ define(["js/views/course_info_handout", "js/views/course_info_update", "js/model
this.courseInfoEdit.$el.find('.save-button').click();
expect(model.save).toHaveBeenCalled();
// Verify push_notification_selected is set to false.
const requestSent = JSON.parse(requests[requests.length - 1].requestBody);
expect(requestSent.push_notification_selected).toEqual(false);
// Verify the link is not rewritten when saved.
expect(requestSent.content).toEqual('/static/image.jpg');
@@ -190,54 +187,6 @@ define(["js/views/course_info_handout", "js/views/course_info_update", "js/model
});
describe("Course Updates WITH Push notification", function() {
const courseInfoTemplate = readFixtures('course_info_update.underscore');
beforeEach(function() {
setFixtures($("<script>", {id: "course_info_update-tpl", type: "text/template"}).text(courseInfoTemplate));
appendSetFixtures(courseInfoPage);
this.collection = new CourseUpdateCollection();
this.collection.url = 'course_info_update/';
this.courseInfoEdit = new CourseInfoUpdateView({
el: $('.course-updates'),
collection: this.collection,
base_asset_url : 'base-asset-url/',
push_notification_enabled : true
});
this.courseInfoEdit.render();
this.event = {preventDefault() { return 'no op'; }};
this.courseInfoEdit.onNew(this.event);
});
it("shows push notification checkbox as selected by default", function() {
expect(this.courseInfoEdit.$el.find('.toggle-checkbox')).toBeChecked();
});
it("sends correct default value for push_notification_selected", function() {
const requests = AjaxHelpers.requests(this);
this.courseInfoEdit.$el.find('.save-button').click();
const requestSent = JSON.parse(requests[requests.length - 1].requestBody);
expect(requestSent.push_notification_selected).toEqual(true);
// Check that analytics send push_notification info
const analytics_payload = window.analytics.track.calls.first().args[1];
expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': true}));
});
it("sends correct value for push_notification_selected when it is unselected", function() {
const requests = AjaxHelpers.requests(this);
// unselect push notification
this.courseInfoEdit.$el.find('.toggle-checkbox').attr('checked', false);
this.courseInfoEdit.$el.find('.save-button').click();
const requestSent = JSON.parse(requests[requests.length - 1].requestBody);
expect(requestSent.push_notification_selected).toEqual(false);
// Check that analytics send push_notification info
const analytics_payload = window.analytics.track.calls.first().args[1];
expect(analytics_payload).toEqual(jasmine.objectContaining({'push_notification_selected': false}));
});
});
describe("Course Handouts", function() {
const handoutsTemplate = readFixtures('course_info_handouts.underscore');

View File

@@ -15,8 +15,7 @@ define(['js/views/baseview', 'js/views/course_info_update', 'js/views/course_inf
new CourseInfoUpdateView({
el: $('body.updates'),
collection: this.model.get('updates'),
base_asset_url: this.model.get('base_asset_url'),
push_notification_enabled: this.options.push_notification_enabled
base_asset_url: this.model.get('base_asset_url')
});
new CourseInfoHandoutView({

View File

@@ -39,10 +39,9 @@ define(['codemirror',
try {
CourseInfoHelper.changeContentToPreview(
update, 'content', self.options.base_asset_url);
// push notification is always disabled for existing updates
HtmlUtils.append(
updateList,
HtmlUtils.HTML(self.template({updateModel: update, push_notification_enabled: false}))
HtmlUtils.HTML(self.template({updateModel: update}))
);
DateUtils.setupDatePicker('date', self, index);
update.isValid();
@@ -132,8 +131,7 @@ define(['codemirror',
var $newForm = $(
this.template({
updateModel: newModel,
push_notification_enabled: this.options.push_notification_enabled
updateModel: newModel
})
);
@@ -164,8 +162,7 @@ define(['codemirror',
targetModel.set({
// translate short-form date (for input) into long form date (for display)
date: $.datepicker.formatDate('MM d, yy', new Date(this.dateEntry(event).val())),
content: this.$codeMirror.getValue(),
push_notification_selected: this.push_notification_selected(event)
content: this.$codeMirror.getValue()
});
// push change to display, hide the editor, submit the change
var saving = new NotificationView.Mini({
@@ -185,8 +182,7 @@ define(['codemirror',
analytics.track('Saved Course Update', {
course: course_location_analytics,
date: this.dateEntry(event).val(),
push_notification_selected: this.push_notification_selected(event)
date: this.dateEntry(event).val()
});
},
@@ -303,12 +299,6 @@ define(['codemirror',
}
this.$currentPost.find('form').hide();
this.$currentPost.find('.CodeMirror').remove();
// hide the push notification checkbox for subsequent edits to the Post
var push_notification_ele = this.$currentPost.find('.new-update-push-notification');
if (push_notification_ele) {
push_notification_ele.hide();
}
}
ModalUtils.hideModalCover(this.$modalCover);
@@ -337,17 +327,6 @@ define(['codemirror',
if (li) {
return $(li).find('.date').first();
}
},
push_notification_selected: function(event) {
var push_notification_checkbox;
var li = $(event.currentTarget).closest('li');
if (li) {
push_notification_checkbox = li.find('.new-update-push-notification .toggle-checkbox');
if (push_notification_checkbox) {
return push_notification_checkbox.is(':checked');
}
}
}
});

View File

@@ -27,8 +27,7 @@ from openedx.core.djangolib.js_utils import (
CourseInfoFactory(
"${updates_url | n, js_escaped_string}",
"${handouts_locator | n, js_escaped_string}",
"${base_asset_url | n, js_escaped_string}",
${push_notification_enabled | n, dump_js_escaped_json}
"${base_asset_url | n, js_escaped_string}"
);
});
</%block>

View File

@@ -2,34 +2,27 @@
<!-- FIXME what style should we use for initially hidden? --> <!-- TODO decide whether this should use codemirror -->
<form class="new-update-form">
<div class="row">
<label for="update-date-<%= updateModel.cid %>" class="inline-label"><%= gettext('Date') %>:</label>
<label for="update-date-<%- updateModel.cid %>" class="inline-label"><%- gettext('Date') %>:</label>
<!-- TODO replace w/ date widget and actual date (problem is that persisted version is "Month day" not an actual date obj -->
<input id="update-date-<%= updateModel.cid %>" type="text" class="date" value="<%= updateModel.get('date') %>">
<input id="update-date-<%- updateModel.cid %>" type="text" class="date" value="<%- updateModel.get('date') %>">
</div>
<div class="row">
<textarea class="new-update-content text-editor"><%= updateModel.get('content') %></textarea>
<textarea class="new-update-content text-editor"><%= updateModel.get('content') /* xss-lint: disable=underscore-not-escaped */ %></textarea>
</div>
<%if (push_notification_enabled) { %>
<div class="row new-update-push-notification">
<input id="update-notification-checkbox-<%= updateModel.cid %>" type="checkbox" class="toggle-checkbox" data-tooltip="<%= gettext('Send push notification to mobile apps') %>" checked />
<label for="update-notification-checkbox-<%= updateModel.cid %>" class="inline-label"><%= gettext('Send notification to mobile apps') %></label>
</div>
<% } %>
<div class="row">
<!-- cid rather than id b/c new ones have cid's not id's -->
<button class="save-button" name="<%= updateModel.cid %>"><%= gettext('Post') %></button>
<button class="cancel-button" name="<%= updateModel.cid %>"><%= gettext('Cancel') %></button>
<button class="save-button" name="<%- updateModel.cid %>"><%- gettext('Post') %></button>
<button class="cancel-button" name="<%- updateModel.cid %>"><%- gettext('Cancel') %></button>
</div>
</form>
<div class="post-preview">
<div class="post-actions">
<button class="edit-button" name="<%= updateModel.cid %>"><span class="edit-icon"></span><%= gettext('Edit') %></button>
<button class="delete-button" name="<%= updateModel.cid %>"><span class="delete-icon"></span><%= gettext('Delete') %></button>
<button class="edit-button" name="<%- updateModel.cid %>"><span class="edit-icon"></span><%- gettext('Edit') %></button>
<button class="delete-button" name="<%- updateModel.cid %>"><span class="delete-icon"></span><%- gettext('Delete') %></button>
</div>
<h2>
<span class="calendar-icon"></span><span class="date-display"><%=
updateModel.get('date') %></span>
<span class="calendar-icon"></span><span class="date-display"><%-updateModel.get('date') %></span>
</h2>
<div class="update-contents"><%= updateModel.get('content') %></div>
<div class="update-contents"><%= updateModel.get('content') /* xss-lint: disable=underscore-not-escaped */ %></div>
</div>
</li>

View File

@@ -222,6 +222,7 @@ class MigrationTests(TestCase):
"""
Tests for migrations.
"""
@unittest.skip("Migration will delete a model. Need to ship not referencing it first. DEPR-41.")
@override_settings(MIGRATION_MODULES={})
def test_migrations_are_in_sync(self):
"""

View File

@@ -11,7 +11,6 @@
-e git+https://github.com/edx/DoneXBlock.git@01a14f3bd80ae47dd08cdbbe2f88f3eb88d00fba#egg=done-xblock
-e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme
-e .
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest
-e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git+https://github.com/edx/RateXBlock.git@367e19c0f6eac8a5f002fd0f1559555f8e74bfff#egg=rate-xblock
-e common/lib/safe_lxml

View File

@@ -12,7 +12,6 @@
-e git+https://github.com/edx/DoneXBlock.git@01a14f3bd80ae47dd08cdbbe2f88f3eb88d00fba#egg=done-xblock
-e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme
-e .
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest
-e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git+https://github.com/edx/RateXBlock.git@367e19c0f6eac8a5f002fd0f1559555f8e74bfff#egg=rate-xblock
-e common/lib/safe_lxml

View File

@@ -61,7 +61,6 @@ git+https://github.com/edx/django-openid-auth.git@0.15.1#egg=django-openid-auth=
git+https://github.com/edx/MongoDBProxy.git@25b99097615bda06bd7cdfe5669ed80dc2a7fed0#egg=MongoDBProxy==0.1.0
-e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest
# Forked to get Django 1.10+ compat that is in origin BitBucket repo, without an official build.
# This can go away when we update auth to not use django-rest-framework-oauth

View File

@@ -11,7 +11,6 @@
-e git+https://github.com/edx/DoneXBlock.git@01a14f3bd80ae47dd08cdbbe2f88f3eb88d00fba#egg=done-xblock
-e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme
-e .
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest
-e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git+https://github.com/edx/RateXBlock.git@367e19c0f6eac8a5f002fd0f1559555f8e74bfff#egg=rate-xblock
-e common/lib/safe_lxml