From fc94ed4b7bab85da0f0305ff1e0cb5efe6a2f375 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 1 Feb 2019 12:07:06 -0600 Subject: [PATCH 1/8] Update setuptools from 40.6.3 to 40.7.2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a14c6997..f45ee5d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ matplotlib==3.0.2 Pillow==5.4.1 -setuptools==40.6.3 +setuptools==40.7.2 From 9bfcb6a947ada9d12494fdff0de4b2362d5df364 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 1 Feb 2019 12:07:08 -0600 Subject: [PATCH 2/8] Update pytest from 4.1.0 to 4.2.0 --- requirements_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index bacc5da6..3474386e 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,5 +1,5 @@ pexpect==4.6.0 -pytest==4.1.0 +pytest==4.2.0 # Running tests on Travis codacy-coverage==1.3.11 From 37f85815e918974a125eaaaedc1409a4cf1cead6 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 1 Feb 2019 12:38:00 -0600 Subject: [PATCH 3/8] Refactor take_score_data Factor out user input take_student_scores. Reassign responsibility for loading class data to assemble_chart_data. Signed-off-by: David --- .../chart_generator/take_chart_data_UI.py | 41 +++++++---- dionysus_app/chart_generator/create_chart.py | 8 +- .../UI_menus_tests/test_take_chart_data_UI.py | 73 +++++++++++++++++-- 3 files changed, 102 insertions(+), 20 deletions(-) diff --git a/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py b/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py index 0ff652a0..600024ce 100644 --- a/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py +++ b/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py @@ -4,7 +4,7 @@ # score entry: -from dionysus_app.class_functions import load_class_data, get_avatar_path +from dionysus_app.class_functions import get_avatar_path from dionysus_app.data_folder import DataFolder from dionysus_app.UI_menus.UI_functions import input_is_essentially_blank @@ -12,7 +12,32 @@ CLASSLIST_DATA_PATH = DataFolder.generate_rel_path(DataFolder.CLASS_DATA.value) -def take_score_data(class_name: str): +def take_score_data(class_name: str, class_data_dict: dict): + """ + Prints score taking instructions, calls take_student_scores. + Prints a newline after completion for readability. + + Returns dict with scores as keys, lists of Path objects as + values. eg student_scores = {33: [Path_obj1, Path_obj2, Path_obj3, + 17: [Path_obj1, Path_obj2, Path_obj3] + } + + :param class_name: str + :param class_data_dict: dict + :return: dict + """ + print(f"\nEnter student scores for {class_name}: \n" + f"Type score for each student, or '_' to exclude student, and press enter.") + + student_scores = take_student_scores(class_name, class_data_dict) + + # Newline between last score and 'Please enter a chart name/title: ' + print('\n') + + return student_scores + + +def take_student_scores(class_name: str, class_data_dict: dict): """ UI function presenting student names from supplied class one at a time and taking a score for each. @@ -27,18 +52,11 @@ def take_score_data(class_name: str): 17: [Path_obj1, Path_obj2, Path_obj3] } - :param class_name: str + :param class_data_dict: dict :return: dict """ - - class_data_dict = load_class_data(class_name) - student_scores = {} - - print(f"\nEnter student scores for {class_name}: \n" - f"Type score for each student, or '_' to exclude student, and press enter.") - for student_name in list(class_data_dict.keys()): student_avatar_filename = class_data_dict[student_name][0] @@ -49,9 +67,6 @@ def take_score_data(class_name: str): if student_score: student_scores[student_score] = student_scores.get(student_score, []) + [avatar_path] - # Newline between last score and 'Please enter a chart name/title: ' - print('\n') - return student_scores diff --git a/dionysus_app/chart_generator/create_chart.py b/dionysus_app/chart_generator/create_chart.py index 0c26732b..ec3a3695 100644 --- a/dionysus_app/chart_generator/create_chart.py +++ b/dionysus_app/chart_generator/create_chart.py @@ -14,7 +14,7 @@ from dionysus_app.chart_generator.generate_image import generate_chart_image from dionysus_app.chart_generator.process_chart_data import DEFAULT_CHART_PARAMS -from dionysus_app.class_functions import select_classlist +from dionysus_app.class_functions import select_classlist, load_class_data from dionysus_app.data_folder import CHART_DATA_FILE_TYPE, DataFolder from dionysus_app.file_functions import convert_to_json, copy_file from dionysus_app.UI_menus.chart_generator.create_chart_UI import (display_image_save_as, @@ -66,6 +66,8 @@ def assemble_chart_data(): """ Collect data/user input for new chart. + Get classname from user, load class data, take chart data from user. + Return values for chart_data_dict assembly: class_name: str chart_name: str @@ -78,7 +80,9 @@ def assemble_chart_data(): class_name = select_classlist() # TODO: warn for empty classlist - student_scores: dict = take_score_data(class_name) + class_data_dict = load_class_data(class_name) + + student_scores: dict = take_score_data(class_name, class_data_dict) chart_name = take_chart_name() diff --git a/test_suite/UI_menus_tests/test_take_chart_data_UI.py b/test_suite/UI_menus_tests/test_take_chart_data_UI.py index ca994ae3..8014cd33 100644 --- a/test_suite/UI_menus_tests/test_take_chart_data_UI.py +++ b/test_suite/UI_menus_tests/test_take_chart_data_UI.py @@ -1,8 +1,71 @@ -# import signal -# from unittest import TestCase -# -# from dionysus_app.chart_generator.take_graph_data import take_score_entry -# import pexpect.run + +from unittest import TestCase + +from dionysus_app.UI_menus.chart_generator.take_chart_data_UI import (take_score_data, + ) + + +class TestTakeScoreData(TestCase): + def test_take_score_data(self): + self.fail() + + + +def take_score_data(class_name: str): + """ + UI function presenting student names from supplied class one at a + time and taking a score for each. + Path objects for each student's avatar are added to a list of avatar + Paths corresponding to scores. + + Scores can be int or float, eg 78.5 is valid entry, and are + converted to float (from str) by default. + + Return is a dict with scores as keys, lists of Path objects as + values. eg student_scores = {33: [Path_obj1, Path_obj2, Path_obj3, + 17: [Path_obj1, Path_obj2, Path_obj3] + } + + + :param class_name: str + :return: dict + """ + + class_data_dict = load_class_data(class_name) + + student_scores = {} + + print(f"\nEnter student scores for {class_name}: \n" + f"Type score for each student, or '_' to exclude student, and press enter.") + + for student_name in list(class_data_dict.keys()): + + student_avatar_filename = class_data_dict[student_name][0] + avatar_path = get_avatar_path(class_name, student_avatar_filename) + + student_score = take_score_entry(student_name) + # add avatar to list of avatars for score + if student_score: + student_scores[student_score] = student_scores.get(student_score, []) + [avatar_path] + + # Newline between last score and 'Please enter a chart name/title: ' + print('\n') + + return student_scores + + + + + + + + + + + + + + def test_take_score_entry(): From 6fc8f2f2bced65a05b326fc2406a791cdc0740e7 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 1 Feb 2019 15:01:38 -0600 Subject: [PATCH 4/8] Add test for take_score_data. Signed-off-by: David --- .../UI_menus_tests/test_take_chart_data_UI.py | 65 ++++++------------- 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/test_suite/UI_menus_tests/test_take_chart_data_UI.py b/test_suite/UI_menus_tests/test_take_chart_data_UI.py index 8014cd33..e2bc6d52 100644 --- a/test_suite/UI_menus_tests/test_take_chart_data_UI.py +++ b/test_suite/UI_menus_tests/test_take_chart_data_UI.py @@ -1,59 +1,34 @@ - -from unittest import TestCase +from unittest import mock, TestCase +from unittest.mock import patch from dionysus_app.UI_menus.chart_generator.take_chart_data_UI import (take_score_data, + take_student_scores, ) +from test_suite.testing_class_data import testing_class_data_set as test_class_data class TestTakeScoreData(TestCase): - def test_take_score_data(self): - self.fail() - - - -def take_score_data(class_name: str): - """ - UI function presenting student names from supplied class one at a - time and taking a score for each. - Path objects for each student's avatar are added to a list of avatar - Paths corresponding to scores. - - Scores can be int or float, eg 78.5 is valid entry, and are - converted to float (from str) by default. - - Return is a dict with scores as keys, lists of Path objects as - values. eg student_scores = {33: [Path_obj1, Path_obj2, Path_obj3, - 17: [Path_obj1, Path_obj2, Path_obj3] - } - - - :param class_name: str - :return: dict - """ - - class_data_dict = load_class_data(class_name) - - student_scores = {} - - print(f"\nEnter student scores for {class_name}: \n" - f"Type score for each student, or '_' to exclude student, and press enter.") - - for student_name in list(class_data_dict.keys()): - - student_avatar_filename = class_data_dict[student_name][0] - avatar_path = get_avatar_path(class_name, student_avatar_filename) + def setUp(self): + self.test_classname = 'the knights who say ni' + self.test_class_data_dict = test_class_data['loaded_dict'] - student_score = take_score_entry(student_name) - # add avatar to list of avatars for score - if student_score: - student_scores[student_score] = student_scores.get(student_score, []) + [avatar_path] + self.score_entry_instruction = (f"\nEnter student scores for {self.test_classname}: \n" + f"Type score for each student, or '_' to exclude student, and press enter.") + self.newline_after_entry = '\n' + self.print_calls = [self.score_entry_instruction, self.newline_after_entry] - # Newline between last score and 'Please enter a chart name/title: ' - print('\n') + self.mock_score_avatar_dict = {'scores': 'list of avatars'} - return student_scores + @patch('dionysus_app.UI_menus.chart_generator.take_chart_data_UI.take_student_scores') + @patch('dionysus_app.UI_menus.chart_generator.take_chart_data_UI.print') + def test_take_score_data(self, mocked_print, mocked_take_student_scores): + mocked_take_student_scores.return_value = self.mock_score_avatar_dict + assert take_score_data(self.test_classname, + self.test_class_data_dict) == self.mock_score_avatar_dict + assert mocked_print.call_args_list == [mock.call(print_call) for print_call in self.print_calls] + mocked_take_student_scores.called_once_with(self.test_classname, self.test_class_data_dict) From 7d1f8236501d7a9c6cf4372a8755869fe0035cd5 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 6 Feb 2019 11:34:30 -0600 Subject: [PATCH 5/8] Add test for take_student_scores. Signed-off-by: David --- .../UI_menus_tests/test_take_chart_data_UI.py | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/test_suite/UI_menus_tests/test_take_chart_data_UI.py b/test_suite/UI_menus_tests/test_take_chart_data_UI.py index e2bc6d52..f0131e37 100644 --- a/test_suite/UI_menus_tests/test_take_chart_data_UI.py +++ b/test_suite/UI_menus_tests/test_take_chart_data_UI.py @@ -1,4 +1,4 @@ -from unittest import mock, TestCase +from unittest import TestCase, mock from unittest.mock import patch from dionysus_app.UI_menus.chart_generator.take_chart_data_UI import (take_score_data, @@ -31,16 +31,49 @@ def test_take_score_data(self, mocked_print, mocked_take_student_scores): mocked_take_student_scores.called_once_with(self.test_classname, self.test_class_data_dict) +class TestTakeStudentScores(TestCase): + mock_DEFAULT_AVATAR_PATH = 'mocked_default_avatar_path' + def setUp(self): + self.mock_DEFAULT_AVATAR_PATH = 'mocked_default_avatar_path' + self.test_classname = 'the knights who say ni' + self.test_class_data_dict = test_class_data['loaded_dict'] - - - - - - - + # NB If data other than avatar in dict values, this test implementation may need to change. + self.mocked_get_avatar_path_return_values = [f'path to {avatar[0]}' if avatar[0] is not None + else self.mock_DEFAULT_AVATAR_PATH + for avatar in test_class_data['loaded_dict'].values()] + + # Gives no score to one student with and without an avatar. + self.mocked_take_score_entry_return_values = [0, 1, 3, None, 50, 99, 100, 1, 2, 3, 4, None, 6, 7, 8] + self.test_take_student_scores_return_value = {0: ['path to Cali_avatar.png'], + 1: [self.mock_DEFAULT_AVATAR_PATH, self.mock_DEFAULT_AVATAR_PATH], + 3: [self.mock_DEFAULT_AVATAR_PATH, self.mock_DEFAULT_AVATAR_PATH], + # None: ['Zach_avatar.png', None], # No score not returned. + 50: [self.mock_DEFAULT_AVATAR_PATH], + 99: [self.mock_DEFAULT_AVATAR_PATH], + 100: [self.mock_DEFAULT_AVATAR_PATH], + 2: ['path to Ashley_avatar.png'], + 4: [self.mock_DEFAULT_AVATAR_PATH], + 6: ['path to Danielle.png'], + 7: [self.mock_DEFAULT_AVATAR_PATH], + 8: [self.mock_DEFAULT_AVATAR_PATH] + } + + @patch('dionysus_app.UI_menus.chart_generator.take_chart_data_UI.get_avatar_path') + @patch('dionysus_app.UI_menus.chart_generator.take_chart_data_UI.take_score_entry') + def test_take_student_scores(self, mocked_take_score_entry, mocked_get_avatar_path): + mocked_take_score_entry.side_effect = self.mocked_take_score_entry_return_values + mocked_get_avatar_path.side_effect = self.mocked_get_avatar_path_return_values + + assert take_student_scores(self.test_classname, + self.test_class_data_dict) == self.test_take_student_scores_return_value + + mocked_take_score_entry.call_args_list = [mock.call(student_name) for student_name in self.test_class_data_dict] + mocked_get_avatar_path.call_args_list = [mock.call(self.test_classname, + self.test_class_data_dict[student_name][0]) + for student_name in self.test_class_data_dict] def test_take_score_entry(): From c4015845f5e563d26d7a1f3a35e151d5a263fbf0 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 6 Feb 2019 12:44:59 -0600 Subject: [PATCH 6/8] Fix bug where avatar for student w/score 0 not added to student_scores. Fixes #141 Signed-off-by: David --- dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py b/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py index 600024ce..6dd0ee37 100644 --- a/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py +++ b/dionysus_app/UI_menus/chart_generator/take_chart_data_UI.py @@ -64,7 +64,7 @@ def take_student_scores(class_name: str, class_data_dict: dict): student_score = take_score_entry(student_name) # add avatar to list of avatars for score - if student_score: + if student_score or student_score is 0: student_scores[student_score] = student_scores.get(student_score, []) + [avatar_path] return student_scores @@ -76,7 +76,7 @@ def take_score_entry(student_name: str, minimum: int=0, maximum: int=100): :param student_name: str :param minimum: int, default=0 :param maximum: int, default=100 - :return: float + :return: float or None """ while True: score = input(f'{student_name}: ') From 4152385c18f399f1c61d9c939a0237eed72797b7 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 6 Feb 2019 12:54:53 -0600 Subject: [PATCH 7/8] Update CHANGELOG, setup.py for bugfix release. Signed-off-by: David --- CHANGELOG.md | 9 +++++---- setup.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e530f7..ba3865eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,17 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.3.1-alpha] ### Added - Improved test coverage. - Add load_chart_data, load_json_from_file - Add .bettercodehub.yml - prevent failed PR checks because of test code. ### Changed -- Fix bug in save_as_dialogue that failed with TypeError when called without filetypes parameter or with default filetypes=None. -- Fix circleci not storing test metadata. - Refactor save_as_dialogue to prevent TypeError. - Refactor load_class_data using load_from_json_file. - +### Fixed +- Fix bug where avatar for student with score of 0 not added to student_scores. +- Fix bug in save_as_dialogue that failed with TypeError when called without filetypes parameter or with default filetypes=None. +- Fix circleci not storing test metadata. ## [0.3.0-alpha] - 2019-01-23 ### Added diff --git a/setup.py b/setup.py index 2b969bf5..4abf0f59 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages setup(name='dionysus_app', - version='0.3.0-alpha', + version='0.3.1-alpha', description='Avatar chart generator', author='David Antonini', author_email='toonarmycaptain@hotmail.com', From a26d3ebc23ebd4e6904bbf5ee55f142873cb104c Mon Sep 17 00:00:00 2001 From: toonarmycaptain Date: Wed, 6 Feb 2019 13:53:35 -0600 Subject: [PATCH 8/8] Fix BCH not excluding test code. (#142) * Fix BCH not excluding test code. --- .bettercodehub.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.bettercodehub.yml b/.bettercodehub.yml index 61a33da0..602a5afc 100644 --- a/.bettercodehub.yml +++ b/.bettercodehub.yml @@ -4,10 +4,10 @@ languages: - name: python production: exclude: - - /test_suite/.* + - .*/test_suite/.* test: include: - - /test_suite/.* + - .*/test_suite/.* component_depth: 2