From ad86b847b2c6d901e52878587497bf1e2a351b9e Mon Sep 17 00:00:00 2001 From: Martyn James Date: Thu, 29 Jan 2015 16:17:13 -0500 Subject: [PATCH 01/11] Clean up and reorganization --- README.md | 9 + google_drive/__init__.py | 5 +- google_drive/google_calendar.py | 55 ++- google_drive/google_docs.py | 67 ++-- google_drive/tests/__init__.py | 1 + google_drive/tests/integration/__init__.py | 1 + .../tests/integration/calendar_base_test.py | 8 +- .../tests/integration/document_base_test.py | 13 +- google_drive/tests/unit/__init__.py | 1 + google_drive/tests/unit/test_docs.py | 74 +++- google_drive/utils.py | 26 -- pylintrc | 328 ++++++++++++++++++ requirements.txt | 12 +- setup.py | 5 +- 14 files changed, 520 insertions(+), 85 deletions(-) create mode 100644 google_drive/tests/__init__.py create mode 100644 google_drive/tests/integration/__init__.py create mode 100644 google_drive/tests/unit/__init__.py delete mode 100644 google_drive/utils.py create mode 100644 pylintrc diff --git a/README.md b/README.md index 62d0fac..c99abc7 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,12 @@ License The Google Drive & Calendar XBlocks are available under the GNU Affero General Public License (AGPLv3). +## Installation Troubleshooting +On a Mac, some people have received errors when installing lxml, trying to find a specific header file for the compiler + +Try the following if you encounter a problem: +``` +CPATH=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libxml2 CFLAGS=-Qunused-arguments CPPFLAGS=-Qunused-arguments pip install lxml +``` + + diff --git a/google_drive/__init__.py b/google_drive/__init__.py index b2456b7..6fa04c4 100644 --- a/google_drive/__init__.py +++ b/google_drive/__init__.py @@ -1,2 +1,5 @@ +""" +Google drive XBlocks +""" from .google_docs import GoogleDocumentBlock -from .google_calendar import GoogleCalendarBlock \ No newline at end of file +from .google_calendar import GoogleCalendarBlock diff --git a/google_drive/google_calendar.py b/google_drive/google_calendar.py index 021426c..9d2f776 100644 --- a/google_drive/google_calendar.py +++ b/google_drive/google_calendar.py @@ -1,20 +1,25 @@ +""" +Google Calendar XBlock implementation +""" # -*- coding: utf-8 -*- # # Imports ########################################################### - -import pkg_resources -import textwrap +import logging from xblock.core import XBlock from xblock.fields import Scope, String, Integer from xblock.fragment import Fragment -from .utils import loader, AttrDict from xblockutils.publish_event import PublishEventMixin +from xblockutils.resources import ResourceLoader + +log = logging.getLogger(__name__) +RESOURCE_LOADER = ResourceLoader(__name__) # Classes ########################################################### + class GoogleCalendarBlock(XBlock, PublishEventMixin): """ XBlock providing a google calendar view for a specific calendar @@ -28,7 +33,10 @@ class GoogleCalendarBlock(XBlock, PublishEventMixin): calendar_id = String( display_name="Public Calendar ID", - help="Google provides an ID for publicly available calendars. In the Google Calendar, open Settings and copy the ID from the Calendar Address section into this field.", + help=( + "Google provides an ID for publicly available calendars. In the Google Calendar, " + "open Settings and copy the ID from the Calendar Address section into this field." + ), scope=Scope.settings, default="edx.org_lom804qe3ttspplj1bgeu1l3ak@group.calendar.google.com" ) @@ -42,7 +50,8 @@ class GoogleCalendarBlock(XBlock, PublishEventMixin): views = [(0, 'Week'), (1, 'Month'), (2, 'Agenda')] - def student_view(self, context): + # Context argument is specified for xblocks, but we are not using herein + def student_view(self, context): # pylint: disable=unused-argument """ Player view, displayed to the student """ @@ -51,39 +60,49 @@ def student_view(self, context): view = self.views[self.default_view][1] - iframe = ''.format(view, self.calendar_id, self.display_name) + iframe = ( + '' + ).format( + view, self.calendar_id, self.display_name + ) - fragment.add_content(loader.render_template('/templates/html/google_calendar.html', { + fragment.add_content(RESOURCE_LOADER.render_template('/templates/html/google_calendar.html', { "self": self, "iframe": iframe })) - fragment.add_css(loader.load_unicode('public/css/google_calendar.css')) - fragment.add_javascript(loader.load_unicode('public/js/google_calendar.js')) + fragment.add_css(RESOURCE_LOADER.load_unicode('public/css/google_calendar.css')) + fragment.add_javascript(RESOURCE_LOADER.load_unicode('public/js/google_calendar.js')) fragment.initialize_js('GoogleCalendarBlock') return fragment - def studio_view(self, context): + # Context argument is specified for xblocks, but we are not using herein + def studio_view(self, context): # pylint: disable=unused-argument """ Editing view in Studio """ fragment = Fragment() - fragment.add_content(loader.render_template('/templates/html/google_calendar_edit.html', { + # Need to access protected members of fields to get their default value + fragment.add_content(RESOURCE_LOADER.render_template('/templates/html/google_calendar_edit.html', { 'self': self, - 'defaultName': self.fields['display_name']._default, - 'defaultID': self.fields['calendar_id']._default + 'defaultName': self.fields['display_name']._default, # pylint: disable=protected-access + 'defaultID': self.fields['calendar_id']._default # pylint: disable=protected-access })) - fragment.add_javascript(loader.load_unicode('public/js/google_calendar_edit.js')) - fragment.add_css(loader.load_unicode('public/css/google_edit.css')) + fragment.add_javascript(RESOURCE_LOADER.load_unicode('public/js/google_calendar_edit.js')) + fragment.add_css(RESOURCE_LOADER.load_unicode('public/css/google_edit.css')) fragment.initialize_js('GoogleCalendarEditBlock') return fragment + # suffix argument is specified for xblocks, but we are not using herein @XBlock.json_handler - def studio_submit(self, submissions, suffix=''): - + def studio_submit(self, submissions, suffix=''): # pylint: disable=unused-argument + """ + Change the settings for this XBlock given by the Studio user + """ self.display_name = submissions['display_name'] self.calendar_id = submissions['calendar_id'] self.default_view = submissions['default_view'] diff --git a/google_drive/google_docs.py b/google_drive/google_docs.py index 925e5d9..567757e 100644 --- a/google_drive/google_docs.py +++ b/google_drive/google_docs.py @@ -1,9 +1,11 @@ +""" +Google Document XBlock implementation +""" # -*- coding: utf-8 -*- # # Imports ########################################################### - -import pkg_resources +import logging import textwrap import requests @@ -11,11 +13,15 @@ from xblock.fields import Scope, String from xblock.fragment import Fragment -from .utils import loader, AttrDict from xblockutils.publish_event import PublishEventMixin +from xblockutils.resources import ResourceLoader + +log = logging.getLogger(__name__) +RESOURCE_LOADER = ResourceLoader(__name__) # Classes ########################################################### + class GoogleDocumentBlock(XBlock, PublishEventMixin): """ XBlock providing a google document embed link @@ -29,7 +35,11 @@ class GoogleDocumentBlock(XBlock, PublishEventMixin): embed_code = String( display_name="Embed Code", - help="Google provides an embed code for Drive documents. In the Google Drive document, from the File menu, select Publish to the Web. Modify settings as needed, click Publish, and copy the embed code into this field.", + help=( + "Google provides an embed code for Drive documents. In the Google Drive document, " + "from the File menu, select Publish to the Web. Modify settings as needed, click " + "Publish, and copy the embed code into this field." + ), scope=Scope.settings, default=textwrap.dedent(""" ' +) +# Classes ########################################################### class GoogleCalendarBlock(XBlock, PublishEventMixin): # pylint: disable=too-many-ancestors """ XBlock providing a google calendar view for a specific calendar @@ -38,7 +47,7 @@ class GoogleCalendarBlock(XBlock, PublishEventMixin): # pylint: disable=too-man "open Settings and copy the ID from the Calendar Address section into this field." ), scope=Scope.settings, - default="edx.org_lom804qe3ttspplj1bgeu1l3ak@group.calendar.google.com" + default=DEFAULT_CALENDAR_ID ) default_view = Integer( @@ -55,19 +64,12 @@ def student_view(self, context): # pylint: disable=unused-argument """ Player view, displayed to the student """ - fragment = Fragment() view = self.views[self.default_view][1] + iframe = CALENDAR_IFRAME.format(view, self.calendar_id, self.display_name) - iframe = ( - '' - ).format( - view, self.calendar_id, self.display_name - ) - - fragment.add_content(RESOURCE_LOADER.render_template('/templates/html/google_calendar.html', { + fragment.add_content(RESOURCE_LOADER.render_template(CALENDAR_TEMPLATE, { "self": self, "iframe": iframe })) @@ -85,7 +87,7 @@ def studio_view(self, context): # pylint: disable=unused-argument """ fragment = Fragment() # Need to access protected members of fields to get their default value - fragment.add_content(RESOURCE_LOADER.render_template('/templates/html/google_calendar_edit.html', { + fragment.add_content(RESOURCE_LOADER.render_template(CALENDAR_EDIT_TEMPLATE, { 'self': self, 'defaultName': self.fields['display_name']._default, # pylint: disable=protected-access 'defaultID': self.fields['calendar_id']._default # pylint: disable=protected-access diff --git a/google_drive/google_docs.py b/google_drive/google_docs.py index 6b345d5..f5ec624 100644 --- a/google_drive/google_docs.py +++ b/google_drive/google_docs.py @@ -19,9 +19,23 @@ LOG = logging.getLogger(__name__) RESOURCE_LOADER = ResourceLoader(__name__) -# Classes ########################################################### +# Constants ########################################################### +DEFAULT_EMBED_CODE = textwrap.dedent(""" + + """) +DOCUMENT_TEMPLATE = "/templates/html/google_docs.html" +DOCUMENT_EDIT_TEMPLATE = "/templates/html/google_docs_edit.html" +# Classes ########################################################### class GoogleDocumentBlock(XBlock, PublishEventMixin): # pylint: disable=too-many-ancestors """ XBlock providing a google document embed link @@ -41,17 +55,8 @@ class GoogleDocumentBlock(XBlock, PublishEventMixin): # pylint: disable=too-man "Publish, and copy the embed code into this field." ), scope=Scope.settings, - default=textwrap.dedent(""" - - """)) + default=DEFAULT_EMBED_CODE + ) alt_text = String( display_name="Alternative Text", @@ -68,10 +73,9 @@ def student_view(self, context): # pylint: disable=unused-argument """ Player view, displayed to the student """ - fragment = Fragment() - fragment.add_content(RESOURCE_LOADER.render_template('/templates/html/google_docs.html', {"self": self})) + fragment.add_content(RESOURCE_LOADER.render_template(DOCUMENT_TEMPLATE, {"self": self})) fragment.add_css(RESOURCE_LOADER.load_unicode('public/css/google_docs.css')) fragment.add_javascript(RESOURCE_LOADER.load_unicode('public/js/google_docs.js')) @@ -86,7 +90,7 @@ def studio_view(self, context): # pylint: disable=unused-argument """ fragment = Fragment() # Need to access protected members of fields to get their default value - fragment.add_content(RESOURCE_LOADER.render_template('/templates/html/google_docs_edit.html', { + fragment.add_content(RESOURCE_LOADER.render_template(DOCUMENT_EDIT_TEMPLATE, { 'self': self, 'defaultName': self.fields['display_name']._default # pylint: disable=protected-access })) diff --git a/google_drive/public/js/google_calendar.js b/google_drive/public/js/google_calendar.js index 24bbd70..373c9e3 100644 --- a/google_drive/public/js/google_calendar.js +++ b/google_drive/public/js/google_calendar.js @@ -1,6 +1,5 @@ /* Javascript for GoogleDocumentBlock. */ function GoogleCalendarBlock(runtime, element) { - $('iframe', element).load(function(){ var iframe_url = $(this).attr('src'); $.ajax({ diff --git a/google_drive/public/js/google_docs.js b/google_drive/public/js/google_docs.js index 2d0a144..227839c 100644 --- a/google_drive/public/js/google_docs.js +++ b/google_drive/public/js/google_docs.js @@ -1,6 +1,5 @@ /* Javascript for GoogleDocumentBlock. */ function GoogleDocumentBlock(runtime, element) { - var iframe = $('iframe', element); var image = $('img', element); var xblock_wrapper = $('.google-docs-xblock-wrapper', element); diff --git a/google_drive/public/js/google_docs_edit.js b/google_drive/public/js/google_docs_edit.js index e986fac..f495c00 100644 --- a/google_drive/public/js/google_docs_edit.js +++ b/google_drive/public/js/google_docs_edit.js @@ -1,5 +1,4 @@ function GoogleDocumentEditBlock(runtime, element) { - var clear_name_button = $('.clear-display-name', element); var save_button = $('.save-button', element); var validation_alert = $('.validation_alert', element); diff --git a/google_drive/tests/__init__.py b/google_drive/tests/__init__.py index d2927ef..418a32e 100644 --- a/google_drive/tests/__init__.py +++ b/google_drive/tests/__init__.py @@ -1 +1 @@ -""" Put tests here """ +""" Unit and integration tests for google drive components """ diff --git a/google_drive/tests/integration/__init__.py b/google_drive/tests/integration/__init__.py index 0306fef..5a51f66 100644 --- a/google_drive/tests/integration/__init__.py +++ b/google_drive/tests/integration/__init__.py @@ -1 +1 @@ -""" Unit tests for google drive components """ +""" Integration tests for google drive components """ diff --git a/google_drive/tests/unit/test_calendar.py b/google_drive/tests/unit/test_calendar.py new file mode 100644 index 0000000..6326ff4 --- /dev/null +++ b/google_drive/tests/unit/test_calendar.py @@ -0,0 +1,93 @@ +""" Unit tests for google document components """ +import json +import unittest +from mock import Mock + +from nose.tools import assert_equals, assert_in +from workbench.runtime import WorkbenchRuntime +from xblock.runtime import KvsFieldData, DictKeyValueStore + +from google_drive import GoogleCalendarBlock +from google_drive.tests.unit.test_utils import generate_scope_ids, make_request + + +class TestGoogleCalendarBlock(unittest.TestCase): + """ Tests for GoogleCalendarBlock """ + @classmethod + def make_calendar_block(cls): + """ helper to construct a GoogleCalendarBlock """ + runtime = WorkbenchRuntime() + key_store = DictKeyValueStore() + db_model = KvsFieldData(key_store) + ids = generate_scope_ids(runtime, 'google_calendar') + return GoogleCalendarBlock(runtime, db_model, scope_ids=ids) + + def test_calendar_template_content(self): # pylint: disable=no-self-use + """ Test content of GoogleCalendarBlock's rendered views """ + block = TestGoogleCalendarBlock.make_calendar_block() + block.usage_id = Mock() + + student_fragment = block.render('student_view', Mock()) + # pylint: disable=no-value-for-parameter + assert_in('
', student_fragment.content) + assert_in( + ( + 'https://www.google.com/calendar/embed?mode=Month&src=edx.org_lom804qe3ttspplj1bgeu1l3ak' + '@group.calendar.google.com&showCalendars=0' + ), + student_fragment.content + ) + assert_in('Google Calendar', student_fragment.content) + + studio_fragment = block.render('studio_view', Mock()) + assert_in( + '
', + studio_fragment.content + ) + assert_in('
', studio_fragment.content) + assert_in('
', studio_fragment.content) + assert_in('
', studio_fragment.content) + + def test_calendar_document_submit(self): # pylint: disable=no-self-use + """ Test studio submission of GoogleCalendarBlock """ + block = TestGoogleCalendarBlock.make_calendar_block() + + body = json.dumps({ + 'display_name': "Google Calendar", + 'calendar_id': "google1234", + 'default_view': 1 + }) + res = block.handle('studio_submit', make_request(body)) + # pylint: disable=no-value-for-parameter + assert_equals(json.loads(res.body), {'result': 'success'}) + + assert_equals(block.display_name, "Google Calendar") + assert_equals(block.calendar_id, "google1234") + assert_equals(block.default_view, 1) + + def test_calendar_publish_event(self): # pylint: disable=no-self-use + """ Test event publishing in GoogleCalendarBlock""" + block = TestGoogleCalendarBlock.make_calendar_block() + + body = json.dumps({ + 'url': ( + 'https://www.google.com/calendar/embed?mode=Month&src=edx.org_lom804qe3ttspplj1bgeu1l3ak' + '@group.calendar.google.com&showCalendars=0' + ), + 'displayed_in': 'iframe', + 'event_type': 'edx.googlecomponent.calendar.displayed' + }) + res = block.handle('publish_event', make_request(body)) + # pylint: disable=no-value-for-parameter + assert_equals(json.loads(res.body), {'result': 'success'}) + + body = json.dumps({ + 'url': ( + 'https://www.google.com/calendar/embed?mode=Month&src=edx.org_lom804qe3ttspplj1bgeu1l3ak' + '@group.calendar.google.com&showCalendars=0' + ), + 'displayed_in': 'iframe', + }) + res = block.handle('publish_event', make_request(body)) + + assert_equals(json.loads(res.body), {'result': 'error', 'message': 'Missing event_type in JSON data'}) diff --git a/google_drive/tests/unit/test_docs.py b/google_drive/tests/unit/test_docs.py index a9e19b9..a499403 100644 --- a/google_drive/tests/unit/test_docs.py +++ b/google_drive/tests/unit/test_docs.py @@ -1,33 +1,14 @@ -""" Tests for google drive components """ +""" Unit tests for google document components """ import json import unittest - -from webob import Request from mock import Mock +from nose.tools import assert_equals, assert_in from workbench.runtime import WorkbenchRuntime from xblock.runtime import KvsFieldData, DictKeyValueStore -from xblock.fields import ScopeIds - -from google_drive import GoogleDocumentBlock, GoogleCalendarBlock - -from nose.tools import assert_equals, assert_in - - -def generate_scope_ids(runtime, block_type): - """ helper to generate scope IDs for an XBlock """ - def_id = runtime.id_generator.create_definition(block_type) - usage_id = runtime.id_generator.create_usage(def_id) - return ScopeIds('user', block_type, def_id, usage_id) - -def make_request(body, method='POST'): - """ helper to make a request """ - request = Request.blank('/') - request.method = 'POST' - request.body = body.encode('utf-8') - request.method = method - return request +from google_drive import GoogleDocumentBlock +from google_drive.tests.unit.test_utils import generate_scope_ids, make_request class TestGoogleDocumentBlock(unittest.TestCase): @@ -155,86 +136,3 @@ def test_document_publish_event(self): # pylint: disable=no-self-use res = block.handle('publish_event', make_request(body)) assert_equals(json.loads(res.body), {'result': 'error', 'message': 'Missing event_type in JSON data'}) - - -class TestGoogleCalendarBlock(unittest.TestCase): - """ Tests for GoogleCalendarBlock """ - - @classmethod - def make_calendar_block(cls): - """ helper to construct a GoogleCalendarBlock """ - runtime = WorkbenchRuntime() - key_store = DictKeyValueStore() - db_model = KvsFieldData(key_store) - ids = generate_scope_ids(runtime, 'google_calendar') - return GoogleCalendarBlock(runtime, db_model, scope_ids=ids) - - def test_calendar_template_content(self): # pylint: disable=no-self-use - """ Test content of GoogleCalendarBlock's rendered views """ - block = TestGoogleCalendarBlock.make_calendar_block() - block.usage_id = Mock() - - student_fragment = block.render('student_view', Mock()) - # pylint: disable=no-value-for-parameter - assert_in('
', student_fragment.content) - assert_in( - ( - 'https://www.google.com/calendar/embed?mode=Month&src=edx.org_lom804qe3ttspplj1bgeu1l3ak' - '@group.calendar.google.com&showCalendars=0' - ), - student_fragment.content - ) - assert_in('Google Calendar', student_fragment.content) - - studio_fragment = block.render('studio_view', Mock()) - assert_in( - '
', - studio_fragment.content - ) - assert_in('
', studio_fragment.content) - assert_in('
', studio_fragment.content) - assert_in('
', studio_fragment.content) - - def test_calendar_document_submit(self): # pylint: disable=no-self-use - """ Test studio submission of GoogleCalendarBlock """ - block = TestGoogleCalendarBlock.make_calendar_block() - - body = json.dumps({ - 'display_name': "Google Calendar", - 'calendar_id': "google1234", - 'default_view': 1 - }) - res = block.handle('studio_submit', make_request(body)) - # pylint: disable=no-value-for-parameter - assert_equals(json.loads(res.body), {'result': 'success'}) - - assert_equals(block.display_name, "Google Calendar") - assert_equals(block.calendar_id, "google1234") - assert_equals(block.default_view, 1) - - def test_calendar_publish_event(self): # pylint: disable=no-self-use - """ Test event publishing in GoogleCalendarBlock""" - block = TestGoogleCalendarBlock.make_calendar_block() - - body = json.dumps({ - 'url': ( - 'https://www.google.com/calendar/embed?mode=Month&src=edx.org_lom804qe3ttspplj1bgeu1l3ak' - '@group.calendar.google.com&showCalendars=0' - ), - 'displayed_in': 'iframe', - 'event_type': 'edx.googlecomponent.calendar.displayed' - }) - res = block.handle('publish_event', make_request(body)) - # pylint: disable=no-value-for-parameter - assert_equals(json.loads(res.body), {'result': 'success'}) - - body = json.dumps({ - 'url': ( - 'https://www.google.com/calendar/embed?mode=Month&src=edx.org_lom804qe3ttspplj1bgeu1l3ak' - '@group.calendar.google.com&showCalendars=0' - ), - 'displayed_in': 'iframe', - }) - res = block.handle('publish_event', make_request(body)) - - assert_equals(json.loads(res.body), {'result': 'error', 'message': 'Missing event_type in JSON data'}) diff --git a/google_drive/tests/unit/test_utils.py b/google_drive/tests/unit/test_utils.py new file mode 100644 index 0000000..ae4694d --- /dev/null +++ b/google_drive/tests/unit/test_utils.py @@ -0,0 +1,20 @@ +""" Utility functions used within unit tests """ +from webob import Request + +from xblock.fields import ScopeIds + + +def generate_scope_ids(runtime, block_type): + """ helper to generate scope IDs for an XBlock """ + def_id = runtime.id_generator.create_definition(block_type) + usage_id = runtime.id_generator.create_usage(def_id) + return ScopeIds('user', block_type, def_id, usage_id) + + +def make_request(body, method='POST'): + """ helper to make a request """ + request = Request.blank('/') + request.method = 'POST' + request.body = body.encode('utf-8') + request.method = method + return request From 64c7b7a696b7864d57e9e95543db132bf9ea70b1 Mon Sep 17 00:00:00 2001 From: marjev Date: Wed, 18 Feb 2015 18:20:44 +0100 Subject: [PATCH 10/11] Added integration tests for studio views; Code clean-up; --- .pylintrc | 25 -- .travis.yml | 3 +- google_drive/google_docs.py | 9 +- .../templates/html/google_calendar_edit.html | 8 +- .../templates/html/google_docs_edit.html | 6 +- google_drive/tests/integration/base_test.py | 14 + .../tests/integration/calendar_base_test.py | 17 - .../tests/integration/document_base_test.py | 26 -- .../tests/integration/studio_scenarios.py | 20 ++ .../tests/integration/test_publish.py | 41 +++ google_drive/tests/integration/test_studio.py | 135 +++++++ .../tests/integration/xml/calendar.xml | 4 +- .../tests/integration/xml/document.xml | 4 +- google_drive/tests/integration/xml/image.xml | 4 +- google_drive/tests/unit/test_calendar.py | 6 +- google_drive/tests/unit/test_docs.py | 10 +- pylintrc | 328 ------------------ requirements.txt | 1 + 18 files changed, 244 insertions(+), 417 deletions(-) create mode 100644 google_drive/tests/integration/base_test.py delete mode 100644 google_drive/tests/integration/calendar_base_test.py delete mode 100644 google_drive/tests/integration/document_base_test.py create mode 100644 google_drive/tests/integration/studio_scenarios.py create mode 100644 google_drive/tests/integration/test_publish.py create mode 100644 google_drive/tests/integration/test_studio.py delete mode 100644 pylintrc diff --git a/.pylintrc b/.pylintrc index fecba8c..1745113 100644 --- a/.pylintrc +++ b/.pylintrc @@ -123,34 +123,9 @@ required-attributes= # List of builtins function names that should not be used, separated by a comma bad-functions=map,filter,apply,input -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - # Regular expression which should only match correct function names function-rgx=[a-z_][a-z0-9_]{2,50}$ -# Regular expression which should only match correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ diff --git a/.travis.yml b/.travis.yml index bbe4321..57058d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,8 @@ before_install: - "sh -e /etc/init.d/xvfb start" install: - - pip install -r requirements.txt + - "pip install -r requirements.txt" + - "pip uninstall -y xblock-google-drive && python setup.py sdist && pip install dist/xblock-google-drive-0.1.tar.gz" script: - DJANGO_SETTINGS_MODULE="settings" nosetests --with-coverage --cover-package="google_drive" --with-django diff --git a/google_drive/google_docs.py b/google_drive/google_docs.py index f5ec624..516122a 100644 --- a/google_drive/google_docs.py +++ b/google_drive/google_docs.py @@ -121,10 +121,15 @@ def check_url(self, data, suffix=''): # pylint: disable=unused-argument,no-self """ Checks that the given document url is accessible, and therefore assumed to be valid """ - test_url = data['url'] try: + test_url = data['url'] url_response = requests.head(test_url) - # Catch wide range of errors + except KeyError as ex: + LOG.debug("URL not provided - %s", unicode(ex)) + return { + 'status_code': 400, + } + # Catch wide range of request exceptions except requests.exceptions.RequestException as ex: LOG.debug("Unable to connect to %s - %s", test_url, unicode(ex)) return { diff --git a/google_drive/templates/html/google_calendar_edit.html b/google_drive/templates/html/google_calendar_edit.html index 0a5f62b..669bb0b 100644 --- a/google_drive/templates/html/google_calendar_edit.html +++ b/google_drive/templates/html/google_calendar_edit.html @@ -1,7 +1,7 @@ {% load i18n %} -
+
@@ -17,7 +17,7 @@

{% trans "Invalid Google Calendar" %}

@@ -28,7 +28,7 @@

{% trans "Invalid Google Calendar" %}

@@ -57,7 +57,7 @@

{% trans "Invalid Google Calendar" %}

  • - {% trans "Save" %} + {% trans "Save" %}
  • diff --git a/google_drive/templates/html/google_docs_edit.html b/google_drive/templates/html/google_docs_edit.html index 896bd84..d728463 100644 --- a/google_drive/templates/html/google_docs_edit.html +++ b/google_drive/templates/html/google_docs_edit.html @@ -1,7 +1,7 @@ {% load i18n %} -
    +
    @@ -17,7 +17,7 @@

    {% trans "Invalid Google Document" %}

    @@ -44,7 +44,7 @@

    {% trans "Invalid Google Document" %}

    • - {% trans "Save" %} + {% trans "Save" %}
    • diff --git a/google_drive/tests/integration/base_test.py b/google_drive/tests/integration/base_test.py new file mode 100644 index 0000000..3f7ac45 --- /dev/null +++ b/google_drive/tests/integration/base_test.py @@ -0,0 +1,14 @@ +""" Base classes for integration tests """ +from xblockutils.base_test import SeleniumBaseTest + + +class GoogleCalendarBaseTest(SeleniumBaseTest): # pylint: disable=too-many-ancestors, too-few-public-methods + """ Base class for Google Calendar integration tests """ + module_name = __name__ + default_css_selector = 'div.google-calendar-xblock-wrapper' + + +class GoogleDocumentBaseTest(SeleniumBaseTest): # pylint: disable=too-many-ancestors, too-few-public-methods + """ Base class for Google Document integration tests """ + module_name = __name__ + default_css_selector = 'div.google-docs-xblock-wrapper' diff --git a/google_drive/tests/integration/calendar_base_test.py b/google_drive/tests/integration/calendar_base_test.py deleted file mode 100644 index befa0c1..0000000 --- a/google_drive/tests/integration/calendar_base_test.py +++ /dev/null @@ -1,17 +0,0 @@ -""" Google Calendar integration tests """ -from xblockutils.base_test import SeleniumBaseTest - - -class GoogleCalendarBaseTest(SeleniumBaseTest): # pylint: disable=too-many-ancestors,too-few-public-methods - """ Test class for google calendar """ - module_name = __name__ - default_css_selector = 'div.google-calendar-xblock-wrapper' - - def test_calendar_publish_event(self): - """ Tests whether the publish event for calendar was triggered """ - calendar = self.go_to_page('Calendar') - load_event_complete = calendar.find_element_by_css_selector('.load_event_complete') - self.assertEqual( - load_event_complete.get_attribute('value'), - "I've published the event that indicates that the load has completed" - ) diff --git a/google_drive/tests/integration/document_base_test.py b/google_drive/tests/integration/document_base_test.py deleted file mode 100644 index 1036d8f..0000000 --- a/google_drive/tests/integration/document_base_test.py +++ /dev/null @@ -1,26 +0,0 @@ -""" Google Document integration tests """ -from xblockutils.base_test import SeleniumBaseTest - - -class GoogleDocumentBaseTest(SeleniumBaseTest): # pylint: disable=too-many-ancestors - """ Test class for google document """ - module_name = __name__ - default_css_selector = 'div.google-docs-xblock-wrapper' - - def test_document_publish_event(self): - """ Tests whether the publish event for document was triggered """ - document = self.go_to_page('Document') - load_event_complete = document.find_element_by_css_selector('.load_event_complete') - self.assertEqual( - load_event_complete.get_attribute('value'), - "I've published the event that indicates that the load has completed" - ) - - def test_image_publish_event(self): - """ Tests whether the publish event for image was triggered """ - image = self.go_to_page('Image') - load_event_complete = image.find_element_by_css_selector('.load_event_complete') - self.assertEqual( - load_event_complete.get_attribute('value'), - "I've published the event that indicates that the load has completed" - ) diff --git a/google_drive/tests/integration/studio_scenarios.py b/google_drive/tests/integration/studio_scenarios.py new file mode 100644 index 0000000..5baa121 --- /dev/null +++ b/google_drive/tests/integration/studio_scenarios.py @@ -0,0 +1,20 @@ +""" +Contains a list of lists that will be used as the DDT arguments for the studio test. +""" +CALENDAR_SCENARIOS = [ + [ + 'Calendar', + ], +] + +DOCUMENT_SCENARIOS = [ + [ + 'Document', + ], +] + +IMAGE_SCENARIOS = [ + [ + 'Image', + ], +] diff --git a/google_drive/tests/integration/test_publish.py b/google_drive/tests/integration/test_publish.py new file mode 100644 index 0000000..e2128b8 --- /dev/null +++ b/google_drive/tests/integration/test_publish.py @@ -0,0 +1,41 @@ +""" Runs tests for publish event functionality """ +from .base_test import GoogleCalendarBaseTest, GoogleDocumentBaseTest + + +class GoogleCalendarPublishTestCase(GoogleCalendarBaseTest): # pylint: disable=too-few-public-methods, too-many-ancestors + """ + Tests for Google Calendar event publishing functionality. + """ + + def test_calendar_publish_event(self): + """ Tests whether the publish event for calendar was triggered """ + calendar = self.go_to_page('Calendar') + load_event_complete = calendar.find_element_by_css_selector('.load_event_complete') + self.assertEqual( + load_event_complete.get_attribute('value'), + "I've published the event that indicates that the load has completed" + ) + + +class GoogleDocumentPublishTestCase(GoogleDocumentBaseTest): # pylint: disable=too-many-ancestors + """ + Tests for Google Document event publishing functionality. + """ + + def test_document_publish_event(self): + """ Tests whether the publish event for document was triggered """ + document = self.go_to_page('Document') + load_event_complete = document.find_element_by_css_selector('.load_event_complete') + self.assertEqual( + load_event_complete.get_attribute('value'), + "I've published the event that indicates that the load has completed" + ) + + def test_image_publish_event(self): + """ Tests whether the publish event for image was triggered """ + image = self.go_to_page('Image') + load_event_complete = image.find_element_by_css_selector('.load_event_complete') + self.assertEqual( + load_event_complete.get_attribute('value'), + "I've published the event that indicates that the load has completed" + ) diff --git a/google_drive/tests/integration/test_studio.py b/google_drive/tests/integration/test_studio.py new file mode 100644 index 0000000..dc904c8 --- /dev/null +++ b/google_drive/tests/integration/test_studio.py @@ -0,0 +1,135 @@ +""" Runs tests for the studio views """ + +from ddt import ddt, unpack, data +from .base_test import GoogleCalendarBaseTest, GoogleDocumentBaseTest +from .studio_scenarios import CALENDAR_SCENARIOS, DOCUMENT_SCENARIOS, IMAGE_SCENARIOS + +DEFAULT_CALENDAR_SRC = ( + 'https://www.google.com/calendar/embed?' + 'mode=Month&' + 'src=edx.org_lom804qe3ttspplj1bgeu1l3ak@group.calendar.google.com&' + 'showCalendars=0' +) + +DEFAULT_DOCUMENT_SRC = ( + 'https://docs.google.com/presentation/d/1x2ZuzqHsMoh1epK8VsGAlanSo7r9z55ualwQlj-ofBQ/embed?' + 'start=true&loop=true&delayms=10000' +) + +TEST_IMAGE_SRC = 'https://docs.google.com/drawings/d/1lmmxboBM5c_0WCTjhAxBdkpqQb3T8VSwtuG0TRR1ODQ/pub?w=960&h=720' + + +@ddt # pylint: disable=too-many-ancestors +class GoogleCalendarStudioTest(GoogleCalendarBaseTest): + """ + Tests for Google Calendar studio view. + """ + default_css_selector = '#calendar-settings-tab' + + def studio_save(self): + """ Save changes made in studio for Google Calendar """ + self.browser.find_element_by_css_selector('#calendar-submit-options').click() + + @data(*CALENDAR_SCENARIOS) # pylint: disable=star-args + @unpack + def test_save_calendar(self, page_name): + """ + Verify that option changes in Google Calendar studio view + are appropriately saved and visible immediately after + """ + self.go_to_page(page_name, view_name='studio_view') + # Expecting every input value to be valid + self.assertTrue(self.browser.find_element_by_css_selector('.validation_alert.covered')) + display_name_input = self.browser.find_element_by_css_selector('#edit_display_name') + # Change display name + display_name_input.clear() + display_name_input.send_keys('My Meetings') + calendar_id_input = self.browser.find_element_by_css_selector('#edit_calendar_id') + # Change calendar ID + calendar_id_input.clear() + calendar_id_input.send_keys('a') + self.wait_until_exists('#edit_calendar_id.error') + # Expects validation error due to calendar ID being invalid + self.assertTrue(self.browser.find_element_by_css_selector('.validation_alert:not(covered)')) + # Check to see that calendar ID input element is marked as invalid + self.assertTrue(self.browser.find_element_by_css_selector('#edit_calendar_id.error')) + # Save button should be disabled + self.assertTrue(self.browser.find_element_by_css_selector('#calendar-submit-options.disabled')) + clean_calendar_id_button = self.browser.find_element_by_css_selector('button.clear-calendar-id') + # Reset calendar ID value to default one + clean_calendar_id_button.click() + # Expecting every input value to be valid again + self.assertTrue(self.browser.find_element_by_css_selector('.validation_alert.covered')) + + self.studio_save() + self.go_to_page(page_name, css_selector='div.google-calendar-xblock-wrapper') + calendar_iframe = self.browser.find_element_by_css_selector('iframe') + # Expecting that default calendar is the one loaded in the IFrame + self.assertEqual(calendar_iframe.get_attribute("src"), DEFAULT_CALENDAR_SRC) + # Expecting that the new display name is the title of the IFrame + self.assertEqual(calendar_iframe.get_attribute("title"), 'My Meetings') + + +@ddt # pylint: disable=too-many-ancestors +class GoogleDocumentStudioTest(GoogleDocumentBaseTest): + """ + Tests for Google Document studio view. + """ + default_css_selector = '#document-settings-tab' + + def studio_save(self): + """ Save changes made in studio for Google Document """ + self.browser.find_element_by_css_selector('#document-submit-options').click() + + @data(*DOCUMENT_SCENARIOS) # pylint: disable=star-args + @unpack + def test_save_document(self, page_name): + """ + Verify that option changes in Google Document studio view + are appropriately saved and visible immediately after + """ + self.go_to_page(page_name, view_name='studio_view') + # Expecting every input value to be valid + self.assertTrue(self.browser.find_element_by_css_selector('.validation_alert.covered')) + display_name_input = self.browser.find_element_by_css_selector('#edit_display_name') + # Change display name + display_name_input.clear() + display_name_input.send_keys('My Document') + # Expecting list item that contains input element for alternative text to be hidden + self.assertTrue(self.browser.find_element_by_css_selector('li#alt_text_item.covered')) + + self.studio_save() + self.go_to_page(page_name, css_selector='div.google-docs-xblock-wrapper') + document_iframe = self.browser.find_element_by_css_selector('iframe') + # Expecting that default calendar is the one loaded in the IFrame + self.assertEqual(document_iframe.get_attribute("src"), DEFAULT_DOCUMENT_SRC) + # Expecting that the new display name is the title of the IFrame + self.assertEqual(document_iframe.get_attribute("title"), 'My Document') + + @data(*IMAGE_SCENARIOS) # pylint: disable=star-args + @unpack + def test_save_image(self, page_name): + """ + Verify that option changes in Google Image studio view + are appropriately saved and visible immediately after + """ + self.go_to_page(page_name, view_name='studio_view') + # Expecting every input value to be valid + self.assertTrue(self.browser.find_element_by_css_selector('.validation_alert.covered')) + display_name_input = self.browser.find_element_by_css_selector('#edit_display_name') + # Change display name + display_name_input.clear() + display_name_input.send_keys('My Image') + # Expecting list item that contains input element for alternative text to be shown + self.assertTrue(self.browser.find_element_by_css_selector('li#alt_text_item:not(covered)')) + alt_text_input = self.browser.find_element_by_css_selector('#edit_alt_text') + # Add alternative text for image + alt_text_input.send_keys('Alternative text for my image') + + self.studio_save() + self.go_to_page(page_name, css_selector='div.google-docs-xblock-wrapper') + image_iframe = self.browser.find_element_by_css_selector('img') + # Expecting that default calendar is the one loaded in the IFrame + self.assertEqual(image_iframe.get_attribute("src"), TEST_IMAGE_SRC) + # Expecting that the new display name is the title of the IFrame + self.assertEqual(image_iframe.get_attribute("alt"), 'Alternative text for my image') diff --git a/google_drive/tests/integration/xml/calendar.xml b/google_drive/tests/integration/xml/calendar.xml index 80bf5b0..7b0761d 100644 --- a/google_drive/tests/integration/xml/calendar.xml +++ b/google_drive/tests/integration/xml/calendar.xml @@ -1,3 +1 @@ - - - + diff --git a/google_drive/tests/integration/xml/document.xml b/google_drive/tests/integration/xml/document.xml index 3072bf4..10c199c 100644 --- a/google_drive/tests/integration/xml/document.xml +++ b/google_drive/tests/integration/xml/document.xml @@ -1,3 +1 @@ - - - + diff --git a/google_drive/tests/integration/xml/image.xml b/google_drive/tests/integration/xml/image.xml index 5152350..54b5652 100644 --- a/google_drive/tests/integration/xml/image.xml +++ b/google_drive/tests/integration/xml/image.xml @@ -1,3 +1 @@ - - - + diff --git a/google_drive/tests/unit/test_calendar.py b/google_drive/tests/unit/test_calendar.py index 6326ff4..286eeec 100644 --- a/google_drive/tests/unit/test_calendar.py +++ b/google_drive/tests/unit/test_calendar.py @@ -13,6 +13,7 @@ class TestGoogleCalendarBlock(unittest.TestCase): """ Tests for GoogleCalendarBlock """ + @classmethod def make_calendar_block(cls): """ helper to construct a GoogleCalendarBlock """ @@ -41,7 +42,10 @@ def test_calendar_template_content(self): # pylint: disable=no-self-use studio_fragment = block.render('studio_view', Mock()) assert_in( - '
      ', + ( + '
      ' + ), studio_fragment.content ) assert_in('
      ', studio_fragment.content) diff --git a/google_drive/tests/unit/test_docs.py b/google_drive/tests/unit/test_docs.py index a499403..9fd4a88 100644 --- a/google_drive/tests/unit/test_docs.py +++ b/google_drive/tests/unit/test_docs.py @@ -44,7 +44,10 @@ def test_document_template_content(self): # pylint: disable=no-self-use studio_fragment = block.render('studio_view', Mock()) assert_in( - '
      ', + ( + '
      ' + ), studio_fragment.content ) assert_in('
      ', studio_fragment.content) @@ -99,6 +102,11 @@ def test_check_document_url(self): # pylint: disable=no-self-use assert_equals(json.loads(res.body), {'status_code': 404}) + data = json.dumps({}) + res = block.handle('check_url', make_request(data)) + + assert_equals(json.loads(res.body), {'status_code': 400}) + def test_document_publish_event(self): # pylint: disable=no-self-use """ Test event publishing in GoogleDocumentBlock""" block = TestGoogleDocumentBlock.make_document_block() diff --git a/pylintrc b/pylintrc deleted file mode 100644 index 2194caf..0000000 --- a/pylintrc +++ /dev/null @@ -1,328 +0,0 @@ -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS, migrations - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable= - locally-disabled, - too-few-public-methods, - bad-builtin, - star-args, - abstract-class-not-used, - abstract-class-little-used, - no-init, - fixme, - too-many-lines, - no-self-use, - too-many-ancestors, - too-many-instance-attributes, - too-few-public-methods, - too-many-public-methods, - too-many-return-statements, - too-many-branches, - too-many-arguments, - too-many-locals, - duplicate-code, - import-error, - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (RP0004). -comment=no - - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns)$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -# Normally limited to 30 chars, but test names can be as long as they want -function-rgx=([a-z_][a-z0-9_]{2,30}|test_[a-z0-9_]+)$ - -# Regular expression which should only match correct method names -# Normally, should be all lower, but some exceptions for unittest methods -method-rgx=([a-z_][a-z0-9_]{2,40}|setUp|set[Uu]pClass|tearDown|tear[Dd]ownClass|assert[A-Z]\w*|maxDiff|test_[a-z0-9_]+)$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct attribute names in class -# bodies -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=f,i,j,k,db,ex,Run,_,__ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=__.*__|test_.+|setUp|tearDown - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=120 - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - -# List of optional constructs for which whitespace checking is disabled -no-space-check=trailing-comma,dict-separator - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject - -# When zope mode is activated, add a predefined set of Zope acquired attributes -# to generated-members. -zope=no - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. Python regular -# expressions are accepted. -generated-members= - REQUEST, - acl_users, - aq_parent, - objects, - DoesNotExist, - can_read, - can_write, - get_url, - size, - content, - status_code, -# For factory_boy factories - create, - build, -# For xblocks - fields, -# For locations - tag, - org, - course, - category, - name, - revision, -# For django models - _meta, - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the beginning of the name of dummy variables -# (i.e. not used). -dummy-variables-rgx=_|dummy|unused|.*_unused - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/requirements.txt b/requirements.txt index 3a0de60..b78dffd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ # May need to do this on a Mac - CPATH=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libxml2 CFLAGS=-Qunused-arguments CPPFLAGS=-Qunused-arguments pip install lxml +ddt lxml mock selenium From 57ccc55d1240304a73b4aa6dc92d6ea8a833ef7a Mon Sep 17 00:00:00 2001 From: Martyn James Date: Fri, 13 Feb 2015 15:55:13 -0500 Subject: [PATCH 11/11] Feedback inspired changes --- README.md | 2 +- google_drive/google_calendar.py | 31 +- google_drive/google_docs.py | 15 +- .../templates/html/google_calendar.html | 2 +- google_drive/tests/unit/test_calendar.py | 5 + google_drive/tests/unit/test_docs.py | 4 + pylintrc | 328 ------------------ setup.py | 3 +- 8 files changed, 40 insertions(+), 350 deletions(-) delete mode 100644 pylintrc diff --git a/README.md b/README.md index 9c44182..213860a 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Analogically, validation takes place for embedded code of Google Drive File. Since error status codes start with 400, it's assumed that each status code that's larger than or equal to 400 states that file is invalid. If for any reason exception occurs while getting an HTTP response, error code is returned, thus overriding default signalization that is invoked by edx platform when the 500 status code is reported. -a11y +Accessibility (a11y) ---- For users with a visual impairment: diff --git a/google_drive/google_calendar.py b/google_drive/google_calendar.py index 7ebf2c6..a51e2a9 100644 --- a/google_drive/google_calendar.py +++ b/google_drive/google_calendar.py @@ -1,6 +1,4 @@ -""" -Google Calendar XBlock implementation -""" +""" Google Calendar XBlock implementation """ # -*- coding: utf-8 -*- # @@ -22,10 +20,6 @@ DEFAULT_CALENDAR_ID = "edx.org_lom804qe3ttspplj1bgeu1l3ak@group.calendar.google.com" CALENDAR_TEMPLATE = "/templates/html/google_calendar.html" CALENDAR_EDIT_TEMPLATE = "/templates/html/google_calendar_edit.html" -CALENDAR_IFRAME = ( - '' -) # Classes ########################################################### @@ -66,12 +60,10 @@ def student_view(self, context): # pylint: disable=unused-argument """ fragment = Fragment() - view = self.views[self.default_view][1] - iframe = CALENDAR_IFRAME.format(view, self.calendar_id, self.display_name) - fragment.add_content(RESOURCE_LOADER.render_template(CALENDAR_TEMPLATE, { - "self": self, - "iframe": iframe + "mode": self.views[self.default_view][1], + "src": self.calendar_id, + "title": self.display_name, })) fragment.add_css(RESOURCE_LOADER.load_unicode('public/css/google_calendar.css')) fragment.add_javascript(RESOURCE_LOADER.load_unicode('public/js/google_calendar.js')) @@ -105,9 +97,18 @@ def studio_submit(self, submissions, suffix=''): # pylint: disable=unused-argum """ Change the settings for this XBlock given by the Studio user """ - self.display_name = submissions['display_name'] - self.calendar_id = submissions['calendar_id'] - self.default_view = submissions['default_view'] + if not isinstance(submissions, dict): + LOG.error("submissions object from Studio is not a dict - %r", submissions) + return { + 'result': 'error' + } + + if 'display_name' in submissions: + self.display_name = submissions['display_name'] + if 'calendar_id' in submissions: + self.calendar_id = submissions['calendar_id'] + if 'default_view' in submissions: + self.default_view = submissions['default_view'] return { 'result': 'success', diff --git a/google_drive/google_docs.py b/google_drive/google_docs.py index f5ec624..8c6f1b9 100644 --- a/google_drive/google_docs.py +++ b/google_drive/google_docs.py @@ -107,9 +107,18 @@ def studio_submit(self, submissions, suffix=''): # pylint: disable=unused-argum """ Change the settings for this XBlock given by the Studio user """ - self.display_name = submissions['display_name'] - self.embed_code = submissions['embed_code'] - self.alt_text = submissions['alt_text'] + if not isinstance(submissions, dict): + LOG.error("submissions object from Studio is not a dict - %r", submissions) + return { + 'result': 'error' + } + + if 'display_name' in submissions: + self.display_name = submissions['display_name'] + if 'embed_code' in submissions: + self.embed_code = submissions['embed_code'] + if 'alt_text' in submissions: + self.alt_text = submissions['alt_text'] return { 'result': 'success', diff --git a/google_drive/templates/html/google_calendar.html b/google_drive/templates/html/google_calendar.html index 606de4d..894645b 100644 --- a/google_drive/templates/html/google_calendar.html +++ b/google_drive/templates/html/google_calendar.html @@ -1,4 +1,4 @@
      - {{ iframe|safe }} +
      diff --git a/google_drive/tests/unit/test_calendar.py b/google_drive/tests/unit/test_calendar.py index 6326ff4..3abbb2a 100644 --- a/google_drive/tests/unit/test_calendar.py +++ b/google_drive/tests/unit/test_calendar.py @@ -65,6 +65,11 @@ def test_calendar_document_submit(self): # pylint: disable=no-self-use assert_equals(block.calendar_id, "google1234") assert_equals(block.default_view, 1) + body = json.dumps('') + res = block.handle('studio_submit', make_request(body)) + # pylint: disable=no-value-for-parameter + assert_equals(json.loads(res.body), {'result': 'error'}) + def test_calendar_publish_event(self): # pylint: disable=no-self-use """ Test event publishing in GoogleCalendarBlock""" block = TestGoogleCalendarBlock.make_calendar_block() diff --git a/google_drive/tests/unit/test_docs.py b/google_drive/tests/unit/test_docs.py index a499403..744e8b0 100644 --- a/google_drive/tests/unit/test_docs.py +++ b/google_drive/tests/unit/test_docs.py @@ -68,6 +68,10 @@ def test_studio_document_submit(self): # pylint: disable=no-self-use assert_equals(block.embed_code, "