scss_view = do( require_http_method("GET"), string_to_response("text/css"), compile_scss) ##################################### # mapfeature ##################################### map_feature_add_view = do( instance_request, route( GET=render_map_feature_add, POST=do( login_or_401, json_api_call, creates_instance_user, add_map_feature))) map_feature_detail_view = do( instance_request, route( GET=render_map_feature_detail, ELSE=do( login_or_401, json_api_call, creates_instance_user, route( PUT=update_map_feature_detail, DELETE=delete_map_feature))))
landing_view = render_template("base.html") add_tree_photo_endpoint = require_http_method("POST")( login_or_401( instance_request( creates_instance_user( render_template("treemap/partials/tree_carousel.html", add_tree_photo_view))))) scss_view = require_http_method("GET")( string_as_file_call("text/css", compile_scss)) photo_review_endpoint = instance_request( route( GET=render_template("treemap/photo_review.html", photo_review))) photo_review_partial_endpoint = instance_request( route( GET=render_template("treemap/partials/photo_review.html", photo_review))) next_photo_endpoint = instance_request( route( GET=render_template("treemap/partials/photo.html", next_photo))) approve_or_reject_photo_view = login_required( instance_request( creates_instance_user(approve_or_reject_photo)))
user_audits_view = render_template("treemap/recent_user_edits.html", user_audits) instance_not_available_view = render_template("treemap/instance_not_available.html") unsupported_view = render_template("treemap/unsupported.html") landing_view = render_template("base.html") add_tree_photo_endpoint = require_http_method("POST")( login_or_401(json_api_call(instance_request(creates_instance_user(add_tree_photo_view)))) ) scss_view = require_http_method("GET")(string_as_file_call("text/css", compile_scss)) photo_review_endpoint = instance_request(route(GET=render_template("treemap/photo_review.html", photo_review))) photo_review_partial_endpoint = instance_request( route(GET=render_template("treemap/partials/photo_review.html", photo_review)) ) next_photo_endpoint = instance_request(route(GET=render_template("treemap/partials/photo.html", next_photo))) approve_or_reject_photo_view = login_required(instance_request(creates_instance_user(approve_or_reject_photo))) static_page_view = instance_request(render_template("treemap/staticpage.html", static_page)) error_404_view = render_template("404.html", statuscode=404) error_500_view = render_template("500.html", statuscode=500) error_503_view = render_template("503.html", statuscode=503)
remove_current_tree_from_plot, add_tree_photo, get_tree_image, plots_closest_to_point, approve_pending_edit, reject_pending_edit, species_list, geocode_address, reset_password, verify_auth, register, add_profile_photo, update_password, edits) from treemap.instance import URL_NAME_PATTERN instance_pattern = r'^(?P<instance_url_name>' + URL_NAME_PATTERN + r')' urlpatterns = patterns( '', (r'^$', status), (r'^version$', version), (instance_pattern + r'/plots$', route(GET=get_plot_list, POST=create_plot_optional_tree)), (instance_pattern + r'/plots/(?P<plot_id>\d+)$', route(GET=get_plot, PUT=update_plot_and_tree, DELETE=remove_plot)), (instance_pattern + r'/plots/(?P<plot_id>\d+)/tree$', route(GET=get_current_tree_from_plot, DELETE=remove_current_tree_from_plot)), (r'^plots/(?P<plot_id>\d+)/tree/photo$', route(POST=add_tree_photo)), (r'^plots/(?P<plot_id>\d+)/tree/photo/(?P<photo_id>\d+)', get_tree_image), (r'^locations/' '(?P<lat>-{0,1}\d+(\.\d+){0,1}),(?P<lon>-{0,1}\d+(\.\d+){0,1})' '/plots', plots_closest_to_point), (instance_pattern + r'/pending-edits/(?P<pending_edit_id>\d+)/approve',
# For "top level" URLs defined here, see treemap/tests/urls.py (RootUrlTests) urlpatterns = patterns( '', (r'^robots.txt$', RedirectView.as_view( url='/static/robots.txt', permanent=True)), # Setting permanent=False in case we want to allow customizing favicons # per instance in the future (r'^favicon\.png$', RedirectView.as_view( url='/static/img/favicon.png', permanent=False)), url('^comments/', include('django.contrib.comments.urls')), url(r'^', include('geocode.urls')), url(r'^$', landing_view), url(r'^config/settings.js$', root_settings_js_view), url(r'^users/%s/$' % USERNAME_PATTERN, route(GET=user_view, PUT=update_user_view), name='user'), url(r'^users/%s/edits/$' % USERNAME_PATTERN, user_audits_view, name='user_audits'), url(r'^users/%s/photo/$' % USERNAME_PATTERN, upload_user_photo_view, name='user_photo'), url(r'^api/v(?P<version>\d+)/', include('api.urls')), # The profile view is handled specially by redirecting to # the page of the currently logged in user url(r'^accounts/profile/$', profile_to_user_view, name='profile'), url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}), url(r'^accounts/forgot-username/$', forgot_username_view, name='forgot_username'), url(r'^accounts/', include('registration_backend.urls')), # Create a redirect view for setting the session language preference # https://docs.djangoproject.com/en/1.0/topics/i18n/#the-set-language-redirect-view # NOQA
(r'^$', status_view), (r'^version$', version_view), (r'^user$', user_endpoint), url(r'^user/(?P<user_id>\d+)$', update_user_endpoint, name='update_user'), url(r'^user/(?P<user_id>\d+)/photo$', update_profile_photo_endpoint, name='update_user_photo'), (r'^user/(?P<user_id>\d+)/reset_password$', reset_password), (r'^user/(?P<user_id>\d+)/edits$', edits), (instance_pattern + r'/plots/(?P<plot_id>\d+)/tree/photo$', add_photo_endpoint), (instance_pattern + r'/plots/(?P<plot_id>\d+)/tree$', route(DELETE=remove_current_tree_from_plot)), (instance_pattern + r'/pending-edits/(?P<pending_edit_id>\d+)/approve', route(POST=approve_pending_edit)), (instance_pattern + r'/pending-edits/(?P<pending_edit_id>\d+)/reject', route(POST=reject_pending_edit)), # OTM2/instance endpoints (instance_pattern + '$', instance_info_endpoint), (instance_pattern + '/species$', species_list_endpoint), (instance_pattern + r'/plots$', plots_endpoint), (instance_pattern + r'/plots/(?P<plot_id>\d+)$', plot_endpoint), (instance_pattern + r'/locations/' '(?P<lat>-{0,1}\d+(\.\d+){0,1}),(?P<lng>-{0,1}\d+(\.\d+){0,1})'
urlpatterns = patterns( "", url(r"^$", index_view), url(r"page/(?P<page>[a-zA-Z0-9 ]+)/$", static_page_view, name="static_page"), url(r"^boundaries/(?P<boundary_id>\d+)/geojson/$", boundary_to_geojson_view), url(r"^boundaries/$", boundary_autocomplete_view), url(r"^edits/$", edits_view, name="edits"), url(r"^photo_review/$", photo_review_endpoint), url(r"^photo_review/next$", next_photo_endpoint), url(r"^photo_review/partial$", photo_review_partial_endpoint), url(r"^species/$", species_list_view), url(r"^map/$", map_view, name="map"), url( r"^features/(?P<feature_id>\d+)/$", route(GET=get_map_feature_detail_view, PUT=update_map_feature_detail_view, DELETE=delete_map_feature_view), name="map_feature_detail", ), url( r"^features/(?P<type>\w+)/$", route(GET=get_map_feature_add_view, POST=add_map_feature_view), name="add_map_feature", ), url(r"^features/(?P<feature_id>\d+)/(?P<edit>edit)$", edit_plot_detail_view, name="map_feature_detail_edit"), url(r"^features/(?P<feature_id>\d+)/popup$", map_feature_popup_view, name="map_feature_popup"), url(r"^features/(?P<feature_id>\d+)/trees/(?P<tree_id>\d+)/$", route(DELETE=delete_tree_view)), url(r"^features/(?P<feature_id>\d+)/sidebar$", get_map_feature_sidebar_view, name="map_feature_sidebar"), url(r"^plots/$", route(POST=add_map_feature_view), name="add_plot"), url(r"^plots/(?P<feature_id>\d+)/detail$", plot_accordion_view, name="plot_accordion"), url(r"^plots/(?P<feature_id>\d+)/eco$", get_plot_eco_view, name="plot_eco"), url(r"^plots/(?P<feature_id>\d+)/trees/(?P<tree_id>\d+)/eco$", get_plot_eco_view, name="tree_eco"),
instances_closest_to_point_endpoint = check_signature( instance_request( json_api_call( instances_closest_to_point))) instance_info_endpoint = check_signature( instance_request( json_api_call( instance_info))) plots_endpoint = check_signature( instance_request( json_api_call( route( POST=login_required( update_or_create_plot), GET=get_plot_list)))) plot_endpoint = check_signature( instance_request( json_api_call( route( GET=get_plot, PUT=login_required(update_or_create_plot), DELETE=login_required(remove_plot))))) species_list_endpoint = check_signature( json_api_call( route(GET=species_list))) user_endpoint = check_signature(
species_list) scss_view = do( require_http_method("GET"), string_to_response("text/css"), compile_scss) ##################################### # mapfeature ##################################### map_feature_add_view = do( instance_request, route( GET=render_map_feature_add, POST=do( json_api_edit, add_map_feature))) map_feature_detail_view = do( instance_request, route( GET=render_map_feature_detail, ELSE=do( json_api_edit, route( PUT=update_map_feature_detail, DELETE=delete_map_feature)))) map_feature_accordion_view = do( instance_request,
boundary_autocomplete_view = do(json_api_call, instance_request, boundary_autocomplete) species_list_view = do(json_api_call, instance_request, species_list) scss_view = do(require_http_method("GET"), string_to_response("text/css"), compile_scss) ##################################### # mapfeature ##################################### map_feature_add_view = do( instance_request, route(GET=render_map_feature_add, POST=do(login_or_401, json_api_call, creates_instance_user, add_map_feature))) map_feature_detail_view = do( instance_request, route(GET=render_map_feature_detail, ELSE=do( login_or_401, json_api_call, creates_instance_user, route(PUT=update_map_feature_detail, DELETE=delete_map_feature)))) map_feature_accordion_view = do( instance_request, render_template('treemap/partials/map_feature_accordion.html'), map_feature_detail)
# For "top level" URLs defined here, see treemap/tests/urls.py (RootUrlTests) urlpatterns = patterns( '', (r'^robots.txt$', RedirectView.as_view(url='/static/robots.txt', permanent=True)), # Setting permanent=False in case we want to allow customizing favicons # per instance in the future (r'^favicon\.png$', RedirectView.as_view(url='/static/img/favicon.png', permanent=False)), url('^comments/', include('django.contrib.comments.urls')), url(r'^', include('geocode.urls')), url(r'^$', landing_view), url(r'^config/settings.js$', root_settings_js_view), url(r'^users/%s/$' % USERNAME_PATTERN, route(GET=user_view, PUT=update_user_view), name='user'), url(r'^users/%s/edits/$' % USERNAME_PATTERN, user_audits_view, name='user_audits'), url(r'^users/%s/photo/$' % USERNAME_PATTERN, upload_user_photo_view, name='user_photo'), url(r'^api/v(?P<version>\d+)/', include('api.urls')), # The profile view is handled specially by redirecting to # the page of the currently logged in user url(r'^accounts/profile/$', profile_to_user_view, name='profile'), url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}), url(r'^accounts/forgot-username/$', forgot_username_view,
instance_pattern = r"^instance/(?P<instance_url_name>" + URL_NAME_PATTERN + r")" lat_lon_pattern = "(?P<lat>-{0,1}\d+(\.\d+){0,1})," "(?P<lng>-{0,1}\d+(\.\d+){0,1})" urlpatterns = patterns( "", (r"^$", status_view), (r"^version$", version_view), url(r"^user$", user_endpoint, name="user_info"), url(r"^user/(?P<user_id>\d+)$", update_user_endpoint, name="update_user"), url(r"^user/(?P<user_id>\d+)/photo$", update_profile_photo_endpoint, name="update_user_photo"), (r"^user/(?P<user_id>\d+)/edits$", edits), (r"^send-password-reset-email$", reset_password_endpoint), ("^locations/" + lat_lon_pattern + "/instances", instances_closest_to_point_endpoint), ("^instances", public_instances_endpoint), (instance_pattern + r"/plots/(?P<plot_id>\d+)/tree/photo$", add_photo_endpoint), (instance_pattern + r"/plots/(?P<plot_id>\d+)/tree$", route(DELETE=remove_current_tree_from_plot)), (instance_pattern + r"/pending-edits/(?P<pending_edit_id>\d+)/approve", route(POST=approve_pending_edit)), (instance_pattern + r"/pending-edits/(?P<pending_edit_id>\d+)/reject", route(POST=reject_pending_edit)), # OTM2/instance endpoints (instance_pattern + "$", instance_info_endpoint), (instance_pattern + "/species$", species_list_endpoint), (instance_pattern + r"/plots$", plots_endpoint), (instance_pattern + r"/plots/(?P<plot_id>\d+)$", plot_endpoint), (instance_pattern + r"/locations/" + lat_lon_pattern + "/plots", plots_closest_to_point_endpoint), url(instance_pattern + r"/users.csv", export_users_csv_endpoint, name="user_csv"), url(instance_pattern + r"/users.json", export_users_json_endpoint, name="user_json"), )
register, add_profile_photo, update_password, plot_endpoint, edits, plots_closest_to_point_endpoint, instance_info_endpoint) from treemap.instance import URL_NAME_PATTERN instance_pattern = r'^(?P<instance_url_name>' + URL_NAME_PATTERN + r')' urlpatterns = patterns( '', (r'^$', status), (r'^version$', version), (instance_pattern + r'/plots/(?P<plot_id>\d+)/tree$', route(DELETE=remove_current_tree_from_plot)), (r'^plots/(?P<plot_id>\d+)/tree/photo$', route(POST=add_tree_photo)), (r'^plots/(?P<plot_id>\d+)/tree/photo/(?P<photo_id>\d+)', get_tree_image), (instance_pattern + r'/pending-edits/(?P<pending_edit_id>\d+)/approve', route(POST=approve_pending_edit)), (instance_pattern + r'/pending-edits/(?P<pending_edit_id>\d+)/reject', route(POST=reject_pending_edit)), (r'^addresses/(?P<address>.+)', geocode_address), (r'^login/reset_password$', reset_password), (r'^login$', login_endpoint),
urlpatterns = patterns( '', url(r'^$', index_view), url(r'page/(?P<page>[a-zA-Z0-9]+)/$', static_page_view, name='static_page'), url(r'^boundaries/(?P<boundary_id>\d+)/geojson/$', boundary_to_geojson_view), url(r'^boundaries/$', boundary_autocomplete_view), url(r'^edits/$', edits_view, name='edits'), url(r'^photo_review/$', photo_review_endpoint), url(r'^photo_review/next$', next_photo_endpoint), url(r'^photo_review/partial$', photo_review_partial_endpoint), url(r'^species/$', species_list_view), url(r'^map/$', map_view, name='map'), url(r'^plots/(?P<plot_id>\d+)/$', route(GET=get_plot_detail_view, PUT=update_plot_detail_view), name='plot_detail'), url(r'^plots/(?P<plot_id>\d+)/eco$', get_plot_eco_view), url(r'^plots/(?P<plot_id>\d+)/sidebar$', get_plot_sidebar_view), url(r'^plots/(?P<plot_id>\d+)/trees/(?P<tree_id>\d+)/eco$', get_plot_eco_view), url(r'^plots/(?P<plot_id>\d+)/(?P<edit>edit)$', edit_plot_detail_view, name='plot_detail_edit'), url(r'^plots/(?P<plot_id>\d+)/trees/(?P<tree_id>\d+)/$', route(GET=get_plot_detail_view, PUT=update_plot_detail_view), name='tree_detail'), url(r'^plots/(?P<plot_id>\d+)/detail$', plot_accordion_view, name='plot_accordion'), url(r'^plots/(?P<plot_id>\d+)/popup$', plot_popup_view),
url('^features/(?P<feature_id>\d+)/photo/(?P<photo_id>\d+)/' '(?P<action>(approve)|(reject))$', approve_or_reject_photo_view, name='approve_or_reject_photo'), url(r'^species/$', species_list_view), url(r'^map/$', map_view, name='map'), url(r'^features/(?P<feature_id>\d+)/$', map_feature_detail_view, name='map_feature_detail'), url(r'^features/(?P<type>\w+)/$', map_feature_add_view, name='add_map_feature'), url(r'^features/(?P<feature_id>\d+)/(?P<edit>edit)$', edit_plot_detail_view, name='map_feature_detail_edit'), url(r'^features/(?P<feature_id>\d+)/popup$', map_feature_popup_view, name='map_feature_popup'), url(r'^features/(?P<feature_id>\d+)/trees/(?P<tree_id>\d+)/$', route(DELETE=delete_tree_view)), url(r'^features/(?P<feature_id>\d+)/sidebar$', get_map_feature_sidebar_view, name='map_feature_sidebar'), url(r'^features/(?P<feature_id>\d+)/photo$', add_map_feature_photo_endpoint, name='add_photo_to_map_feature'), url(r'^features/(?P<feature_id>\d+)/detail$', map_feature_accordion_view, name='map_feature_accordion'), url(r'^plots/$', route(POST=map_feature_add_view), name='add_plot'), url(r'^plots/(?P<feature_id>\d+)/eco$', get_plot_eco_view, name='plot_eco'), url(r'^plots/(?P<feature_id>\d+)/trees/(?P<tree_id>\d+)/eco$', get_plot_eco_view, name='tree_eco'), url(r'^plots/(?P<feature_id>\d+)/trees/(?P<tree_id>\d+)/$', tree_detail_view, name='tree_detail'),
# Note that API requests going to private instances require # authentication "login_optional" before they can access they # instance data instance_api_do = partial(do, check_signature, instance_request, json_api_call) plots_closest_to_point_endpoint = instance_api_do(plots_closest_to_point) instances_closest_to_point_endpoint = instance_api_do( instances_closest_to_point) instance_info_endpoint = instance_api_do(instance_info) plots_endpoint = instance_api_do( route(GET=get_plot_list, POST=do(login_required, update_or_create_plot))) plot_endpoint = instance_api_do( route(GET=get_plot, ELSE=do(login_required, route(PUT=update_or_create_plot, DELETE=remove_plot)))) species_list_endpoint = instance_api_do(route(GET=species_list)) user_endpoint = do( check_signature, json_api_call, route(GET=do(login_required, user_info), POST=do(return_400_if_validation_errors, create_user))) update_user_endpoint = do(check_signature_and_require_login, json_api_call, return_400_if_validation_errors,
csrf_exempt(json_api_call( instances_closest_to_point)))) instance_info_endpoint = login_optional( instance_request( csrf_exempt(json_api_call( instance_info)))) login_endpoint = csrf_exempt( json_api_call(login_required(user_info))) plots_endpoint = json_api_call( route( POST=login_required( instance_request( update_or_create_plot)), GET=login_optional( instance_request( get_plot_list)))) plot_endpoint = json_api_call( route( GET=login_optional( instance_request(get_plot)), PUT=login_required( instance_request(update_or_create_plot)), DELETE=login_required( instance_request(remove_plot)))) species_list_endpoint = instance_request( json_api_call(
logged_in_api_do = partial(do, csrf_exempt, set_api_version, check_signature_and_require_login, json_api_call) plots_closest_to_point_endpoint = instance_api_do(plots_closest_to_point) instances_closest_to_point_endpoint = api_do( instances_closest_to_point) public_instances_endpoint = api_do(public_instances) instance_info_endpoint = instance_api_do(instance_info) plots_endpoint = instance_api_do( route(GET=get_plot_list, POST=do( login_required, update_or_create_plot))) plot_endpoint = instance_api_do( route(GET=get_plot, ELSE=do(login_required, route( PUT=update_or_create_plot, DELETE=remove_plot)))) species_list_endpoint = instance_api_do( route(GET=species_list)) user_endpoint = api_do( route(
urlpatterns = patterns( '', url(r'^$', index_view), url(r'page/(?P<page>[a-zA-Z0-9]+)/$', static_page_view, name='static_page'), url(r'^boundaries/(?P<boundary_id>\d+)/geojson/$', boundary_to_geojson_view), url(r'^boundaries/$', boundary_autocomplete_view), url(r'^edits/$', edits_view, name='edits'), url(r'^photo_review/$', photo_review_endpoint), url(r'^photo_review/next$', next_photo_endpoint), url(r'^photo_review/partial$', photo_review_partial_endpoint), url(r'^species/$', species_list_view), url(r'^map/$', map_view, name='map'), url(r'^plots/(?P<plot_id>\d+)/$', route(GET=get_plot_detail_view, PUT=update_plot_detail_view, DELETE=delete_plot_view), name='plot_detail'), url(r'^plots/(?P<plot_id>\d+)/eco$', get_plot_eco_view), url(r'^plots/(?P<plot_id>\d+)/sidebar$', get_plot_sidebar_view), url(r'^plots/(?P<plot_id>\d+)/trees/(?P<tree_id>\d+)/eco$', get_plot_eco_view), url(r'^plots/(?P<plot_id>\d+)/(?P<edit>edit)$', edit_plot_detail_view, name='plot_detail_edit'), url(r'^plots/(?P<plot_id>\d+)/trees/(?P<tree_id>\d+)/$', route(GET=get_plot_detail_view, PUT=update_plot_detail_view, DELETE=delete_tree_view), name='tree_detail'), url(r'^plots/(?P<plot_id>\d+)/detail$', plot_accordion_view, name='plot_accordion'),
name='update_user'), url(r'^user/(?P<user_id>\d+)/photo$', update_profile_photo_endpoint, name='update_user_photo'), (r'^user/(?P<user_id>\d+)/reset_password$', reset_password), (r'^user/(?P<user_id>\d+)/edits$', edits), ('^locations/' + lat_lon_pattern + '/instances', instances_closest_to_point_endpoint), ('^instances', public_instances_endpoint), (instance_pattern + r'/plots/(?P<plot_id>\d+)/tree/photo$', add_photo_endpoint), (instance_pattern + r'/plots/(?P<plot_id>\d+)/tree$', route(DELETE=remove_current_tree_from_plot)), (instance_pattern + r'/pending-edits/(?P<pending_edit_id>\d+)/approve', route(POST=approve_pending_edit)), (instance_pattern + r'/pending-edits/(?P<pending_edit_id>\d+)/reject', route(POST=reject_pending_edit)), # OTM2/instance endpoints (instance_pattern + '$', instance_info_endpoint), (instance_pattern + '/species$', species_list_endpoint), (instance_pattern + r'/plots$', plots_endpoint), (instance_pattern + r'/plots/(?P<plot_id>\d+)$', plot_endpoint), (instance_pattern + r'/locations/' + lat_lon_pattern + '/plots', plots_closest_to_point_endpoint),
api_do = partial(do, csrf_exempt, check_signature, set_api_version, json_api_call) logged_in_api_do = partial(do, csrf_exempt, set_api_version, check_signature_and_require_login, json_api_call) plots_closest_to_point_endpoint = instance_api_do(plots_closest_to_point) instances_closest_to_point_endpoint = api_do( instances_closest_to_point) instance_info_endpoint = instance_api_do(instance_info) plots_endpoint = instance_api_do( route(GET=get_plot_list, POST=do( login_required, update_or_create_plot))) plot_endpoint = instance_api_do( route(GET=get_plot, ELSE=do(login_required, route( PUT=update_or_create_plot, DELETE=remove_plot)))) species_list_endpoint = instance_api_do( route(GET=species_list)) user_endpoint = api_do( route(
url(r'^species/$', species_list_view), url(r'^map/$', map_view, name='map'), url(r'^features/(?P<feature_id>\d+)/$', map_feature_detail_view, name='map_feature_detail'), url(r'^features/(?P<type>\w+)/$', map_feature_add_view, name='add_map_feature'), url(r'^features/(?P<feature_id>\d+)/(?P<edit>edit)$', edit_plot_detail_view, name='map_feature_detail_edit'), url(r'^features/(?P<feature_id>\d+)/popup$', map_feature_popup_view, name='map_feature_popup'), url(r'^features/(?P<feature_id>\d+)/trees/(?P<tree_id>\d+)/$', route(DELETE=delete_tree_view)), url(r'^features/(?P<feature_id>\d+)/sidebar$', get_map_feature_sidebar_view, name='map_feature_sidebar'), url(r'^features/(?P<feature_id>\d+)/photo$', add_map_feature_photo_endpoint, name='add_photo_to_map_feature'), url(r'^features/(?P<feature_id>\d+)/detail$', map_feature_accordion_view, name='map_feature_accordion'), url('^features/(?P<feature_id>\d+)/photo/(?P<photo_id>\d+)$', rotate_map_feature_photo_endpoint, name='rotate_photo'), url(r'^plots/$', route(POST=map_feature_add_view), name='add_plot'), url(r'^plots/(?P<feature_id>\d+)/eco$', get_plot_eco_view, name='plot_eco'),