Skip to content

Commit 95aea4d

Browse files
authored
Merge pull request #333 from open-craft/mtyaka/FAL-2198-django3.2
[FAL-2198] Django 3.2 support
2 parents d5755a3 + b755331 commit 95aea4d

File tree

81 files changed

+956
-794
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+956
-794
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
*~
22
*.pyc
3-
/.coverage
3+
/.coverage*
44
/xblock_problem_builder.egg-info
5-
/workbench.*
5+
/workbench*
66
/dist
77
/templates
88
/var
9+
/src
910
*.iml
1011
.idea/*
1112
dump.rdb

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ help: ## display this help message
88
@echo "Please use \`make <target>' where <target> is one of"
99
@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}'
1010

11-
upgrade:
11+
upgrade: ## Upgrade test requirements
1212
pip-compile --upgrade --output-file test_requirements.txt test_requirements.in
1313

1414
extract_translations: ## extract strings to be translated, outputting .po files
@@ -24,7 +24,7 @@ compile_translations: ## compile translation files, outputting .mo files for eac
2424
cd $(WORKING_DIR) && i18n_tool generate
2525
python manage.py compilejsi18n --namespace ProblemBuilderXBlockI18N --output $(JS_TARGET)
2626

27-
detect_changed_source_translations:
27+
detect_changed_source_translations: ## Detect changes in source code that affect translations
2828
cd $(WORKING_DIR) && i18n_tool changed
2929

3030
dummy_translations: ## generate dummy translation (.po) files

README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,54 @@ make test.unit
169169
make test.integration
170170
```
171171

172+
Debugging CI Failures
173+
---------------------
174+
175+
Sometimes it can be hard to figure out why some tests fail in the CI.
176+
When Circle CI browser based tests fail for unknown reasons, it can be helpful to run them with VNC enabled
177+
so that you can observe the browser (or even interact with it) while the tests are running.
178+
179+
To enable VNC on Circle CI, first re-run the failing test with SSH enabled: in the Circle CI UI,
180+
click the "Rerun" dropdown and select "Rerun Job with SSH". The job will be re-run with SSH enabled.
181+
You can find the IP/port combination that lets you log into the VM with your github SSH key under the "Enable SSH"
182+
step in the pipeline UI.
183+
184+
SSH into the VM, forwarding the VNC port:
185+
186+
```bash
187+
ssh -p <port> <ip-address> -L 5900:localhost:5900
188+
```
189+
190+
Install the required packages:
191+
192+
```bash
193+
sudo apt-get install -yq xvfb x11vnc fluxbox
194+
```
195+
196+
Start up xvfb and the VNC server:
197+
198+
```bash
199+
rm -f /tmp/.X$(echo ${DISPLAY:-:0} | cut -b2-)-lock
200+
Xvfb ${DISPLAY:-:0} -ac -listen tcp -screen 0 1440x900x24 &
201+
/usr/bin/fluxbox -display ${DISPLAY:-:0} -screen 0 &
202+
x11vnc -display ${DISPLAY:-:0} -forever -noxdamage -rfbport 5900 -quiet -passwd pass &
203+
```
204+
205+
You should now be able to connect to the server via VNC. On macOS, you can use the built-in VNC viewer
206+
that you can launch by opening Finder and choosing the "Go -> Connect to Server.." from the menu.
207+
Type in `localhost:5900` and enter `pass` when asked for the password.
208+
209+
You are all set up to run integration tests with screen sharing enabled.
210+
For some reason Firefox does not want to start in foreground mode when run as non-root,
211+
so you'll have to run the tests as root.
212+
213+
```bash
214+
unset MOZ_HEADLESS
215+
cd /home/circleci/project
216+
source venv/bin/activate
217+
make test
218+
```
219+
172220
Working with Translations
173221
-------------------------
174222

circle.yml

Lines changed: 28 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
version: 2.1
2+
orbs:
3+
browser-tools: circleci/[email protected]
24
jobs:
35
build:
46
docker:
57
- image: <<parameters.docker_image>>
6-
- image: circleci/mysql:5.6
8+
- image: circleci/mysql:5.7
79
command: mysqld --character-set-server=latin1 --collation-server=latin1_swedish_ci
810
environment:
911
MYSQL_ROOT_PASSWORD: rootpw
@@ -12,29 +14,17 @@ jobs:
1214
type: string
1315
docker_image:
1416
type: string
17+
parallel:
18+
default: 1
19+
type: integer
20+
parallelism: <<parameters.parallel>>
1521
environment:
1622
MOZ_HEADLESS: 1
1723
WORKBENCH_DATABASES: '{"default": {"ENGINE": "django.db.backends.mysql", "NAME": "db", "USER": "root", "PASSWORD": "rootpw", "HOST": "127.0.0.1", "OPTIONS": {"charset": "utf8mb4"}}}'
1824
steps:
1925
- checkout
20-
- run:
21-
name: Update system
22-
command: |
23-
sudo apt-get update
24-
sudo apt-get install -y libgtk3.0-cil-dev libasound2 libasound2 libdbus-glib-1-2 libdbus-1-3 libgtk2.0-0 default-libmysqlclient-dev
25-
- run:
26-
name: Install geckodriver
27-
command: |
28-
wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
29-
tar xzf geckodriver-v0.26.0-linux64.tar.gz
30-
sudo mv geckodriver /usr/local/bin/geckodriver
31-
which geckodriver
32-
- run:
33-
name: Install new firefox
34-
command: |
35-
wget https://archive.mozilla.org/pub/firefox/releases/70.0.1/linux-x86_64/en-US/firefox-70.0.1.tar.bz2
36-
tar jxf firefox-70.0.1.tar.bz2
37-
export PATH="$(pwd)/firefox/:$PATH"
26+
- browser-tools/install-firefox
27+
- browser-tools/install-geckodriver
3828
- run:
3929
name: Sync submodules
4030
command: git submodule sync
@@ -44,43 +34,14 @@ jobs:
4434
- run:
4535
name: Run tests
4636
command: |
47-
export PATH="$(pwd)/firefox/:$PATH"
48-
mkdir -p /tmp/coverage/$CIRCLE_SHA1
4937
virtualenv venv
5038
source venv/bin/activate
39+
mkdir var
40+
pip -q install -r requirements.txt
41+
pip -q install -r requirements-dev.txt
5142
pip -q install -r test_requirements.txt
5243
pip install -e .
5344
<<parameters.test_command>>
54-
if [ -e .coverage.* ]; then
55-
cp .coverage.* /tmp/coverage/$CIRCLE_SHA1/.
56-
fi
57-
- persist_to_workspace:
58-
root: /tmp
59-
paths:
60-
- coverage
61-
62-
coverage:
63-
docker:
64-
- image: <<parameters.docker_image>>
65-
parameters:
66-
docker_image:
67-
type: string
68-
steps:
69-
- checkout
70-
- run:
71-
name: Update system and install deps
72-
command: |
73-
sudo apt-get update
74-
virtualenv venv
75-
source venv/bin/activate
76-
pip install 'coverage==5.0.3'
77-
- attach_workspace:
78-
at: /tmp/workspace
79-
- run:
80-
command: |
81-
source venv/bin/activate
82-
coverage combine /tmp/workspace/coverage/$CIRCLE_SHA1/
83-
coverage report
8445
8546
deploy:
8647
docker:
@@ -131,42 +92,38 @@ workflows:
13192
build_and_deploy:
13293
jobs:
13394
- build:
134-
name: py35-quality
95+
name: py38-quality
13596
test_command: make quality
136-
docker_image: circleci/python:3.5-buster-browsers
97+
docker_image: cimg/python:3.8-browsers
13798
filters:
13899
tags:
139100
only: /.*/
140101
- build:
141-
name: py35-unit
102+
name: py38-unit
142103
test_command: make test.unit
143-
docker_image: circleci/python:3.5-buster-browsers
104+
docker_image: cimg/python:3.8-browsers
144105
filters:
145106
tags:
146107
only: /.*/
147108
- build:
148-
name: py35-integration
149-
test_command: make test.integration
150-
docker_image: circleci/python:3.5-buster-browsers
151-
filters:
152-
tags:
153-
only: /.*/
154-
- coverage:
155-
name: py35-coverage
156-
docker_image: circleci/python:3.5-buster
109+
name: py38-integration
110+
parallel: 4
111+
test_command: |
112+
set -e
113+
TEST_FILES=$(circleci tests glob "problem_builder/tests/integration/test_*.py" | circleci tests split --split-by=timings)
114+
pytest --verbose $TEST_FILES
115+
docker_image: cimg/python:3.8-browsers
157116
filters:
158117
tags:
159118
only: /.*/
160-
requires:
161-
- py35-quality
162-
- py35-unit
163-
- py35-integration
164119
- deploy:
165-
name: py35-deploy-bdist_wheel
166-
docker_image: circleci/python:3.5-buster
120+
name: py38-deploy-bdist_wheel
121+
docker_image: cimg/python:3.8
167122
dist_type: bdist_wheel
168123
requires:
169-
- py35-coverage
124+
- py38-quality
125+
- py38-unit
126+
- py38-integration
170127
filters:
171128
tags:
172129
only: /v[0-9]+(\.[0-9]+)*/

problem_builder/answer.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
#
32
# Copyright (c) 2014-2015 Harvard, edX & OpenCraft
43
#
@@ -23,12 +22,11 @@
2322
import uuid
2423

2524
import pkg_resources
26-
import six
27-
from lazy import lazy
2825
from django import utils
26+
from lazy import lazy
27+
from web_fragments.fragment import Fragment
2928
from xblock.core import XBlock
3029
from xblock.fields import Integer, Scope, String
31-
from xblock.fragment import Fragment
3230
from xblock.validation import ValidationMessage
3331
from xblockutils.resources import ResourceLoader
3432
from xblockutils.studio_editable import (StudioEditableXBlockMixin,
@@ -60,7 +58,7 @@ class AnswerMixin(XBlockWithPreviewMixin, XBlockWithTranslationServiceMixin, Stu
6058
Mixin to give an XBlock the ability to read/write data to the Answers DB table.
6159
"""
6260
def build_user_state_data(self, context=None):
63-
result = super(AnswerMixin, self).build_user_state_data()
61+
result = super().build_user_state_data()
6462
result['student_input'] = self.student_input
6563
return result
6664

@@ -131,13 +129,13 @@ def validate_field_data(self, validation, data):
131129
"""
132130
Validate this block's field data.
133131
"""
134-
super(AnswerMixin, self).validate_field_data(validation, data)
132+
super().validate_field_data(validation, data)
135133

136134
def add_error(msg):
137135
validation.add(ValidationMessage(ValidationMessage.ERROR, msg))
138136

139137
if not data.name:
140-
add_error(u"A Question ID is required.")
138+
add_error("A Question ID is required.")
141139

142140

143141
@XBlock.needs("i18n")
@@ -150,7 +148,7 @@ class AnswerBlock(SubmittingXBlockMixin, AnswerMixin, QuestionMixin, StudioEdita
150148
to make them searchable and referenceable across xblocks.
151149
"""
152150
CATEGORY = 'pb-answer'
153-
STUDIO_LABEL = _(u"Long Answer")
151+
STUDIO_LABEL = _("Long Answer")
154152
answerable = True
155153

156154
name = String(
@@ -188,11 +186,10 @@ def resource_string(path):
188186
return data.decode("utf8")
189187

190188
def get_translation_content(self):
189+
lang = utils.translation.to_locale(utils.translation.get_language())
191190
try:
192-
return self.resource_string('public/js/translations/{lang}/textjs.js'.format(
193-
lang=utils.translation.to_locale(utils.translation.get_language()),
194-
))
195-
except IOError:
191+
return self.resource_string(f'public/js/translations/{lang}/textjs.js')
192+
except OSError:
196193
return self.resource_string('public/js/translations/en/textjs.js')
197194

198195
def mentoring_view(self, context=None):
@@ -245,7 +242,8 @@ def submit(self, submission):
245242
item_key['item_id'] = self.name
246243
sub_api.create_submission(item_key, self.student_input)
247244

248-
log.info(u'Answer submitted for`{}`: "{}"'.format(self.name, self.student_input))
245+
log_message = f'Answer submitted for`{self.name}`: "{self.student_input}"'
246+
log.info(log_message)
249247
return self.get_results()
250248

251249
@property
@@ -264,7 +262,7 @@ def save(self):
264262
"""
265263
Replicate data changes on the related Django model used for sharing of data accross XBlocks
266264
"""
267-
super(AnswerBlock, self).save()
265+
super().save()
268266

269267
student_id = self._get_student_id()
270268
if not student_id:
@@ -294,7 +292,7 @@ def student_view_data(self, context=None):
294292
"""
295293
return {
296294
'id': self.name,
297-
'block_id': six.text_type(self.scope_ids.usage_id),
295+
'block_id': str(self.scope_ids.usage_id),
298296
'display_name': self.display_name,
299297
'type': self.CATEGORY,
300298
'weight': self.weight,
@@ -310,7 +308,7 @@ class AnswerRecapBlock(AnswerMixin, StudioEditableXBlockMixin, XBlock):
310308
"""
311309

312310
CATEGORY = 'pb-answer-recap'
313-
STUDIO_LABEL = _(u"Long Answer Recap")
311+
STUDIO_LABEL = _("Long Answer Recap")
314312

315313
name = String(
316314
display_name=_("Question ID"),
@@ -343,9 +341,9 @@ def mentoring_view(self, context=None):
343341
location = self.location.replace(branch=None, version=None) # Standardize the key in case it isn't already
344342
target_key = {
345343
'student_id': student_submissions_key,
346-
'course_id': six.text_type(location.course_key),
344+
'course_id': str(location.course_key),
347345
'item_id': self.name,
348-
'item_type': u'pb-answer',
346+
'item_type': 'pb-answer',
349347
}
350348
submissions = sub_api.get_submissions(target_key, limit=1)
351349
try:
@@ -381,6 +379,6 @@ def student_view_data(self, context=None):
381379
'name': self.name, # For backwards compatibility; same as 'id'
382380
'display_name': self.display_name,
383381
'description': self.description,
384-
'block_id': six.text_type(self.scope_ids.usage_id),
382+
'block_id': str(self.scope_ids.usage_id),
385383
'type': self.CATEGORY
386384
}

0 commit comments

Comments
 (0)