From: Christian Heller Date: Fri, 16 Aug 2024 01:35:34 +0000 (+0200) Subject: For various boolean settings, treat absence of form POST as setting False. X-Git-Url: https://plomlompom.com/repos/%7B%7B%20web_path%20%7D%7D/static/do_tasks?a=commitdiff_plain;h=e0681c1e366007ca7a662a855b16dd5e2f553da7;p=plomtask For various boolean settings, treat absence of form POST as setting False. --- diff --git a/plomtask/http.py b/plomtask/http.py index bb6e4ed..e224ea0 100644 --- a/plomtask/http.py +++ b/plomtask/http.py @@ -82,12 +82,9 @@ class InputsParser: msg = f'cannot int form field value for key {key}: {val}' raise BadFormatException(msg) from e - def get_bool_or_none(self, key: str) -> bool | None: - """Return value to key if truish; if no value to key, None.""" - val = self.get_str(key) - if val is None: - return None - return val in {'True', 'true', '1', 'on'} + def get_bool(self, key: str) -> bool: + """Return if value to key truish; return False if None/no value.""" + return self.get_str(key) in {'True', 'true', '1', 'on'} def get_all_of_key_prefixed(self, key_prefix: str) -> dict[str, list[str]]: """Retrieve dict of strings at keys starting with key_prefix.""" @@ -655,11 +652,9 @@ class TaskHandler(BaseHTTPRequestHandler): for id_ in self._form.get_all_int('make_empty')]} step_fillers_to = self._form.get_all_of_key_prefixed('step_filler_to_') to_update: dict[str, Any] = { - 'comment': self._form.get_str_or_fail('comment', '')} - for k in ('is_done', 'calendarize'): - v = self._form.get_bool_or_none(k) - if v is not None: - to_update[k] = v + 'comment': self._form.get_str_or_fail('comment', ''), + 'is_done': self._form.get_bool('is_done'), + 'calendarize': self._form.get_bool('calendarize')} cond_rels = [self._form.get_all_int(name) for name in ['conditions', 'blockers', 'enables', 'disables']] effort_or_not = self._form.get_str('effort') @@ -752,7 +747,7 @@ class TaskHandler(BaseHTTPRequestHandler): 'effort': self._form.get_float_or_fail('effort')} cond_rels = [self._form.get_all_int(s) for s in ['conditions', 'blockers', 'enables', 'disables']] - calendarize = self._form.get_bool_or_none('calendarize') + calendarize = self._form.get_bool('calendarize') step_of = self._form.get_all_str('step_of') suppressions = self._form.get_all_int('suppresses') kept_steps = self._form.get_all_int('kept_steps') @@ -766,8 +761,7 @@ class TaskHandler(BaseHTTPRequestHandler): # for k, v in versioned.items(): getattr(process, k).set(v) - if calendarize is not None: - process.calendarize = calendarize + process.calendarize = calendarize process.save(self._conn) assert isinstance(process.id_, int) # set relations to Conditions and ProcessSteps / other Processes @@ -807,10 +801,9 @@ class TaskHandler(BaseHTTPRequestHandler): """Update/insert Condition of ?id= and fields defined in postvars.""" title = self._form.get_str_or_fail('title') description = self._form.get_str_or_fail('description') - is_active = self._form.get_bool_or_none('is_active') + is_active = self._form.get_bool('is_active') + condition.is_active = is_active # - if is_active is not None: - condition.is_active = is_active condition.title.set(title) condition.description.set(description) condition.save(self._conn) diff --git a/tests/conditions.py b/tests/conditions.py index 3b64959..c071e65 100644 --- a/tests/conditions.py +++ b/tests/conditions.py @@ -100,6 +100,10 @@ class TestsWithServer(TestCaseWithServer): 'is_active': 1}) self.check_json_get(url_single, exp_single) self.check_json_get(url_all, exp_all) + # test POST sans 'is_active' setting it negative + self.post_exp_cond(all_exps, {'title': 'bar', 'description': 'oof'}) + self.check_json_get(url_single, exp_single) + self.check_json_get(url_all, exp_all) # test deletion POST's effect, both to return id=1 into empty single, # full /conditions into empty list self.post_exp_cond(all_exps, {'delete': ''}, redir_to_id=False) diff --git a/tests/misc.py b/tests/misc.py index 3aa1314..a6df2e5 100644 --- a/tests/misc.py +++ b/tests/misc.py @@ -98,36 +98,23 @@ class TestsSansServer(TestCase): with self.assertRaises(BadFormatException): InputsParser({'foo': []}).get_float_or_fail('foo') - def test_InputsParser_get_bool_or_none(self) -> None: - """Test InputsParser.get_all_str.""" - parser = InputsParser({}) - self.assertEqual(None, parser.get_bool_or_none('foo')) - parser = InputsParser({'val': ['foo']}) - self.assertEqual(None, parser.get_bool_or_none('foo')) - parser = InputsParser({'val': ['True']}) - self.assertEqual(None, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': []}) - self.assertEqual(None, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['None']}) - self.assertEqual(False, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['0']}) - self.assertEqual(False, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['']}) - self.assertEqual(False, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['bar']}) - self.assertEqual(False, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['bar', 'baz']}) - self.assertEqual(False, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['False']}) - self.assertEqual(False, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['true']}) - self.assertEqual(True, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['True']}) - self.assertEqual(True, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['1']}) - self.assertEqual(True, parser.get_bool_or_none('foo')) - parser = InputsParser({'foo': ['on']}) - self.assertEqual(True, parser.get_bool_or_none('foo')) + def test_InputsParser_get_bool(self) -> None: + """Test InputsParser.get_bool.""" + self.assertEqual(0, InputsParser({}).get_bool('foo')) + self.assertEqual(0, InputsParser({'val': ['foo']}).get_bool('foo')) + self.assertEqual(0, InputsParser({'val': ['True']}).get_bool('foo')) + self.assertEqual(0, InputsParser({'foo': []}).get_bool('foo')) + self.assertEqual(0, InputsParser({'foo': ['None']}).get_bool('foo')) + self.assertEqual(0, InputsParser({'foo': ['0']}).get_bool('foo')) + self.assertEqual(0, InputsParser({'foo': ['']}).get_bool('foo')) + self.assertEqual(0, InputsParser({'foo': ['bar']}).get_bool('foo')) + self.assertEqual(0, InputsParser({'foo': ['bar', + 'baz']}).get_bool('foo')) + self.assertEqual(0, InputsParser({'foo': ['False']}).get_bool('foo')) + self.assertEqual(1, InputsParser({'foo': ['true']}).get_bool('foo')) + self.assertEqual(1, InputsParser({'foo': ['True']}).get_bool('foo')) + self.assertEqual(1, InputsParser({'foo': ['1']}).get_bool('foo')) + self.assertEqual(1, InputsParser({'foo': ['on']}).get_bool('foo')) def test_InputsParser_get_all_str(self) -> None: """Test InputsParser.get_all_str.""" diff --git a/tests/processes.py b/tests/processes.py index 0c2c1a9..0fa352d 100644 --- a/tests/processes.py +++ b/tests/processes.py @@ -324,6 +324,11 @@ class TestsWithServer(TestCaseWithServer): valid_post = {'title': 'foo', 'description': 'oof', 'effort': 2.3} self.post_exp_process([exp], valid_post, 1) self.check_json_get('/process?id=1', exp) + # check boolean 'calendarize' + self.post_exp_process([exp], valid_post | {'calendarize': True}, 1) + self.check_json_get('/process?id=1', exp) + self.post_exp_process([exp], valid_post, 1) + self.check_json_get('/process?id=1', exp) # check n_todos field self.post_exp_day([], {'new_todo': ['1']}, '2024-01-01') self.post_exp_day([], {'new_todo': ['1']}, '2024-01-02') diff --git a/tests/todos.py b/tests/todos.py index 6118ab7..ea61a33 100644 --- a/tests/todos.py +++ b/tests/todos.py @@ -5,8 +5,7 @@ from tests.utils import (TestCaseSansDB, TestCaseWithDB, TestCaseWithServer, from plomtask.todos import Todo, TodoNode from plomtask.processes import Process, ProcessStep from plomtask.conditions import Condition -from plomtask.exceptions import (NotFoundException, BadFormatException, - HandledException) +from plomtask.exceptions import BadFormatException, HandledException class TestsWithDB(TestCaseWithDB, TestCaseSansDB): @@ -31,26 +30,6 @@ class TestsWithDB(TestCaseWithDB, TestCaseSansDB): self.cond2.save(self.db_conn) self.default_init_kwargs['process'] = self.proc - def test_Todo_init(self) -> None: - """Test creation of Todo and what they default to.""" - process = Process(None) - with self.assertRaises(NotFoundException): - Todo(None, process, False, self.date1) - process.save(self.db_conn) - assert isinstance(self.cond1.id_, int) - assert isinstance(self.cond2.id_, int) - process.set_condition_relations(self.db_conn, - [self.cond1.id_, self.cond2.id_], [], - [self.cond1.id_], [self.cond2.id_]) - todo_no_id = Todo(None, process, False, self.date1) - self.assertEqual(todo_no_id.conditions, [self.cond1, self.cond2]) - self.assertEqual(todo_no_id.enables, [self.cond1]) - self.assertEqual(todo_no_id.disables, [self.cond2]) - todo_yes_id = Todo(5, process, False, self.date1) - self.assertEqual(todo_yes_id.conditions, []) - self.assertEqual(todo_yes_id.enables, []) - self.assertEqual(todo_yes_id.disables, []) - def test_Todo_by_date(self) -> None: """Test findability of Todos by date.""" t1 = Todo(None, self.proc, False, self.date1) @@ -66,21 +45,6 @@ class TestsWithDB(TestCaseWithDB, TestCaseSansDB): """Test .by_date_range_with_limits.""" self.check_by_date_range_with_limits('day') - def test_Todo_on_conditions(self) -> None: - """Test effect of Todos on Conditions.""" - assert isinstance(self.cond1.id_, int) - assert isinstance(self.cond2.id_, int) - todo = Todo(None, self.proc, False, self.date1) - todo.save(self.db_conn) - todo.set_condition_relations(self.db_conn, [], [], - [self.cond1.id_], [self.cond2.id_]) - todo.is_done = True - self.assertEqual(self.cond1.is_active, True) - self.assertEqual(self.cond2.is_active, False) - todo.is_done = False - self.assertEqual(self.cond1.is_active, True) - self.assertEqual(self.cond2.is_active, False) - def test_Todo_children(self) -> None: """Test Todo.children relations.""" todo_1 = Todo(None, self.proc, False, self.date1) @@ -308,9 +272,8 @@ class TestsWithServer(TestCaseWithServer): 'comment': 'foo', 'effort': 2.3} self._post_exp_todo(1, todo_post, exp) self.check_json_get('/todo?id=1', exp) - # test implicitly un-setting (only) comment by empty post - self.check_post({}, '/todo?id=1') - exp.lib_get('Todo', 1)['comment'] = '' + # test implicitly un-setting comment/calendarize/is_done by empty post + self._post_exp_todo(1, {}, exp) self.check_json_get('/todo?id=1', exp) # test effort post can be explicitly unset by "effort":"" post self.check_post({'effort': ''}, '/todo?id=1') diff --git a/tests/utils.py b/tests/utils.py index e706ec0..045194f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -718,8 +718,8 @@ class Expected: return cond = self.lib_get('Condition', id_) if cond: - if 'is_active' in d: - cond['is_active'] = d['is_active'] + cond['is_active'] = 'is_active' in d and\ + d['is_active'] in VALID_TRUES for category in ['title', 'description']: history = cond['_versioned'][category] if len(history) > 0: @@ -766,7 +766,8 @@ class Expected: def set_todo_from_post(self, id_: int, d: dict[str, Any]) -> None: """Set Todo of id_ in library based on POST dict d.""" - corrected_kwargs: dict[str, Any] = {'children': []} + corrected_kwargs: dict[str, Any] = { + 'children': [], 'is_done': 0, 'calendarize': 0, 'comment': ''} for k, v in d.items(): if k.startswith('step_filler_to_'): continue @@ -774,8 +775,8 @@ class Expected: new_children = v if isinstance(v, list) else [v] corrected_kwargs['children'] += new_children continue - if k in {'is_done', 'calendarize'}: - v = v in VALID_TRUES + if k in {'is_done', 'calendarize'} and v in VALID_TRUES: + v = True corrected_kwargs[k] = v todo = self.lib_get('Todo', id_) if todo: @@ -846,12 +847,13 @@ class Expected: d['title'], d['description'], d['effort']) ignore = {'title', 'description', 'effort', 'new_top_step', 'step_of', 'kept_steps'} + proc['calendarize'] = False for k, v in d.items(): if k in ignore\ or k.startswith('step_') or k.startswith('new_step_to'): continue - if k in {'calendarize'}: - v = v in VALID_TRUES + if k in {'calendarize'} and v in VALID_TRUES: + v = True elif k in {'suppressed_steps', 'explicit_steps', 'conditions', 'disables', 'enables', 'blockers'}: if not isinstance(v, list):