diff --git a/common/viewUtils.py b/common/viewUtils.py index 9a66ef5b..06c994fb 100644 --- a/common/viewUtils.py +++ b/common/viewUtils.py @@ -11,9 +11,9 @@ from visualizer.models import TextForWinner from visualizer.sankey.graphToD3 import D3Sankey from visualizer.sidecar.reader import SidecarReader -from visualizer.tabular.tabular import TabulateByRoundInteractive,\ - TabulateByRound,\ - TabulateByCandidate,\ +from visualizer.tabular.tabular import TabulateByRoundInteractive, \ + TabulateByRound, \ + TabulateByCandidate, \ SingleTableSummary diff --git a/electionpage/tests.py b/electionpage/tests.py index 9243b042..08d7bd11 100644 --- a/electionpage/tests.py +++ b/electionpage/tests.py @@ -1,3 +1,4 @@ + """ Tests for the election pages: The general info page exists @@ -23,8 +24,10 @@ from django.core.files import File from django.urls import reverse from requests_mock import Mocker -from selenium.common.exceptions import NoSuchElementException +from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait from common.testUtils import TestHelpers from electionpage.models import ElectionPage, ScrapableElectionPage, SingleSourceElectionPage @@ -183,14 +186,20 @@ def test_create_scrapable_page_logged_out(self): def test_create_scrapable_page(self): """ With proper permissions, can create a scrapable election page """ - def submit_with_num_elections_and_get_error(num): - self.browser.find_element(By.ID, "id_numElections").clear() - self.browser.find_element(By.ID, "id_numElections").send_keys(num) + def submit_with_num_elections_and_get_error(num, expectReload): + numElectionsElement = self.browser.find_element(By.ID, "id_numElections") + numElectionsElement.clear() + numElectionsElement.send_keys(num) self.browser.find_element(By.ID, "submit").click() + + if expectReload: + self._ensure_eventually_asserts( + lambda: EC.staleness_of(numElectionsElement)) + try: return self.browser.find_element( By.ID, "id_numElections").get_attribute("validationMessage") - except NoSuchElementException: + except (NoSuchElementException, StaleElementReferenceException): return None self._provide_all_credentials() @@ -207,16 +216,16 @@ def submit_with_num_elections_and_get_error(num): # Hitting submit should fail with 0 numElections self.assertEqual( - submit_with_num_elections_and_get_error('0'), + submit_with_num_elections_and_get_error('0', False), "Value must be greater than or equal to 1.") # Hitting submit should fail with >60 numElections self.assertEqual( - submit_with_num_elections_and_get_error('65'), + submit_with_num_elections_and_get_error('65', False), "Value must be less than or equal to 60.") # Finally, should succeed with 2 - self.assertIsNone(submit_with_num_elections_and_get_error('2')) + self.assertIsNone(submit_with_num_elections_and_get_error('2', True)) self.assertEqual(urlparse(self.browser.current_url).path, '/pPopulate/cuteslug') def test_scrapable_page_slug_must_be_unique(self): @@ -295,6 +304,12 @@ def test_are_results_certified_initializes_correctly(self): self.browser.find_element(By.ID, "id_areResultsCertified").click() self.browser.find_element(By.ID, "submit").click() + # Wait for the object to be loaded + self._ensure_eventually_asserts( + lambda: ScrapableElectionPage.objects.filter( + slug='cuteslug').count() == 1) + + # Then ensure all scrapers have areResultsCertified for scraper in ScrapableElectionPage.objects.get(slug='cuteslug').listOfScrapers.all(): self.assertTrue(scraper.areResultsCertified) diff --git a/infra/requirements-core.txt b/infra/requirements-core.txt index bf46c004..f7f72a0a 100644 --- a/infra/requirements-core.txt +++ b/infra/requirements-core.txt @@ -25,6 +25,7 @@ coverage==7.2.7 pytest-django==4.5.2 pylint==2.17.4 pylint-django==2.5.3 +pycodestyle==2.12.1 # For django-rest djangorestframework==3.15.2 @@ -35,6 +36,7 @@ markdown==3.4.3 celery==5.3.0 django-cleanup==7.0.0 moviepy==1.0.3 +Pillow==9.5.0 # moviepy bug workaround: github.com/Zulko/moviepy/issues/2072 # For Heroku gunicorn==22.0.0 diff --git a/movie/creation/movieCreator.py b/movie/creation/movieCreator.py index 70964c2b..24dde052 100644 --- a/movie/creation/movieCreator.py +++ b/movie/creation/movieCreator.py @@ -11,7 +11,7 @@ from django.core.files import File from django.urls import reverse from moviepy.config import change_settings -from moviepy.editor import AudioFileClip, CompositeVideoClip, ImageClip, TextClip,\ +from moviepy.editor import AudioFileClip, CompositeVideoClip, ImageClip, TextClip, \ concatenate_videoclips import selenium diff --git a/movie/creation/textToSpeech.py b/movie/creation/textToSpeech.py index cb192653..6e524829 100644 --- a/movie/creation/textToSpeech.py +++ b/movie/creation/textToSpeech.py @@ -56,12 +56,12 @@ def __init__(self, pollyClient, s3Client, text): def _spawn_task(self, text): """ Spawns the AWS job """ return self.pollyClient.start_speech_synthesis_task( - VoiceId='Joanna', + VoiceId='Danielle', OutputS3BucketName=settings.AWS_POLLY_STORAGE_BUCKET_NAME, OutputS3KeyPrefix=self.prefix, OutputFormat='mp3', Text=text, - Engine="neural") + Engine="generative") def _get_task_status(self): """ Poll Polly for the task status """ @@ -132,7 +132,7 @@ def download_if_ready(self, toFilename): return True - def download_synchronously(self, timeoutSeconds=20): + def download_synchronously(self, timeoutSeconds=30): """ Wait up to timeoutSeconds, waiting for the task to complete. @return a tempfile object: the file will be deleted once the object is destructed. """ pollIntervalSeconds = 1 diff --git a/movie/tests.py b/movie/tests.py index a37b43b6..3a4e1ef6 100644 --- a/movie/tests.py +++ b/movie/tests.py @@ -185,8 +185,8 @@ def test_avoid_upload_collision(self): slug = "slug" assert self._num_movies() == 0 - with tempfile.NamedTemporaryFile(suffix=".mp4") as mp4Tf,\ - tempfile.NamedTemporaryFile(suffix=".gif") as gifTf,\ + with tempfile.NamedTemporaryFile(suffix=".mp4") as mp4Tf, \ + tempfile.NamedTemporaryFile(suffix=".gif") as gifTf, \ tempfile.NamedTemporaryFile(suffix=".png") as imageTf: movie = Movie() movie.resolutionWidth = 1