Skip to content

Commit

Permalink
HB2-87 implement rules in templates
Browse files Browse the repository at this point in the history
  • Loading branch information
Cedric Hofstetter committed Nov 11, 2020
1 parent 996542f commit 8aac04b
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 263 deletions.
6 changes: 4 additions & 2 deletions homebytwo/importers/views.py
Expand Up @@ -5,6 +5,8 @@
from django.urls import NoReverseMatch
from django.views.decorators.http import require_POST

from rules.contrib.views import permission_required

from ..routes.forms import RouteForm
from .decorators import remote_connection
from .forms import GpxUploadForm, SwitzerlandMobilityLogin
Expand Down Expand Up @@ -55,13 +57,13 @@ def import_routes(request, data_source):

@login_required
@remote_connection
@permission_required("routes.import_route")
def import_route(request, data_source, source_id):
"""
import routes from external sources
There is a modelform for the route with custom __init__ and save methods
to find available checkpoints and save the ones selected by the athlete
to the route.
to find available checkpoints and save the ones selected by the athlete.
"""

template = "routes/route/route_form.html"
Expand Down
13 changes: 9 additions & 4 deletions homebytwo/routes/models/route.py
@@ -1,21 +1,21 @@
import rules

from collections import deque
from datetime import datetime, timedelta
from tempfile import NamedTemporaryFile
from uuid import uuid4

from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.contrib.gis.db import models
from django.urls import reverse

import gpxpy
import gpxpy.gpx
import rules
from garmin_uploader.api import GarminAPI, GarminAPIException
from garmin_uploader.workflow import Activity as GarminActivity
from requests.exceptions import HTTPError
from rules.contrib.models import RulesModelMixin, RulesModelBase
from rules.contrib.models import RulesModelBase, RulesModelMixin

from ..models import Checkpoint, Track
from ..utils import (
Expand All @@ -28,6 +28,8 @@

@rules.predicate
def is_route_owner(user, route):
if not route or isinstance(user, AnonymousUser):
return False
return route.athlete == user.athlete


Expand Down Expand Up @@ -90,9 +92,12 @@ class Route(RulesModelMixin, Track, metaclass=RulesModelBase):

class Meta:
rules_permissions = {
"import": rules.always_allow,
"view": rules.always_allow,
"change": is_route_owner,
"update": is_route_owner,
"delete": is_route_owner,
"download": is_route_owner,
"garmin_upload": is_route_owner,
}
constraints = [
models.UniqueConstraint(
Expand Down
68 changes: 36 additions & 32 deletions homebytwo/routes/tests/test_gpx.py
Expand Up @@ -2,13 +2,14 @@
from os.path import dirname, realpath
from xml.dom import minidom

from django.conf import settings
from django.test import TestCase, override_settings
from django.urls import reverse

import pytest
import responses
from garmin_uploader import api as garmin_api
from mock import patch
from pytest_django.asserts import assertContains, assertRedirects

from ...utils.factories import AthleteFactory
from ...utils.tests import create_route_with_checkpoints
Expand Down Expand Up @@ -174,7 +175,7 @@ def test_download_route_gpx_other_athlete_view(self):
gpx_url = reverse("routes:gpx", kwargs={"pk": self.route.pk})
response = self.client.get(gpx_url)

self.assertEqual(response.status_code, 404)
assert response.status_code == 403

def test_download_route_gpx_route_with_no_schedule(self):
assert "schedule" not in self.route.data.columns
Expand All @@ -184,36 +185,6 @@ def test_download_route_gpx_route_with_no_schedule(self):

self.assertIn("<name>{}</name>".format(self.route.name), file_content)

@override_settings(GARMIN_ACTIVITY_URL="https://example.com/garmin/{}")
def test_garmin_activity_url(self):
self.route.garmin_id = 123456
self.route.save(update_fields=["garmin_id"])
garmin_url = settings.GARMIN_ACTIVITY_URL.format(self.route.garmin_id)

response = self.client.get(
reverse("routes:route", kwargs={"pk": self.route.id})
)
self.assertContains(response, garmin_url)

def test_garmin_upload_not_route_athlete(self):
second_athlete = AthleteFactory(user__password="123456")
self.client.login(username=second_athlete.user.username, password="123456")
response = self.client.get(
reverse("routes:garmin_upload", args=[self.route.pk])
)
self.assertEqual(response.status_code, 404)

def test_garmin_upload_view_success(self):
upload_url = reverse("routes:garmin_upload", args=[self.route.pk])
route_url = reverse("routes:route", args=[self.route.pk])

with patch(
"homebytwo.routes.tasks.upload_route_to_garmin_task.delay"
) as mock_task:
response = self.client.get(upload_url)
self.assertRedirects(response, route_url)
self.assertTrue(mock_task.called)

def test_garmin_upload_task_success(self):
self.route.garmin_id = 123456
self.route.save(update_fields=["garmin_id"])
Expand Down Expand Up @@ -299,6 +270,39 @@ def test_garmin_delete_failure(self):
self.assertIn("Failed to delete activity", response)


@pytest.fixture
def gpx_route(athlete):
return create_route_with_checkpoints(number_of_checkpoints=5, athlete=athlete)


def test_garmin_activity_url(athlete, client, gpx_route, settings):
settings.GARMIN_ACTIVITY_URL = "https://example.com/garmin/{}"
gpx_route.garmin_id = 123456
gpx_route.save(update_fields=["garmin_id"])
response = client.get(gpx_route.get_absolute_url())
user = response.context["user"]
assert user.has_perm(gpx_route.get_perm("garmin_upload"), gpx_route)
assertContains(response, gpx_route.garmin_activity_url)


def test_garmin_upload_not_owner(athlete, client, gpx_route):
gpx_route.athlete = AthleteFactory()
gpx_route.save(update_fields=["athlete"])
response = client.get(gpx_route.get_absolute_url("garmin_upload"))

assert response.status_code == 403


def test_garmin_upload(athlete, client, gpx_route):
upload_url = gpx_route.get_absolute_url("garmin_upload")
route_url = gpx_route.get_absolute_url()

with patch("homebytwo.routes.tasks.upload_route_to_garmin_task.delay") as mock_task:
response = client.get(upload_url)
assertRedirects(response, route_url)
assert mock_task.called


def test_download_route_gpx_view(athlete, client):
route = create_route_with_checkpoints(number_of_checkpoints=5, athlete=athlete)
wpt_xml = '<wpt lat="{1}" lon="{0}">'
Expand Down

0 comments on commit 8aac04b

Please sign in to comment.