diff --git a/c2corg_api/tests/views/test_route.py b/c2corg_api/tests/views/test_route.py index 5dcf66f62..1c8874aa3 100644 --- a/c2corg_api/tests/views/test_route.py +++ b/c2corg_api/tests/views/test_route.py @@ -116,16 +116,18 @@ def test_get(self): linked_waypoints = associations.get('waypoints') self.assertEqual(2, len(linked_waypoints)) self.assertEqual( - self.waypoint.document_id, linked_waypoints[0].get('document_id')) + self.waypoint.document_id, + linked_waypoints[0].get('document_id')) self.assertEqual( - self.waypoint3.document_id, linked_waypoints[1].get('document_id')) + self.waypoint_tc.document_id, + linked_waypoints[1].get('document_id')) # check waypoint data in listing self.assertEqual( self.waypoint.locales[0].access_period, linked_waypoints[0].get('locales')[0].get('access_period') ) self.assertEqual( - self.waypoint3.public_transportation_rating, + self.waypoint_tc.public_transportation_rating, linked_waypoints[1].get('public_transportation_rating') ) self.assertIn('geometry', linked_waypoints[0]) @@ -257,6 +259,41 @@ def test_post_empty_activities_and_associations_error(self): self.assertError( errors, 'associations.waypoints', 'at least one waypoint required') + def test_climing_indoor_waypoint_association(self): + body_post = { + 'main_waypoint_id': self.waypoint_climbing_indoor.document_id, + 'activities': ['hiking', 'skitouring'], + 'elevation_min': 700, + 'elevation_max': 1500, + 'height_diff_up': 800, + 'height_diff_down': 800, + 'durations': ['1'], + 'geometry': { + 'id': 5678, 'version': 6789, + 'geom_detail': + '{"type": "LineString", "coordinates": ' + + '[[635956, 5723604], [635966, 5723644]]}' + }, + 'locales': [ + {'lang': 'en', 'title': 'Some nice loop', + 'gear': 'shoes'} + ], + 'associations': { + 'waypoints': [ + {'document_id': self.waypoint_climbing_indoor.document_id} + ] + } + } + body = self.post_error(body_post) + errors = body.get('errors') + self.assertEqual(len(errors), 3) + self.assertEqual( + errors[0].get('description'), + 'climbing_indoor waypoint cannot be linked to a route') + self.assertEqual( + errors[0].get('name'), + 'associations.climbing_indoor_waypoint') + def test_post_invalid_activity(self): body_post = { 'activities': ['cooking'], @@ -685,7 +722,7 @@ def test_post_main_wp_without_association(self): {'lang': 'en', 'title': 'Some nice loop', 'gear': 'shoes'} ], - # no association for the main waypoint + # no association to the main waypoint 'associations': { 'waypoints': [ {'document_id': self.waypoint2.document_id} @@ -696,7 +733,7 @@ def test_post_main_wp_without_association(self): errors = body.get('errors') self.assertEqual(len(errors), 1) self.assertError( - errors, 'main_waypoint_id', 'no association for the main waypoint') + errors, 'main_waypoint_id', 'no association to the main waypoint') def test_put_wrong_document_id(self): body = { @@ -818,8 +855,10 @@ def test_put_success_all(self): '[[635956, 5723604], [635976, 5723654]]}' }, 'associations': { - 'waypoints': [{'document_id': self.waypoint.document_id}, - {'document_id': self.waypoint3.document_id}] + 'waypoints': [ + {'document_id': self.waypoint.document_id}, + {'document_id': self.waypoint_tc.document_id} + ] } } } @@ -872,8 +911,10 @@ def test_put_success_figures_only(self): 'title_prefix': 'Should be ignored'} ], 'associations': { - 'waypoints': [{'document_id': self.waypoint.document_id}, - {'document_id': self.waypoint3.document_id}] + 'waypoints': [ + {'document_id': self.waypoint.document_id}, + {'document_id': self.waypoint_tc.document_id} + ] } } } @@ -913,8 +954,10 @@ def test_put_success_new_track_with_default_geom(self): '{"type": "Point", "coordinates": [635000, 5723000]}' }, 'associations': { - 'waypoints': [{'document_id': self.waypoint.document_id}, - {'document_id': self.waypoint3.document_id}] + 'waypoints': [ + {'document_id': self.waypoint.document_id}, + {'document_id': self.waypoint_tc.document_id} + ] } } } @@ -944,7 +987,7 @@ def test_put_success_main_wp_changed(self): 'associations': { 'waypoints': [ {'document_id': self.waypoint2.document_id}, - {'document_id': self.waypoint3.document_id} + {'document_id': self.waypoint_tc.document_id} ] } } @@ -993,8 +1036,10 @@ def test_put_success_lang_only(self): 'version': self.locale_en.version} ], 'associations': { - 'waypoints': [{'document_id': self.waypoint.document_id}, - {'document_id': self.waypoint3.document_id}] + 'waypoints': [ + {'document_id': self.waypoint.document_id}, + {'document_id': self.waypoint_tc.document_id} + ] } } } @@ -1023,8 +1068,10 @@ def test_put_success_new_lang(self): 'description': '...', 'gear': 'si'} ], 'associations': { - 'waypoints': [{'document_id': self.waypoint.document_id}, - {'document_id': self.waypoint3.document_id}] + 'waypoints': [ + {'document_id': self.waypoint.document_id}, + {'document_id': self.waypoint_tc.document_id} + ] } } } @@ -1203,7 +1250,7 @@ def _add_test_data(self): gear='paraglider')) self.session.add(self.route4) - # add some associations + # waypoints self.waypoint = Waypoint( waypoint_type='climbing_outdoor', elevation=4, geometry=DocumentGeometry( @@ -1223,17 +1270,24 @@ def _add_test_data(self): lang='en', title='Mont Granier 2 (en)', description='...', access='yep')) self.session.add(self.waypoint2) - self.waypoint3 = Waypoint( + self.waypoint_tc = Waypoint( waypoint_type='access', elevation=1776, public_transportation_rating='poor service', geometry=DocumentGeometry( geom='SRID=3857;POINT(778846 5580167)')) - self.waypoint3.locales.append(WaypointLocale( + self.waypoint_tc.locales.append(WaypointLocale( lang='fr', title='Roche écroulée', description='...', access='yep', access_period='hiver' )) - self.session.add(self.waypoint3) + self.session.add(self.waypoint_tc) + self.waypoint_climbing_indoor = Waypoint( + waypoint_type='climbing_indoor', elevation=1, + geometry=DocumentGeometry( + geom='SRID=3857;POINT(635956 5723604)')) + self.session.add(self.waypoint_climbing_indoor) self.session.flush() + + # associations self._add_association(Association.create( parent_document=self.route, child_document=self.route4), user_id) @@ -1244,7 +1298,7 @@ def _add_test_data(self): parent_document=self.waypoint, child_document=self.route), user_id) self._add_association(Association.create( - parent_document=self.waypoint3, + parent_document=self.waypoint_tc, child_document=self.route), user_id) # add a map diff --git a/c2corg_api/views/route.py b/c2corg_api/views/route.py index 6ef069d16..351a01a1a 100644 --- a/c2corg_api/views/route.py +++ b/c2corg_api/views/route.py @@ -63,7 +63,7 @@ def validate_main_waypoint(is_on_create, request, **kwargs): # no association found request.errors.add( - 'body', 'main_waypoint_id', 'no association for the main waypoint') + 'body', 'main_waypoint_id', 'no association to the main waypoint') def validate_required_associations(request, **kwargs): diff --git a/c2corg_api/views/validation.py b/c2corg_api/views/validation.py index c0fd3ef87..65a8e31c9 100644 --- a/c2corg_api/views/validation.py +++ b/c2corg_api/views/validation.py @@ -14,7 +14,7 @@ from c2corg_api.models.xreport import XREPORT_TYPE from c2corg_api.models.route import ROUTE_TYPE from c2corg_api.models.user_profile import USERPROFILE_TYPE -from c2corg_api.models.waypoint import WAYPOINT_TYPE +from c2corg_api.models.waypoint import WAYPOINT_TYPE, Waypoint from c2corg_api.views.document_associations import get_first_column from c2corg_api.models.common.associations import valid_associations @@ -604,6 +604,22 @@ def _get_linked_document_ids(associations): ]) +def _is_any_climbing_indoor_waypoint(associations_in): + """ Check if there is any climbing indoor waypoints in the linked waypoints + """ + waypoints_id = [doc['document_id'] for doc in associations_in['waypoints']] + + # load the waypoint type for each linked waypoint + if waypoints_id: + query_waypoints_with_type = DBSession. \ + query(Waypoint.waypoint_type). \ + filter(Waypoint.document_id.in_(waypoints_id)) + for waypoint in query_waypoints_with_type.all(): + if waypoint.waypoint_type == 'climbing_indoor': + return True + return False + + def _add_associations( associations, associations_in, main_document_type, document_key, other_document_type, errors): @@ -624,18 +640,26 @@ def _add_associations( errors.add( 'body', 'associations.' + document_key, 'invalid association type') - else: - if document_key == 'waypoints' and main_document_type != BOOK_TYPE: - is_parent = True - elif document_key == 'waypoint_children': - is_parent = False - - associations[document_key] = [ - { - 'document_id': doc['document_id'], - 'is_parent': is_parent - } for doc in associations_in[document_key] - ] + return + + if document_key == 'waypoints' and main_document_type == ROUTE_TYPE: + if _is_any_climbing_indoor_waypoint(associations_in): + errors.add( + 'body', + 'associations.climbing_indoor_waypoint', + 'climbing_indoor waypoint cannot be linked to a route') + return + elif document_key == 'waypoints' and main_document_type != BOOK_TYPE: + is_parent = True + elif document_key == 'waypoint_children': + is_parent = False + + associations[document_key] = [ + { + 'document_id': doc['document_id'], + 'is_parent': is_parent + } for doc in associations_in[document_key] + ] def _is_parent_of_association(main_document_type, other_document_type):