Skip to content

Commit

Permalink
controllers/tour: Reset unspecified fields to DB default #776
Browse files Browse the repository at this point in the history
If a value is unspecified, update_or_insert() will leave alone on
update. We want missing values to go back to their default, as they
would have done if they weren't specified in the first place.

Add the database defaults at the start of merging the tourstop dict, so
this happens. Add some missing defaults for non-null fields so we merge
the right values.
  • Loading branch information
lentinj committed Apr 23, 2024
1 parent 845dd20 commit 633be82
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 3 deletions.
2 changes: 2 additions & 0 deletions controllers/tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ def data():
tss_targets = {}
for i, ts in enumerate(request.vars['tourstops']):
ts = {
# All fields not otherwise specfied return to their default
**{k:db.tourstop.get(k).default for k in db.tourstop.fields if k not in ['created', 'updated']},
**ts_shared,
**ts,
# Deep-clone template_data, should always exist
Expand Down
6 changes: 3 additions & 3 deletions models/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,18 +772,18 @@
Field('identifier', type='string', notnull=True), # Unique identifier for tourstop, for establishing symlinks
Field('ord', type='integer', notnull=True), # The position of this tourstop in the tour, starting with 1
Field('ott', type='integer', notnull=False), # The OTT this tourstop points at. NULL => return to start
Field('secondary_ott', type='integer', notnull=True), # A second OTT when targeting a common ancestor
Field('secondary_ott', type='integer', notnull=True, default=0), # A second OTT when targeting a common ancestor
Field('qs_opts', type='string', notnull=False, default=''), # QS-style options to apply to modify tourstop, e.g. into_node=true&initmark=...
Field('author', type='text', notnull=True, default=''), # Author of tourstop (doesn't necessarily match tour in case of remix)
Field('transition_in', type='string', notnull=True, requires=IS_IN_SET(('fly', 'leap', 'fly_straight'))), # Transition to use when flying to stop
Field('transition_in', type='string', notnull=True, requires=IS_IN_SET(('fly', 'leap', 'fly_straight')), default='fly'), # Transition to use when flying to stop
Field('fly_in_speed', type='double', notnull=False, default=1), # Speed relative to global_anim_speed
Field('transition_in_wait', type='integer', notnull=False), # How long to wait before entering into transition
Field('stop_wait', type='integer', notnull=False), # How long to wait at this stop (null => wait until "next" is pressed)
Field('stop_wait_after_backward', type='integer', notnull=False), # How long to wait if entered by going back
Field('template_data', type='text', notnull=True,
filter_in=lambda obj: json.dumps(obj),
filter_out=lambda txt: json.loads(txt)), # JSON template data for tourstop
Field('lang', type='string', notnull=True, length=3), #the 'primary' 2 or 3 letter 'lang' code for this name (e.g. 'en', 'cmn'). See http://www.w3.org/International/articles/language-tags/
Field('lang', type='string', notnull=True, length=3, default='en'), #the 'primary' 2 or 3 letter 'lang' code for this name (e.g. 'en', 'cmn'). See http://www.w3.org/International/articles/language-tags/
Field('checked_by', type='string'), # Who has checked the validity of this data?
Field('checked_updated', 'datetime', default=None), # When did they do it?
Field('visible_in_search', 'boolean', notnull=True, default=True), # Available in tourstop-search for remixing a tour?
Expand Down
65 changes: 65 additions & 0 deletions tests/unit/test_controllers_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,71 @@ def test_data_shareddata(self):
]
)

def test_data_resettodefault(self):
"""Removing values will reset them to their default"""
otts = util.find_unsponsored_otts(10)

# Can insert a tour with a common ancestor pinpoint
t = self.tour_put('UT::TOUR', dict(
title="A unit test tour",
description="It's a nice tour",
author="UT::Author",
tourstop_shared=dict(
stop_wait=1234,
),
tourstops=[
dict(
ott=otts[0],
identifier="ott0",
stop_wait=3,
author="Frank",
),
dict(
ott=otts[1],
identifier="ott1",
author="Gelda",
),
],
))
# One entry uses default
self.assertEqual(
[ts['stop_wait'] for ts in self.tour_get('UT::TOUR')['tourstops']],
[3, 1234],
)
# Both have an Author
self.assertEqual(
[ts['author'] for ts in self.tour_get('UT::TOUR')['tourstops']],
['Frank', 'Gelda'],
)

# Clear author, stop_wait. Get set back to their DB defaults
t = self.tour_put('UT::TOUR', dict(
title="A unit test tour",
description="It's a nice tour",
author="UT::Author",
tourstop_shared=dict(
),
tourstops=[
dict(
ott=otts[0],
identifier="ott0",
stop_wait=3,
),
dict(
ott=otts[1],
identifier="ott1",
),
],
))
self.assertEqual(
[ts['stop_wait'] for ts in self.tour_get('UT::TOUR')['tourstops']],
[3, None],
)
self.assertEqual(
[ts['author'] for ts in self.tour_get('UT::TOUR')['tourstops']],
['', ''],
)

def test_list(self):
def t_list(tours, include_rest=""):
return util.call_controller(
Expand Down

0 comments on commit 633be82

Please sign in to comment.