Project for the UPV to develop an app like BlaBlaCar but only for UPV people.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

all tests green

+790 -239
+2 -1
package.json
··· 12 12 "build": "gulp deploy" 13 13 }, 14 14 "devDependencies": { 15 - "angular": "^1.5.5", 15 + "angular": "^1.5.11", 16 16 "angular-cookies": "^1.5.7", 17 17 "angular-google-maps": "^2.3.3", 18 18 "angular-i18n": "^1.5.9", ··· 41 41 "lodash": "^4.13.1", 42 42 "moment": "^2.13.0", 43 43 "node-sass": "^3.4.2", 44 + "rrule": "^2.2.0", 44 45 "tether": "^1.3.2", 45 46 "vinyl-buffer": "^1.0.0", 46 47 "vinyl-source-stream": "^1.1.0",
+4 -4
upvcarshare/config/settings/test.py
··· 12 12 # ------------------------------------------------------------------------------ 13 13 # Database in memory for tests 14 14 DATABASES = { 15 - "default": { 16 - "ENGINE": "django.contrib.gis.db.backends.spatialite", 17 - "NAME": ":memory:", 18 - }, 15 + 'default': env.db( 16 + 'DATABASE_URL', default='spatialite:///{}'.format(str(ROOT_DIR.path('{}.db'.format(PROJECT_NAME.lower())))) 17 + ), 19 18 } 19 + DATABASES['default']['PORT'] = str(DATABASES['default']['PORT']) # Fix a problem with Oracle connector 20 20 DATABASES['default']['ATOMIC_REQUESTS'] = True 21 21 SPATIALITE_LIBRARY_PATH = env('SPATIALITE_LIBRARY_PATH', default='/usr/local/lib/mod_spatialite.dylib')
+7 -3
upvcarshare/journeys/admin.py
··· 9 9 from journeys import DEFAULT_GOOGLE_MAPS_SRID, DEFAULT_PROJECTED_SRID, DEFAULT_WGS84_SRID 10 10 from journeys.helpers import make_point_projected, make_point 11 11 from journeys.models import Residence, Journey, Campus, Message, Transport, \ 12 - Passenger 12 + Passenger, JourneyTemplate 13 13 14 14 15 15 class PlaceAdminForm(forms.ModelForm): ··· 63 63 64 64 @admin.register(Journey) 65 65 class JourneyAdmin(admin.ModelAdmin): 66 - list_display = ["id", "residence", "campus", "kind", "departure", 67 - "created"] 66 + list_display = ["id", "departure", "created"] 67 + 68 + 69 + @admin.register(JourneyTemplate) 70 + class JourneyAdmin(admin.ModelAdmin): 71 + list_display = ["id", "residence", "campus", "kind", "created"] 68 72 69 73 70 74 @admin.register(Passenger)
+2 -2
upvcarshare/journeys/api/v1/resources.py
··· 200 200 } 201 201 pk = kwargs.get('pk', None) 202 202 if pk is not None: 203 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 203 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 204 204 args["journey"] = journey 205 205 queryset = Journey.objects.recommended(**args) 206 206 page = self.paginate_queryset(queryset) ··· 256 256 @staticmethod 257 257 def cancel(request, **kwargs): 258 258 pk = kwargs.get('pk', 0) 259 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 259 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 260 260 journey.cancel() 261 261 return Response(status=status.HTTP_201_CREATED) 262 262
+32 -42
upvcarshare/journeys/forms.py
··· 5 5 6 6 import floppyforms 7 7 import pytz 8 + import re 9 + from dateutil.rrule import rrulestr 8 10 from django import forms 9 11 from django.contrib.gis.geos import GEOSGeometry 10 12 from django.core.exceptions import ObjectDoesNotExist 13 + from django.utils import timezone 11 14 from django.utils.timezone import make_aware 12 15 from django.utils.translation import ugettext_lazy as _ 13 - from django.utils import timezone 14 16 15 17 from journeys import JOURNEY_KINDS, GOING, RETURN, DEFAULT_GOOGLE_MAPS_SRID, \ 16 18 DEFAULT_PROJECTED_SRID, DEFAULT_WGS84_SRID 17 19 from journeys.helpers import expand, make_point 18 - from journeys.models import Residence, Journey, Campus, Transport 20 + from journeys.models import Residence, Journey, Campus, Transport, JourneyTemplate 19 21 from users.models import User 20 22 21 23 ··· 73 75 74 76 class Meta: 75 77 model = Journey 76 - fields = ["residence", "campus", "kind", "i_am_driver", "transport", "free_places", "departure", "time_window", 77 - "arrival", "recurrence"] 78 + fields = ["free_places", "departure", "arrival"] 78 79 widgets = { 79 - "transport": forms.Select(attrs={"class": "form-control"}), 80 - "residence": forms.Select(attrs={"class": "form-control"}), 81 - "campus": forms.Select(attrs={"class": "form-control"}), 82 - "kind": forms.Select(attrs={"class": "form-control"}), 83 80 "free_places": forms.NumberInput(attrs={"class": "form-control"}), 84 81 "departure": floppyforms.DateTimeInput(attrs={"class": "form-control"}), 85 82 "arrival": floppyforms.DateTimeInput(attrs={"class": "form-control"}), 86 - "time_window": forms.NumberInput(attrs={"class": "form-control"}), 87 83 } 88 84 89 85 def __init__(self, *args, **kwargs): 90 86 self.user = kwargs.pop("user") 91 87 super(JourneyForm, self).__init__(*args, **kwargs) 92 - if self.user: 93 - self.fields['residence'].queryset = Residence.objects.filter(user=self.user) 94 - self.fields['transport'].queryset = Transport.objects.filter(user=self.user) 95 88 96 89 def clean_free_places(self): 97 90 free_places = self.cleaned_data["free_places"] 98 - transport = self.cleaned_data["transport"] 91 + transport = self.instance.template.transport if self.instance is not None else None 99 92 if transport is not None and free_places > transport.default_places: 100 93 raise forms.ValidationError(_("No puedes ofertar más plazas que las que tienes en el transporte")) 101 94 return free_places ··· 124 117 raise forms.ValidationError(_("No puedes crear viajes que llegues antes de salir")) 125 118 return arrival 126 119 127 - def save(self, commit=True, **kwargs): 128 - """When save a journey form, you have to provide an user.""" 129 - user = self.user 130 - if "user" in kwargs: 131 - assert isinstance(kwargs["user"], User) 132 - user = kwargs.get("user") 133 - journey = super(JourneyForm, self).save(commit=False) 134 - journey.user = user 135 - journey.driver = user if self.cleaned_data["i_am_driver"] else None 136 - if commit: 137 - journey.save() 138 - return journey 139 120 140 - 141 - class SmartJourneyForm(forms.ModelForm): 121 + class SmartJourneyTemplateForm(forms.ModelForm): 142 122 143 123 origin = forms.CharField(widget=forms.HiddenInput()) 144 124 destiny = forms.CharField(widget=forms.HiddenInput()) ··· 156 136 ) 157 137 ) 158 138 139 + free_places = forms.IntegerField( 140 + label=_("Cuántas plazas libres tengo"), 141 + required=False, 142 + widget=forms.NumberInput(attrs={"class": "form-control"}) 143 + ) 144 + 159 145 class Meta: 160 - model = Journey 146 + model = JourneyTemplate 161 147 fields = ["origin", "destiny", "i_am_driver", "transport", 162 - "free_places", "departure", "arrival", "time_window", 163 - "recurrence"] 148 + "free_places", "departure", "recurrence", "arrival", "time_window"] 164 149 widgets = { 165 150 "transport": forms.Select(attrs={"class": "form-control"}), 166 151 "kind": forms.Select(attrs={"class": "form-control"}), ··· 172 157 attrs={"class": "form-control"} 173 158 ), 174 159 "time_window": forms.NumberInput(attrs={"class": "form-control"}), 160 + "recurrence": forms.HiddenInput() 175 161 } 176 162 177 163 def __init__(self, *args, **kwargs): 178 164 self.user = kwargs.pop("user") 179 - super(SmartJourneyForm, self).__init__(*args, **kwargs) 165 + super(SmartJourneyTemplateForm, self).__init__(*args, **kwargs) 180 166 if self.user: 181 167 self.fields['transport'].queryset = Transport.objects.filter( 182 168 user=self.user ··· 229 215 raise forms.ValidationError(_("No puedes ofertar más plazas que las que tienes en el transporte")) 230 216 return free_places 231 217 218 + def clean_recurrence(self): 219 + """Delete DTSTART from recurrence, we use departure field.""" 220 + recurrence = self.cleaned_data["recurrence"] 221 + return re.sub(r"DTSTART=.+;", "", recurrence) 222 + 232 223 def save(self, commit=True, **kwargs): 233 224 """When save a journey form, you have to provide an user.""" 234 225 user = self.user 235 226 if "user" in kwargs: 236 227 assert isinstance(kwargs["user"], User) 237 228 user = kwargs.get("user") 238 - journey = super(SmartJourneyForm, self).save(commit=False) 239 - journey.user = user 240 - journey.driver = user if self.cleaned_data["i_am_driver"] else None 229 + journey_template = super(SmartJourneyTemplateForm, self).save(commit=False) 230 + journey_template.user = user 231 + journey_template.driver = user if self.cleaned_data["i_am_driver"] else None 241 232 # Smart origin, destiny and kind 242 233 origin = self.cleaned_data["origin"] 243 234 destiny = self.cleaned_data["destiny"] ··· 246 237 Campus: "campus", 247 238 } 248 239 attribute = attribute_selector[origin.__class__] 249 - setattr(journey, attribute, origin) 240 + setattr(journey_template, attribute, origin) 250 241 attribute = attribute_selector[destiny.__class__] 251 - setattr(journey, attribute, destiny) 252 - journey.kind = GOING if isinstance(origin, Residence) else RETURN 242 + setattr(journey_template, attribute, destiny) 243 + journey_template.kind = GOING if isinstance(origin, Residence) else RETURN 253 244 if commit: 254 - journey.save() 245 + journey_template.save() 255 246 # Expand journey recurrence 256 - journeys = expand(journey) 257 - Journey.objects.bulk_create(journeys) 258 - return journey 247 + expand(journey_template) 248 + return journey_template 259 249 260 250 261 251 class FilterForm(forms.Form): ··· 384 374 distance = self.cleaned_data["distance"] 385 375 departure_date = self.cleaned_data["departure_date"] 386 376 departure_time = self.cleaned_data["departure_time"] 387 - departure = datetime.datetime.combine(departure_date, departure_time) 377 + departure = make_aware(datetime.datetime.combine(departure_date, departure_time)) 388 378 search_by_time = self.cleaned_data.get("search_by_time", False) 389 379 time_window = self.cleaned_data["time_window"] 390 380 return Journey.objects.search(
+14 -27
upvcarshare/journeys/helpers.py
··· 6 6 7 7 from django.contrib.gis.gdal import SpatialReference, CoordTransform 8 8 from django.utils import timezone 9 + from django.utils.timezone import make_aware 9 10 10 11 from journeys import DEFAULT_PROJECTED_SRID, DEFAULT_WGS84_SRID 11 12 ··· 48 49 return first_day_current_week() + datetime.timedelta(days=7) 49 50 50 51 51 - def expand(journey): 52 - """Expands given journey using recurrence field to create new journeys.""" 53 - from journeys.models import Journey 54 - 55 - # Finish date is 1 of september, new course 56 - today = journey.departure 52 + def default_until(today): 57 53 finish_date = today.replace(day=1, month=9) 58 54 if today.month >= 9: 59 55 finish_date = finish_date.replace(year=finish_date.year + 1) 56 + return finish_date 57 + 58 + 59 + def expand(journey_template): 60 + """Expands given journey using recurrence field to create new journeys.""" 61 + recurrence_dates = journey_template.recurrence_dates() 60 62 journeys = [] 61 - if journey.recurrence: 62 - datetime_start = journey.departure + datetime.timedelta(days=1) 63 - datetime_end = datetime.datetime.combine(finish_date, time=datetime.time(0, 0, 0, 0)) 64 - if journey.recurrence.dtend: 65 - datetime_end = min(journey.recurrence.dtend, datetime_end) 66 - for date in journey.recurrence.occurrences(dtstart=datetime_start, dtend=datetime_end): 67 - new_journey = Journey.objects.get(pk=journey.pk) 68 - new_journey.pk = None 69 - new_journey.update_modified = True # TODO I dot know why this is needed, sorry :( 70 - new_journey.parent = journey 71 - new_journey.departure = date.replace( 72 - hour=new_journey.departure.hour, 73 - minute=new_journey.departure.minute, 74 - tzinfo=new_journey.departure.tzinfo 63 + for dates in recurrence_dates: 64 + journeys.append( 65 + journey_template.create_journey( 66 + departure=dates[0], 67 + arrival=dates[1] 75 68 ) 76 - if new_journey.arrival: 77 - new_journey.arrival = date.replace( 78 - hour=new_journey.arrival.hour, 79 - minute=new_journey.arrival.minute, 80 - tzinfo=new_journey.arrival.tzinfo 81 - ) 82 - journeys.append(new_journey) 69 + ) 83 70 return journeys
+12 -9
upvcarshare/journeys/managers.py
··· 21 21 :param journey: 22 22 :param override_distance: 23 23 """ 24 - key = "template__residence{}" if journey.kind == GOING else "template__campus{}" 25 - distance = getattr(journey, key.format("")).distance if override_distance is None else override_distance 24 + template = journey.template 25 + template_key = "residence{}" if journey.template.kind == GOING else "campus{}" 26 + key = "template__%s" % template_key 27 + distance = getattr(template, template_key.format("")).distance \ 28 + if override_distance is None else override_distance 26 29 return { 27 30 key.format("__position__distance_lte"): ( 28 - getattr(journey, key.format("")).position, 31 + getattr(template, template_key.format("")).position, 29 32 D(m=distance) 30 33 ), 31 - "departure__lte": journey.departure + datetime.timedelta(minutes=journey.time_window), 32 - "departure__gte": journey.departure - datetime.timedelta(minutes=journey.time_window), 34 + "departure__lte": journey.departure + datetime.timedelta(minutes=journey.template.time_window), 35 + "departure__gte": journey.departure - datetime.timedelta(minutes=journey.template.time_window), 33 36 } 34 37 35 38 ··· 66 69 } 67 70 if transport is not None: 68 71 data["driver"] = user 69 - data["free_places"] = transport.default_places 72 + # data["free_places"] = transport.default_places 70 73 return self.create(**data) 71 74 72 75 ··· 75 78 def visible(self, user=None): 76 79 """Journey visible for the given user.""" 77 80 if user is not None: 78 - return self.filter(user__groups=user.groups.all()) 81 + return self.filter(template__user__groups=user.groups.all()) 79 82 return self 80 83 81 84 ··· 115 118 116 119 nearby = self.available(kind=kind) 117 120 if kind is not None: 118 - key = "residence{}" if kind == GOING else "campus{}" 121 + key = "template__residence{}" if kind == GOING else "template__campus{}" 119 122 key = key.format("__position__distance_lte") 120 123 nearby = nearby.filter(**{key: (geometry, distance)}) 121 124 else: ··· 133 136 now = timezone.now() 134 137 queryset = self.filter(template__user=user, template__driver__isnull=True, departure__gt=now) 135 138 if kind is not None: 136 - queryset = queryset.filter(kind=kind) 139 + queryset = queryset.filter(template__kind=kind) 137 140 return queryset 138 141 139 142 def recommended(self, user, kind=None, journey=None, override_distance=None, ignore_full=False):
+82
upvcarshare/journeys/migrations/0015_auto_20170624_1433.py
··· 1 + # -*- coding: utf-8 -*- 2 + # Generated by Django 1.10.2 on 2017-06-24 14:33 3 + from __future__ import unicode_literals 4 + 5 + from django.conf import settings 6 + from django.db import migrations, models 7 + import django.db.models.deletion 8 + import django_extensions.db.fields 9 + import recurrence.fields 10 + 11 + 12 + class Migration(migrations.Migration): 13 + 14 + dependencies = [ 15 + migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 + ('journeys', '0014_auto_20170530_0923'), 17 + ] 18 + 19 + operations = [ 20 + migrations.CreateModel( 21 + name='JourneyTemplate', 22 + fields=[ 23 + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 + ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), 25 + ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), 26 + ('time_window', models.PositiveIntegerField(blank=True, default=30, help_text='Se buscarán por los viajes que salgan hasta con estos minutos de antelación', verbose_name='ventana de tiempo')), 27 + ('recurrence', recurrence.fields.RecurrenceField(blank=True, null=True, verbose_name='¿Vas a realizar este viaje más de una vez?')), 28 + ('kind', models.PositiveIntegerField(choices=[(0, 'ida'), (1, 'vuelta')], verbose_name='tipo de viaje')), 29 + ('campus', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='journeys', to='journeys.Campus', verbose_name='campus')), 30 + ('driver', models.ForeignKey(blank=True, help_text='user who drives during the journey', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 31 + ('residence', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='journeys', to='journeys.Residence', verbose_name='lugar')), 32 + ('transport', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='journeys', to='journeys.Transport', verbose_name='Medio de transporte utilizado')), 33 + ('user', models.ForeignKey(help_text='user who creates the journey', on_delete=django.db.models.deletion.CASCADE, related_name='journeys', to=settings.AUTH_USER_MODEL)), 34 + ], 35 + options={ 36 + 'ordering': ('-modified', '-created'), 37 + 'abstract': False, 38 + 'get_latest_by': 'modified', 39 + }, 40 + ), 41 + migrations.RemoveField( 42 + model_name='journey', 43 + name='campus', 44 + ), 45 + migrations.RemoveField( 46 + model_name='journey', 47 + name='driver', 48 + ), 49 + migrations.RemoveField( 50 + model_name='journey', 51 + name='kind', 52 + ), 53 + migrations.RemoveField( 54 + model_name='journey', 55 + name='parent', 56 + ), 57 + migrations.RemoveField( 58 + model_name='journey', 59 + name='recurrence', 60 + ), 61 + migrations.RemoveField( 62 + model_name='journey', 63 + name='residence', 64 + ), 65 + migrations.RemoveField( 66 + model_name='journey', 67 + name='time_window', 68 + ), 69 + migrations.RemoveField( 70 + model_name='journey', 71 + name='transport', 72 + ), 73 + migrations.RemoveField( 74 + model_name='journey', 75 + name='user', 76 + ), 77 + migrations.AddField( 78 + model_name='journey', 79 + name='template', 80 + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='journeys', to='journeys.JourneyTemplate'), 81 + ), 82 + ]
+20
upvcarshare/journeys/migrations/0016_auto_20170624_1958.py
··· 1 + # -*- coding: utf-8 -*- 2 + # Generated by Django 1.10.2 on 2017-06-24 19:58 3 + from __future__ import unicode_literals 4 + 5 + from django.db import migrations, models 6 + 7 + 8 + class Migration(migrations.Migration): 9 + 10 + dependencies = [ 11 + ('journeys', '0015_auto_20170624_1433'), 12 + ] 13 + 14 + operations = [ 15 + migrations.AlterField( 16 + model_name='journeytemplate', 17 + name='recurrence', 18 + field=models.TextField(blank=True, null=True, verbose_name='¿Vas a realizar este viaje más de una vez?'), 19 + ), 20 + ]
+25
upvcarshare/journeys/migrations/0017_auto_20170624_2019.py
··· 1 + # -*- coding: utf-8 -*- 2 + # Generated by Django 1.10.2 on 2017-06-24 20:19 3 + from __future__ import unicode_literals 4 + 5 + from django.db import migrations, models 6 + 7 + 8 + class Migration(migrations.Migration): 9 + 10 + dependencies = [ 11 + ('journeys', '0016_auto_20170624_1958'), 12 + ] 13 + 14 + operations = [ 15 + migrations.AddField( 16 + model_name='journeytemplate', 17 + name='arrival', 18 + field=models.DateTimeField(blank=True, null=True, verbose_name='Cuándo creo que voy a llegar*'), 19 + ), 20 + migrations.AddField( 21 + model_name='journeytemplate', 22 + name='departure', 23 + field=models.DateTimeField(null=True, verbose_name='Cuándo voy a realizar el viaje*'), 24 + ), 25 + ]
+53 -14
upvcarshare/journeys/models.py
··· 2 2 from __future__ import unicode_literals, print_function, absolute_import 3 3 4 4 from copy import copy 5 + from dateutil.rrule import rrulestr 6 + from django.utils.timezone import make_aware 5 7 from functools import reduce 6 8 import datetime 7 9 ··· 21 23 from journeys import JOURNEY_KINDS, GOING, RETURN, DEFAULT_DISTANCE, DEFAULT_PROJECTED_SRID, DEFAULT_WGS84_SRID, \ 22 24 DEFAULT_TIME_WINDOW, PASSENGER_STATUSES, UNKNOWN, CONFIRMED, REJECTED, DEFAULT_GOOGLE_MAPS_SRID 23 25 from journeys.exceptions import NoFreePlaces, NotAPassenger, AlreadyAPassenger 24 - from journeys.helpers import make_point_wgs84, make_point 25 - from journeys.managers import JourneyManager, ResidenceManager, MessageManager 26 + from journeys.helpers import make_point_wgs84, make_point, default_until 27 + from journeys.managers import JourneyManager, ResidenceManager, MessageManager, JourneyTemplateManager 26 28 from notifications import JOIN, LEAVE, CANCEL, CONFIRM, REJECT, THROW_OUT 27 29 from notifications.decorators import dispatch 28 30 ··· 144 146 ) 145 147 146 148 # Data about recurrence 147 - recurrence = RecurrenceField(verbose_name=_("¿Vas a realizar este viaje más de una vez?"), null=True, blank=True) 149 + departure = models.DateTimeField(verbose_name=_("Cuándo voy a realizar el viaje*"), null=True) 150 + arrival = models.DateTimeField(verbose_name=_("Cuándo creo que voy a llegar*"), null=True, blank=True) 151 + recurrence = models.TextField( 152 + verbose_name=_("¿Vas a realizar este viaje más de una vez?"), 153 + null=True, 154 + blank=True 155 + ) 148 156 149 157 # Kind of the journey 150 158 kind = models.PositiveIntegerField(choices=JOURNEY_KINDS, verbose_name=_("tipo de viaje")) ··· 158 166 blank=True 159 167 ) 160 168 169 + objects = JourneyTemplateManager() 170 + 171 + def has_recurrence(self): 172 + return self.recurrence is not None and self.recurrence != "" 173 + 161 174 def create_journey(self, departure, arrival): 162 175 """Creates a Journey using the template and the data given.""" 163 - journey = Journey( 164 - template=self, 165 - departure=departure, 166 - arrival=arrival, 167 - free_places=self.transport.default_places, 168 - total_passengers=0, 169 - ) 176 + attributes = { 177 + "template": self, 178 + "departure": departure, 179 + "arrival": arrival, 180 + } 181 + if self.driver is not None: 182 + attributes["free_places"] = self.transport.default_places if self.transport is not None else 4 183 + attributes["total_passengers"] = 0 184 + journey = Journey(**attributes) 170 185 journey.save() 171 186 return journey 172 187 188 + def recurrence_dates(self): 189 + """Returns a list of (departure, arrival) datetimes, to create the list of 190 + journeys. 191 + """ 192 + if self.has_recurrence(): 193 + interval = self.arrival - self.departure 194 + rules = rrulestr(self.recurrence, dtstart=self.departure) 195 + if rules._until is None: 196 + rules._until = default_until(self.departure) 197 + dates = list(map(lambda d: make_aware(d), list(rules))) 198 + return zip(dates, map(lambda d: d + interval, dates)) 199 + return [(self.departure, self.arrival)] 200 + 173 201 def save(self, **kwargs): 174 202 """Override save to set the default time window value. Default value not set in Oracle.""" 175 203 if not self.time_window: ··· 182 210 """A model class to represent a journey between two node.""" 183 211 184 212 # Reference to the template 185 - template = models.ForeignKey("journeys.JourneyTemplate", related_name="journeys") 213 + template = models.ForeignKey("journeys.JourneyTemplate", related_name="journeys", null=True) 186 214 187 215 # Data about places and passengers 188 216 free_places = models.PositiveIntegerField(default=4, verbose_name=_("plazas libres"), blank=True, null=True) ··· 230 258 def driver(self): 231 259 return self.template.driver 232 260 261 + @property 262 + def kind(self): 263 + return self.template.kind 264 + 233 265 def __str__(self): 234 266 return self.description(strip_html=True) 235 267 ··· 287 319 """ 288 320 # Join only one 289 321 if join_to is None or join_to == "one": 290 - if self.passengers.filter(user=user).exists() or self.driver == user: 322 + if self.passengers.filter(user=user).exists() or self.template.driver == user: 291 323 raise AlreadyAPassenger() 292 324 if self.count_passengers() < self.free_places: 293 325 passenger = Passenger.objects.create( ··· 351 383 # self.passengers.filter(user=user).update(status=CONFIRMED) 352 384 passenger = self.passengers.filter(user=user).first() 353 385 if passenger: 354 - passengers = passenger.recurrence() 386 + passengers = passenger.brothers() 355 387 passengers.update(status=CONFIRMED) 356 388 self.total_passengers += 1 357 389 self.save() ··· 364 396 # self.passengers.filter(user=user).update(status=REJECTED) 365 397 passenger = self.passengers.filter(user=user).first() 366 398 if passenger: 367 - passengers = passenger.recurrence() 399 + passengers = passenger.brothers() 368 400 passengers.update(status=REJECTED) 369 401 370 402 def is_passenger(self, user, all_passengers=False): ··· 422 454 elif self.is_passenger(user): 423 455 return self.passengers.filter(status=CONFIRMED) 424 456 return Passenger.objects.none() 457 + 458 + def save(self, **kwargs): 459 + if self.template is not None and self.departure is None: 460 + self.departure = self.template.departure 461 + if self.template is not None and self.arrival is None: 462 + self.arrival = self.template.arrival 463 + super(Journey, self).save(**kwargs) 425 464 426 465 427 466 class Passenger(TimeStampedModel):
+10 -1
upvcarshare/journeys/tests/factories.py
··· 28 28 model = "journeys.Campus" 29 29 30 30 31 - class JourneyFactory(factory.django.DjangoModelFactory): 31 + class JourneyTemplateFactory(factory.django.DjangoModelFactory): 32 32 user = factory.SubFactory(UserFactory) 33 33 residence = factory.SubFactory(ResidenceFactory) 34 34 campus = factory.SubFactory(CampusFactory) ··· 36 36 arrival = factory.LazyFunction(lambda: timezone.now() + datetime.timedelta(days=1, minutes=30)) 37 37 time_window = DEFAULT_TIME_WINDOW 38 38 kind = GOING 39 + 40 + class Meta: 41 + model = "journeys.JourneyTemplate" 42 + 43 + 44 + class JourneyFactory(factory.django.DjangoModelFactory): 45 + template = factory.SubFactory(JourneyTemplateFactory) 46 + departure = factory.LazyFunction(lambda: timezone.now() + datetime.timedelta(days=1)) 47 + arrival = factory.LazyFunction(lambda: timezone.now() + datetime.timedelta(days=1, minutes=30)) 39 48 40 49 class Meta: 41 50 model = "journeys.Journey"
+33 -17
upvcarshare/journeys/tests/test_api.py
··· 10 10 11 11 from journeys import GOING, RETURN, DEFAULT_PROJECTED_SRID 12 12 from journeys.models import Transport, Journey 13 - from journeys.tests.factories import TransportFactory, ResidenceFactory, CampusFactory, JourneyFactory, MessageFactory 13 + from journeys.tests.factories import TransportFactory, ResidenceFactory, CampusFactory, JourneyFactory, MessageFactory, \ 14 + JourneyTemplateFactory 14 15 from users.tests.factories import UserFactory 15 16 from users.tests.mocks import UPVLoginDataService 16 17 ··· 85 86 user = UserFactory() 86 87 origin = ResidenceFactory(user=user) 87 88 destination = CampusFactory() 88 - return JourneyFactory(user=user, residence=origin, campus=destination, kind=kind) 89 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination, kind=kind) 90 + return JourneyFactory(template=template, departure=template.departure, arrival=template.arrival) 89 91 90 92 def test_get_journeys(self): 91 93 user = UserFactory() ··· 177 179 campus = CampusFactory() 178 180 user3 = UserFactory() 179 181 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545274.90, srid=DEFAULT_PROJECTED_SRID)) 180 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus) 181 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 182 - JourneyFactory(user=user3, driver=user3, residence=residence3, campus=campus) 182 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus) 183 + JourneyFactory(template=template) 184 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 185 + JourneyFactory(template=template) 186 + template = JourneyTemplateFactory(user=user3, driver=user3, residence=residence3, campus=campus) 187 + JourneyFactory(template=template) 183 188 user4 = UserFactory() 184 189 residence4 = ResidenceFactory( 185 190 user=user4, position=Point(882532.74, 545437.43, srid=DEFAULT_PROJECTED_SRID), distance=2500 186 191 ) 187 - JourneyFactory(user=user4, residence=residence4, campus=campus) 192 + template = JourneyTemplateFactory(user=user4, residence=residence4, campus=campus) 193 + JourneyFactory(template=template) 188 194 # Make query... 189 195 url = "/api/v1/journeys/recommended/" 190 196 self.client.force_authenticate(user=user4) ··· 202 208 campus = CampusFactory() 203 209 user3 = UserFactory() 204 210 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545274.90, srid=DEFAULT_PROJECTED_SRID)) 205 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus) 206 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 207 - JourneyFactory(user=user3, driver=user3, residence=residence3, campus=campus) 211 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus) 212 + JourneyFactory(template=template) 213 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 214 + JourneyFactory(template=template) 215 + template = JourneyTemplateFactory(user=user3, driver=user3, residence=residence3, campus=campus) 216 + JourneyFactory(template=template) 208 217 user4 = UserFactory() 209 218 residence4 = ResidenceFactory( 210 219 user=user4, position=Point(882532.74, 545437.43, srid=DEFAULT_PROJECTED_SRID), distance=2500 211 220 ) 212 - journey = JourneyFactory(user=user4, residence=residence4, campus=campus) 221 + template = JourneyTemplateFactory(user=user4, residence=residence4, campus=campus) 222 + journey = JourneyFactory(template=template) 213 223 # Make query... 214 224 url = "/api/v1/journeys/{}/recommended/".format(journey.pk) 215 225 self.client.force_authenticate(user=user4) ··· 226 236 user = UserFactory() 227 237 origin = ResidenceFactory(user=user) 228 238 destination = CampusFactory() 229 - journey = JourneyFactory(user=user, residence=origin, campus=destination) 239 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination) 240 + journey = JourneyFactory(template=template) 230 241 [MessageFactory( 231 242 user=UserFactory(), 232 - journey=JourneyFactory(user=UserFactory(), residence=origin, campus=destination)) 243 + journey=JourneyFactory(template=JourneyTemplateFactory(user=UserFactory(), residence=origin, campus=destination))) 233 244 for _ in range(2)] 234 245 [MessageFactory(user=user, journey=journey) for _ in range(5)] 235 246 [MessageFactory(user=UserFactory(), journey=journey) for _ in range(5)] ··· 244 255 user = UserFactory() 245 256 origin = ResidenceFactory(user=user) 246 257 destination = CampusFactory() 247 - journey = JourneyFactory(user=user, residence=origin, campus=destination) 258 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination) 259 + journey = JourneyFactory(template=template) 248 260 [MessageFactory(user=user, journey=journey) for _ in range(5)] 249 261 [MessageFactory(user=UserFactory(), journey=journey) for _ in range(5)] 250 262 url = "/api/v1/journeys/{}/messages/".format(journey.pk) ··· 258 270 user = UserFactory() 259 271 origin = ResidenceFactory(user=user) 260 272 destination = CampusFactory() 261 - journey = JourneyFactory(user=user, residence=origin, campus=destination) 273 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination) 274 + journey = JourneyFactory(template=template) 262 275 self.assertEquals(0, journey.messages.count()) 263 276 data = { 264 277 "content": "Hello!", ··· 274 287 user = UserFactory() 275 288 origin = ResidenceFactory(user=user) 276 289 destination = CampusFactory() 277 - journey1 = JourneyFactory(user=user, residence=origin, campus=destination) 278 - journey2 = JourneyFactory(user=user, residence=origin, campus=destination) 279 - journey3 = JourneyFactory(user=UserFactory(), residence=origin, campus=destination) 290 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination) 291 + journey1 = JourneyFactory(template=template) 292 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination) 293 + journey2 = JourneyFactory(template=template) 294 + template = JourneyTemplateFactory(user=UserFactory(), residence=origin, campus=destination) 295 + journey3 = JourneyFactory(template=template) 280 296 [MessageFactory(user=user, journey=journey1) for _ in range(2)] 281 297 [MessageFactory(user=UserFactory(), journey=journey1) for _ in range(2)] 282 298 [MessageFactory(user=user, journey=journey2) for _ in range(2)]
+11 -4
upvcarshare/journeys/tests/test_forms.py
··· 4 4 import datetime 5 5 6 6 import six 7 + from django.utils.timezone import make_aware 7 8 from test_plus import TestCase 8 9 9 10 from journeys import DEFAULT_PROJECTED_SRID, DEFAULT_GOOGLE_MAPS_SRID 10 11 from journeys.forms import SearchJourneyForm 11 12 from journeys.helpers import make_point 12 - from journeys.tests.factories import JourneyFactory 13 + from journeys.tests.factories import JourneyFactory, JourneyTemplateFactory 13 14 from users.tests.factories import UserFactory 14 15 from users.tests.mocks import UPVLoginDataService 15 16 ··· 24 25 25 26 def test_search_form(self): 26 27 user = UserFactory() 27 - JourneyFactory(departure=datetime.datetime.now() + datetime.timedelta(days=2), user=user, driver=user) 28 - journey = JourneyFactory(user=user, driver=user) 28 + template = JourneyTemplateFactory( 29 + departure=make_aware(datetime.datetime.now() + datetime.timedelta(days=2)), 30 + user=user, 31 + driver=user 32 + ) 33 + JourneyFactory(template=template, departure=template.departure) 34 + template = JourneyTemplateFactory(user=user, driver=user) 35 + journey = JourneyFactory(template=template) 29 36 30 37 data = { 31 38 "departure_date": journey.departure.date(), ··· 36 43 journey.residence.position, 37 44 origin_coord_srid=DEFAULT_PROJECTED_SRID, 38 45 destiny_coord_srid=DEFAULT_GOOGLE_MAPS_SRID 39 - )) 46 + )), 40 47 } 41 48 form = SearchJourneyForm(data) 42 49 self.assertTrue(form.is_valid())
+16 -19
upvcarshare/journeys/tests/test_helpers.py
··· 4 4 import datetime 5 5 6 6 import recurrence 7 + from dateutil.rrule import rrule, DAILY 7 8 from django.contrib.gis.geos import Point 8 9 from django.utils.timezone import make_naive 9 10 from test_plus import TestCase ··· 11 12 from journeys import DEFAULT_PROJECTED_SRID, DEFAULT_WGS84_SRID 12 13 from journeys.helpers import make_point_wgs84, make_point_projected, date_to_datetime, first_day_current_week, \ 13 14 last_day_current_week, expand 14 - from journeys.tests.factories import JourneyFactory 15 + from journeys.tests.factories import JourneyFactory, JourneyTemplateFactory 15 16 from users.tests.mocks import UPVLoginDataService 16 17 17 18 try: ··· 44 45 self.assertIsInstance(last_day_current_week(), datetime.date) 45 46 46 47 def test_expand(self): 47 - journey = JourneyFactory() 48 - journey.departure = journey.departure.replace(month=1) 48 + journey = JourneyTemplateFactory() 49 + journey.departure = journey.departure.replace(day=1, month=1) 49 50 journey.save() 50 - 51 - rule = recurrence.Rule(recurrence.DAILY) 52 - pattern = recurrence.Recurrence( 53 - dtstart=make_naive(journey.departure) + datetime.timedelta(days=1), 54 - dtend=make_naive(journey.departure) + datetime.timedelta(days=20), 55 - rrules=[rule, ] 51 + pattern = rrule( 52 + dtstart=make_naive(journey.departure), 53 + until=make_naive(journey.departure) + datetime.timedelta(days=20), 54 + freq=DAILY 56 55 ) 57 - 58 - journey.recurrence = pattern 56 + journey.recurrence = str(pattern) 59 57 journey.save() 60 58 journeys = expand(journey) 61 - self.assertEquals(22, len(journeys)) 59 + self.assertEquals(21, len(journeys)) 62 60 63 61 def test_expand_september(self): 64 - journey = JourneyFactory() 62 + journey = JourneyTemplateFactory() 65 63 journey.departure = journey.departure.replace(day=1, month=9) 66 64 journey.save() 67 65 68 - rule = recurrence.Rule(recurrence.DAILY) 69 - pattern = recurrence.Recurrence( 66 + pattern = rrule( 70 67 dtstart=make_naive(journey.departure) + datetime.timedelta(days=1), 71 - dtend=make_naive(journey.departure) + datetime.timedelta(days=20), 72 - rrules=[rule, ] 68 + until=make_naive(journey.departure) + datetime.timedelta(days=20), 69 + freq=DAILY 73 70 ) 74 71 75 - journey.recurrence = pattern 72 + journey.recurrence = str(pattern) 76 73 journey.save() 77 74 journeys = expand(journey) 78 - self.assertEquals(22, len(journeys)) 75 + self.assertEquals(20, len(journeys))
+62 -30
upvcarshare/journeys/tests/test_models.py
··· 10 10 11 11 from journeys import GOING, RETURN, DEFAULT_PROJECTED_SRID 12 12 from journeys.exceptions import AlreadyAPassenger, NotAPassenger 13 - from journeys.models import Journey, Passenger, Residence 14 - from journeys.tests.factories import ResidenceFactory, CampusFactory, TransportFactory, JourneyFactory 13 + from journeys.helpers import expand 14 + from journeys.models import Journey, Passenger, Residence, JourneyTemplate 15 + from journeys.tests.factories import ResidenceFactory, CampusFactory, TransportFactory, JourneyFactory, \ 16 + JourneyTemplateFactory 15 17 from users.tests.factories import UserFactory 16 18 from users.tests.mocks import UPVLoginDataService 17 19 ··· 30 32 user = UserFactory() 31 33 origin = ResidenceFactory(user=user) 32 34 destination = CampusFactory() 33 - return JourneyFactory(user=user, residence=origin, campus=destination, kind=kind) 35 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination, kind=kind) 36 + return JourneyFactory(template=template) 34 37 35 38 def test_smart_create_no_transport(self): 36 39 """Test smart create of a journey without transport.""" 37 40 user = UserFactory() 38 41 origin = ResidenceFactory(user=user) 39 42 destination = CampusFactory() 40 - journey = Journey.objects.smart_create( 43 + template = JourneyTemplate.objects.smart_create( 41 44 user=user, origin=origin, destination=destination, departure=timezone.now() + datetime.timedelta(days=1) 42 45 ) 46 + journeys = expand(template) 47 + journey = journeys[0] 43 48 self.assertEquals(Journey.objects.count(), 1) 44 49 self.assertEquals(Journey.objects.first(), journey) 45 50 ··· 49 54 transport = TransportFactory(user=user) 50 55 origin = ResidenceFactory(user=user) 51 56 destination = CampusFactory() 52 - journey = Journey.objects.smart_create( 57 + template = JourneyTemplate.objects.smart_create( 53 58 user=user, origin=origin, destination=destination, departure=timezone.now() + datetime.timedelta(days=1), 54 59 transport=transport 55 60 ) 61 + journeys = expand(template) 62 + journey = journeys[0] 56 63 self.assertEquals(Journey.objects.count(), 1) 57 64 self.assertEquals(Journey.objects.first(), journey) 58 65 self.assertEquals(journey.free_places, transport.default_places) ··· 64 71 user = UserFactory() 65 72 origin = ResidenceFactory(user=user) 66 73 destination = CampusFactory() 67 - journey = JourneyFactory(user=user, residence=origin, campus=destination, kind=GOING) 74 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination, kind=GOING) 75 + journey = JourneyFactory(template=template) 68 76 self.assertEquals(journey.origin, origin) 69 77 70 78 def test_destination(self): 71 79 user = UserFactory() 72 80 origin = ResidenceFactory(user=user) 73 81 destination = CampusFactory() 74 - journey = JourneyFactory(user=user, residence=origin, campus=destination, kind=GOING) 82 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination, kind=GOING) 83 + journey = JourneyFactory(template=template) 75 84 self.assertEquals(journey.destination, destination) 76 85 77 86 def test_join_passenger(self): ··· 131 140 user2 = UserFactory() 132 141 residence2 = ResidenceFactory(user=user2, position=Point(882823.07, 545542.48, srid=DEFAULT_PROJECTED_SRID)) 133 142 campus = CampusFactory() 134 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus) 135 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 143 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus) 144 + JourneyFactory(template=template) 145 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 146 + JourneyFactory(template=template) 136 147 # Creates a journey without driver 137 148 user3 = UserFactory() 138 149 residence3 = ResidenceFactory(user=user3, position=Point(882454.58, 545877.33, srid=DEFAULT_PROJECTED_SRID)) 139 - JourneyFactory(user=user3, residence=residence3, campus=campus) 150 + template = JourneyTemplateFactory(user=user3, residence=residence3, campus=campus) 151 + JourneyFactory(template=template) 140 152 self.assertEquals(Journey.objects.available(user=user3, kind=GOING).count(), 2) 141 153 142 154 def test_available_return_query(self): ··· 146 158 user2 = UserFactory() 147 159 residence2 = ResidenceFactory(user=user2, position=Point(882823.07, 545542.48, srid=DEFAULT_PROJECTED_SRID)) 148 160 campus = CampusFactory() 149 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus, kind=RETURN) 150 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 161 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus, kind=RETURN) 162 + JourneyFactory(template=template) 163 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 164 + JourneyFactory(template=template) 151 165 # Creates a journey without driver 152 166 user3 = UserFactory() 153 167 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545877.33, srid=DEFAULT_PROJECTED_SRID)) 154 - JourneyFactory(user=user3, residence=residence3, campus=campus, kind=RETURN) 168 + template = JourneyTemplateFactory(user=user3, residence=residence3, campus=campus, kind=RETURN) 169 + JourneyFactory(template=template) 155 170 self.assertEquals(Journey.objects.available(user=user3, kind=RETURN).count(), 1) 156 171 157 172 def test_available_query(self): ··· 161 176 user2 = UserFactory() 162 177 residence2 = ResidenceFactory(user=user2, position=Point(882823.07, 545542.48, srid=DEFAULT_PROJECTED_SRID)) 163 178 campus = CampusFactory() 164 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus, kind=RETURN) 165 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 179 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus, kind=RETURN) 180 + JourneyFactory(template=template) 181 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 182 + JourneyFactory(template=template) 166 183 # Creates a journey without driver 167 184 user3 = UserFactory() 168 185 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545274.90, srid=DEFAULT_PROJECTED_SRID)) 169 - JourneyFactory(user=user3, residence=residence3, campus=campus, kind=RETURN) 186 + template = JourneyTemplateFactory(user=user3, residence=residence3, campus=campus, kind=RETURN) 187 + JourneyFactory(template=template) 170 188 self.assertEquals(Journey.objects.available(user=user3).count(), 2) 171 189 172 190 def test_nearby_query(self): ··· 178 196 campus = CampusFactory() 179 197 user3 = UserFactory() 180 198 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545274.90, srid=DEFAULT_PROJECTED_SRID)) 181 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus) 182 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 183 - JourneyFactory(user=user3, driver=user3, residence=residence3, campus=campus) 199 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus) 200 + JourneyFactory(template=template) 201 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 202 + JourneyFactory(template=template) 203 + template = JourneyTemplateFactory(user=user3, driver=user3, residence=residence3, campus=campus) 204 + JourneyFactory(template=template) 184 205 point = Point(882532.74, 545437.43, srid=DEFAULT_PROJECTED_SRID) 185 206 self.assertEquals(Journey.objects.nearby( 186 207 geometry=point, ··· 196 217 campus = CampusFactory() 197 218 user3 = UserFactory() 198 219 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545274.90, srid=DEFAULT_PROJECTED_SRID)) 199 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus) 200 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 201 - JourneyFactory(user=user3, driver=user3, residence=residence3, campus=campus) 220 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus) 221 + JourneyFactory(template=template) 222 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 223 + JourneyFactory(template=template) 224 + template = JourneyTemplateFactory(user=user3, driver=user3, residence=residence3, campus=campus) 225 + JourneyFactory(template=template) 202 226 point = Point(882532.74, 545437.43, srid=DEFAULT_PROJECTED_SRID) 203 227 self.assertEquals(Journey.objects.nearby( 204 228 geometry=point, ··· 215 239 campus = CampusFactory() 216 240 user3 = UserFactory() 217 241 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545274.90, srid=DEFAULT_PROJECTED_SRID)) 218 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus) 219 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 220 - JourneyFactory(user=user3, driver=user3, residence=residence3, campus=campus) 242 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus) 243 + JourneyFactory(template=template) 244 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 245 + JourneyFactory(template=template) 246 + template = JourneyTemplateFactory(user=user3, driver=user3, residence=residence3, campus=campus) 247 + JourneyFactory(template=template) 221 248 user4 = UserFactory() 222 249 residence4 = ResidenceFactory( 223 250 user=user4, position=Point(882532.74, 545437.43, srid=DEFAULT_PROJECTED_SRID), distance=2500 224 251 ) 225 - JourneyFactory(user=user4, residence=residence4, campus=campus) 252 + template = JourneyTemplateFactory(user=user4, residence=residence4, campus=campus) 253 + JourneyFactory(template=template) 226 254 self.assertEquals(Journey.objects.recommended( 227 255 user=user4, 228 256 ).count(), 2) ··· 236 264 campus = CampusFactory() 237 265 user3 = UserFactory() 238 266 residence3 = ResidenceFactory(user=user3, position=Point(865621.24, 545274.90, srid=DEFAULT_PROJECTED_SRID)) 239 - JourneyFactory(user=user1, driver=user1, residence=residence1, campus=campus) 240 - JourneyFactory(user=user2, driver=user2, residence=residence2, campus=campus) 241 - JourneyFactory(user=user3, driver=user3, residence=residence3, campus=campus) 267 + template = JourneyTemplateFactory(user=user1, driver=user1, residence=residence1, campus=campus) 268 + JourneyFactory(template=template) 269 + template = JourneyTemplateFactory(user=user2, driver=user2, residence=residence2, campus=campus) 270 + JourneyFactory(template=template) 271 + template = JourneyTemplateFactory(user=user3, driver=user3, residence=residence3, campus=campus) 272 + JourneyFactory(template=template) 242 273 user4 = UserFactory() 243 274 residence4 = ResidenceFactory( 244 275 user=user4, position=Point(882532.74, 545437.43, srid=DEFAULT_PROJECTED_SRID), distance=2500 245 276 ) 246 - JourneyFactory(user=user4, residence=residence4, campus=campus) 277 + template = JourneyTemplateFactory(user=user4, residence=residence4, campus=campus) 278 + JourneyFactory(template=template) 247 279 self.assertEquals(Journey.objects.recommended( 248 280 user=user4, 249 281 kind=GOING
+32 -24
upvcarshare/journeys/tests/test_views.py
··· 8 8 9 9 from journeys import GOING, RETURN 10 10 from journeys.models import Journey, Residence, Passenger 11 - from journeys.tests.factories import JourneyFactory, ResidenceFactory, CampusFactory 11 + from journeys.tests.factories import JourneyFactory, ResidenceFactory, CampusFactory, JourneyTemplateFactory 12 12 from users.tests.factories import UserFactory 13 13 from users.tests.mocks import UPVLoginDataService 14 14 ··· 99 99 100 100 def test_get_edit_journey(self): 101 101 user = self.make_user(username="foo") 102 - journey = JourneyFactory(user=user) 102 + journey = JourneyFactory(template=JourneyTemplateFactory(user=user)) 103 103 url_name = "journeys:edit" 104 104 self.assertLoginRequired(url_name, pk=journey.pk) 105 105 with self.login(user): ··· 108 108 109 109 def test_post_edit_journey(self): 110 110 user = self.make_user(username="foo") 111 - journey = JourneyFactory(user=user, kind=GOING) 111 + template = JourneyTemplateFactory(user=user, kind=GOING) 112 + journey = JourneyFactory(template=template) 112 113 url_name = "journeys:edit" 113 114 self.assertLoginRequired(url_name, pk=journey.pk) 114 115 with self.login(user): 115 116 data = { 116 - "residence": ResidenceFactory(user=user).pk, 117 - "campus": CampusFactory().pk, 118 - "kind": RETURN, 119 - "free_places": 4, 120 - "time_window": 30, 117 + # "residence": ResidenceFactory(user=user).pk, 118 + # "campus": CampusFactory().pk, 119 + # "kind": RETURN, 120 + "free_places": 3, 121 + # "time_window": 30, 121 122 "departure": (timezone.now() + datetime.timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S"), 122 123 "recurrence": "", 123 124 } 124 125 response = self.post(url_name=url_name, pk=journey.pk, data=data) 125 126 self.response_302(response=response) 126 127 journey = Journey.objects.get(pk=journey.pk) 127 - self.assertEquals(RETURN, journey.kind) 128 + self.assertEquals(data["free_places"], journey.free_places) 128 129 129 130 def test_get_recommended_journey(self): 130 131 user = self.make_user(username="foo") ··· 152 153 153 154 def test_post_join(self): 154 155 user = self.make_user(username="foo") 155 - journey = JourneyFactory(user=user, kind=GOING) 156 + template = JourneyTemplateFactory(user=user, kind=GOING) 157 + journey = JourneyFactory(template=template) 156 158 url_name = "journeys:join" 157 159 self.assertLoginRequired(url_name, pk=journey.pk) 158 160 with self.login(user): ··· 161 163 162 164 def test_post_join_recurrence_all(self): 163 165 user = self.make_user(username="foo") 164 - journey = JourneyFactory(user=user, kind=GOING) 165 - journeys = [JourneyFactory(user=user, kind=GOING, parent=journey) for _ in range(10)] 166 + template = JourneyTemplateFactory(user=user, kind=GOING) 167 + journey = JourneyFactory(template=template) 168 + journeys = [JourneyFactory(template=template) for _ in range(10)] 166 169 url_name = "journeys:join" 167 170 self.assertLoginRequired(url_name, pk=journey.pk) 168 171 with self.login(user): ··· 175 178 176 179 def test_post_join_recurrence_one(self): 177 180 user = self.make_user(username="foo") 178 - journey = JourneyFactory(user=user, kind=GOING) 179 - [JourneyFactory(user=user, kind=GOING, parent=journey) for _ in range(10)] 181 + template = JourneyTemplateFactory(user=user, kind=GOING) 182 + journey = JourneyFactory(template=template) 183 + [JourneyFactory(template=template) for _ in range(10)] 180 184 url_name = "journeys:join" 181 185 self.assertLoginRequired(url_name, pk=journey.pk) 182 186 with self.login(user): ··· 189 193 190 194 def test_post_leave(self): 191 195 user = self.make_user(username="foo") 192 - journey = JourneyFactory(user=user, kind=GOING) 196 + journey = JourneyFactory(template=JourneyTemplateFactory(user=user)) 193 197 journey.join_passenger(user) 194 198 url_name = "journeys:leave" 195 199 self.assertLoginRequired(url_name, pk=journey.pk) ··· 199 203 200 204 def test_post_throw_out(self): 201 205 user = self.make_user(username="foo") 202 - journey = JourneyFactory(user=user, kind=GOING) 206 + journey = JourneyFactory(template=JourneyTemplateFactory(user=user)) 203 207 passenger = journey.join_passenger(user) 204 208 url_name = "journeys:throw-out" 205 209 self.assertLoginRequired(url_name, pk=passenger.pk) ··· 209 213 210 214 def test_cancel_journey(self): 211 215 user = self.make_user(username="foo") 212 - journey = JourneyFactory(user=user) 216 + template = JourneyTemplateFactory(user=user) 217 + journey = JourneyFactory(template=template) 213 218 url_name = "journeys:cancel" 214 219 self.assertLoginRequired(url_name, pk=journey.pk) 215 220 with self.login(user): ··· 218 223 219 224 def test_post_cancel_journey(self): 220 225 user = self.make_user(username="foo") 221 - journey = JourneyFactory(user=user) 226 + template = JourneyTemplateFactory(user=user) 227 + journey = JourneyFactory(template=template) 222 228 url_name = "journeys:cancel" 223 229 self.assertLoginRequired(url_name, pk=journey.pk) 224 230 with self.login(user): ··· 228 234 229 235 def test_delete_journey(self): 230 236 user = self.make_user(username="foo") 231 - journeys = [JourneyFactory(user=user) for _ in range(10)] 232 - journey = JourneyFactory(user=user) 237 + journeys = [JourneyFactory(template=JourneyTemplateFactory(user=user)) for _ in range(10)] 238 + journey = JourneyFactory(template=JourneyTemplateFactory(user=user)) 233 239 url_name = "journeys:delete" 234 240 self.assertLoginRequired(url_name, pk=journey.pk) 235 241 self.assertEquals(len(journeys) + 1, Journey.objects.count()) ··· 241 247 242 248 def test_delete_parent_journeys(self): 243 249 user = self.make_user(username="foo") 244 - journey = JourneyFactory(user=user) 245 - journeys = [JourneyFactory(user=user, parent=journey) for _ in range(10)] 250 + template = JourneyTemplateFactory(user=user) 251 + journey = JourneyFactory(template=template) 252 + journeys = [JourneyFactory(template=template) for _ in range(10)] 246 253 url_name = "journeys:delete" 247 254 self.assertLoginRequired(url_name, pk=journey.pk) 248 255 self.assertEquals(len(journeys) + 1, Journey.objects.count()) ··· 254 261 255 262 def test_delete_all_journeys(self): 256 263 user = self.make_user(username="foo") 257 - journey = JourneyFactory(user=user) 258 - journeys = [JourneyFactory(user=user, parent=journey) for _ in range(10)] 264 + template = JourneyTemplateFactory(user=user) 265 + journey = JourneyFactory(template=template) 266 + journeys = [JourneyFactory(template=template) for _ in range(10)] 259 267 other_journeys = [JourneyFactory() for _ in range(5)] 260 268 url_name = "journeys:delete-all" 261 269 self.assertLoginRequired(url_name, pk=journey.pk)
+25 -25
upvcarshare/journeys/views/journeys.py
··· 10 10 from django.http import Http404 11 11 from django.shortcuts import render, redirect, get_object_or_404 12 12 from django.utils import timezone 13 - from django.views.generic import View 14 - from django.utils.translation import ugettext_lazy as _ 15 13 from django.utils.timezone import make_naive 14 + from django.utils.translation import ugettext_lazy as _ 15 + from django.views.generic import View 16 16 17 17 from journeys import GOING 18 18 from journeys.exceptions import AlreadyAPassenger, NoFreePlaces, NotAPassenger 19 - from journeys.forms import SmartJourneyForm, JourneyForm, FilterForm, \ 20 - ConfirmRejectJourneyForm, SearchJourneyForm 19 + from journeys.forms import SmartJourneyTemplateForm, JourneyForm, FilterForm, \ 20 + ConfirmRejectJourneyForm, SearchJourneyForm 21 21 from journeys.models import Residence, Campus, Journey, Passenger 22 22 23 23 ··· 25 25 """View to show journey creation form and to handle its creation.""" 26 26 27 27 template_name = "journeys/create.smart.html" 28 - form = SmartJourneyForm 28 + form = SmartJourneyTemplateForm 29 29 30 30 def get(self, request): 31 31 residences = Residence.objects.filter(user=request.user) ··· 42 42 data = { 43 43 "form": form 44 44 } 45 + # Warnings 46 + if request.user.transports.count() == 0: 47 + messages.warning(request, _("Parece que no has creado ningún medio de transporte, " 48 + "si quieres crear un viaje y que otros compañeros puedan " 49 + "apuntarse, tienes que registrar antes un medio de transporte.")) 50 + if request.user.residences.count() == 0: 51 + messages.warning(request, _("Parece que no has especificado desde donde o hasta donde sueles viajar. " 52 + "Para dar de alta un viaje deberás antes registrar al menos un lugar para usar " 53 + "como origen o destino.")) 45 54 return render(request, self.template_name, data) 46 55 47 56 def post(self, request): ··· 61 70 template_name = "journeys/edit.html" 62 71 63 72 def get(self, request, pk): 64 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 73 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 65 74 form = JourneyForm( 66 75 instance=journey, 67 76 initial={"i_am_driver": journey.driver is not None and journey.driver == request.user}, ··· 74 83 return render(request, self.template_name, data) 75 84 76 85 def post(self, request, pk): 77 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 86 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 78 87 form = JourneyForm(request.POST, instance=journey, user=request.user) 79 88 data = { 80 89 "form": form, ··· 113 122 "fulfilled_by": journey.fulfilled_by(), 114 123 "passengers": journey.passengers_list(request.user), 115 124 "recommended": journey.recommended(), 116 - "has_recurrence": journey.parent is not None or journey.children.exists() 125 + "has_recurrence": journey.has_recurrence 117 126 } 118 127 return render(request, self.template_name, data) 119 128 ··· 145 154 template_name = "journeys/list.html" 146 155 147 156 def get(self, request): 148 - journeys = Journey.objects.filter(user=request.user).order_by("departure") 157 + journeys = Journey.objects.filter(template__user=request.user).order_by("departure") 149 158 data = { 150 159 "journeys": journeys, 151 160 "journeys_count": journeys.count() ··· 269 278 template_name = "journeys/cancel.html" 270 279 271 280 def get(self, request, pk): 272 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 281 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 273 282 data = { 274 283 "journey": journey, 275 284 } ··· 277 286 278 287 @staticmethod 279 288 def post(request, pk): 280 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 289 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 281 290 journey.cancel() 282 291 return redirect("journeys:details", pk=journey.pk) 283 292 ··· 287 296 288 297 @staticmethod 289 298 def get(request, pk): 290 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 299 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 291 300 # Delete only if the journey hasn't driver 292 301 if journey.driver is None: 293 - if journey.has_recurrence and journey.children.exists(): 294 - new_parent = journey.children.order_by("departure").first() 295 - journey.children.exclude(pk=new_parent.pk).update(parent=new_parent) 296 - new_parent.parent = None 297 - new_parent.save() 298 - # Get again the journey 299 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 300 302 journey.delete() 301 303 messages.success(request, _('Has borrado el viaje')) 302 304 return redirect("journeys:list") ··· 309 311 310 312 @staticmethod 311 313 def get(request, pk): 312 - journey = get_object_or_404(Journey, pk=pk, user=request.user) 314 + journey = get_object_or_404(Journey, pk=pk, template__user=request.user) 313 315 # Delete only if the journey hasn't driver 314 316 if journey.driver is None: 315 317 if journey.has_recurrence: 316 - if journey.parent is not None: 317 - journeys = journey.parent.children.filter(departure__gte=journey.departure) 318 - journeys.delete() 319 - else: 320 - journey.delete() 318 + # If has recurrente, delete all future brothers 319 + journeys = journey.brothers().filter(departure__gte=journey.departure) 320 + journeys.delete() 321 321 else: 322 322 journey.delete() 323 323 messages.success(request, _('Has borrado el viaje y sus repeticiones'))
+4 -2
upvcarshare/notifications/tests/test_api.py
··· 8 8 from rest_framework.test import APITestCase 9 9 10 10 from journeys import GOING 11 - from journeys.tests.factories import CampusFactory, JourneyFactory 11 + from journeys.tests.factories import CampusFactory, JourneyFactory, JourneyTemplateFactory 12 12 from journeys.tests.factories import ResidenceFactory 13 13 from notifications import JOIN 14 14 from notifications import LEAVE ··· 30 30 user = UserFactory() if user is None else user 31 31 origin = ResidenceFactory(user=user) 32 32 destination = CampusFactory() 33 - return JourneyFactory(user=user, residence=origin, campus=destination, kind=kind) 33 + return JourneyFactory( 34 + template=JourneyTemplateFactory(user=user, residence=origin, campus=destination, kind=kind) 35 + ) 34 36 35 37 def test_get_notifications(self): 36 38 user = UserFactory()
+3 -2
upvcarshare/notifications/tests/test_models.py
··· 4 4 from test_plus import TestCase 5 5 6 6 from journeys import GOING 7 - from journeys.tests.factories import CampusFactory 7 + from journeys.tests.factories import CampusFactory, JourneyTemplateFactory 8 8 from journeys.tests.factories import ResidenceFactory, JourneyFactory 9 9 from notifications import JOIN, LEAVE 10 10 from notifications.models import Notification ··· 25 25 user = UserFactory() if user is None else user 26 26 origin = ResidenceFactory(user=user) 27 27 destination = CampusFactory() 28 - return JourneyFactory(user=user, residence=origin, campus=destination, kind=kind) 28 + template = JourneyTemplateFactory(user=user, residence=origin, campus=destination, kind=kind) 29 + return JourneyFactory(template=template) 29 30 30 31 def test_join_generation(self): 31 32 initial_user = UserFactory()
+14
upvcarshare/static/src/js/journeys/components/rrules.js
··· 1 + import RRulesController from '../controllers/rrules'; 2 + 3 + 4 + const RRulesComponent = { 5 + controller: RRulesController, 6 + templateUrl: "/partials/journeys/rrules.html", 7 + bindings: { 8 + fieldName: '@', 9 + fieldId: '@', 10 + overrideDeparture: '<' 11 + } 12 + }; 13 + 14 + export default RRulesComponent;
+134
upvcarshare/static/src/js/journeys/controllers/rrules.js
··· 1 + // Controlle for RRule component 2 + import RRule from 'rrule'; 3 + import { SPANISH, gettext } from './rrules_i18n'; 4 + 5 + 6 + class RRulesController { 7 + 8 + constructor($scope) { 9 + this.$scope = $scope; 10 + } 11 + 12 + $onInit() { 13 + this.rrule = null; 14 + this.rruleString = null; 15 + this.rruleText = null; 16 + this.previousRRule = null; 17 + this.isActivated = false; 18 + this.dtstart = new Date(); 19 + 20 + this.freqOptions = [ 21 + {label: "Cada año", value: RRule.YEARLY}, 22 + {label: "Cada mes", value: RRule.MONTHLY}, 23 + {label: "Cada semana", value: RRule.WEEKLY}, 24 + {label: "Cada día", value: RRule.DAILY} 25 + ] 26 + 27 + this.byweekdayOptions = [ 28 + {label: "L", value: RRule.MO, model: false}, 29 + {label: "M", value: RRule.TU, model: false}, 30 + {label: "X", value: RRule.WE, model: false}, 31 + {label: "J", value: RRule.TH, model: false}, 32 + {label: "V", value: RRule.FR, model: false}, 33 + {label: "S", value: RRule.SA, model: false}, 34 + {label: "D", value: RRule.SU, model: false}, 35 + ] 36 + 37 + this.wkstOptions = [ 38 + {label: "L", value: RRule.MO, model: false}, 39 + {label: "M", value: RRule.TU, model: false}, 40 + {label: "X", value: RRule.WE, model: false}, 41 + {label: "J", value: RRule.TH, model: false}, 42 + {label: "V", value: RRule.FR, model: false}, 43 + {label: "S", value: RRule.SA, model: false}, 44 + {label: "D", value: RRule.SU, model: false}, 45 + ] 46 + 47 + this.intervalOptions = [...Array(29).keys()].map(x => x + 1); 48 + this.intervalLabel = null; 49 + 50 + this.ends = "never"; 51 + } 52 + 53 + $doCheck() { 54 + if (!this.isActivated) { 55 + this.previousRRule = this.rrule; 56 + this.rrule = null; 57 + } else if (this.rrule === null) { 58 + this.rrule = this.previousRRule !== null ? previousRRule : this.initialRRule(); 59 + } 60 + if (this.rrule !== null) { 61 + this.updateIntervalLabel(); 62 + this.updateByweekday(); 63 + this.updateEnds(); 64 + this.updateRRule(); 65 + } 66 + } 67 + 68 + $onChanges(changesObj) { 69 + this.dtstart = this.overrideDeparture; 70 + if (this.rrule !== null && this.rrule !== undefined) { 71 + this.rrule.dtstart = this.overrideDeparture; 72 + this.updateRRule(); 73 + } 74 + } 75 + 76 + updateEnds() { 77 + if (this.rrule !== null && this.rrule !== undefined) { 78 + if (this.ends == "never") { 79 + this.rrule.count = null; 80 + this.rrule.until = null; 81 + } else if (this.ends == "count") { 82 + this.rrule.until = null; 83 + if (this.rrule.count == null) this.rrule.count = 1; 84 + } else if (this.ends == "until") { 85 + this.rrule.count = null; 86 + } 87 + } 88 + } 89 + 90 + updateByweekday() { 91 + let byweekday = []; 92 + this.byweekdayOptions.map((option) => { 93 + if (option.model) { 94 + byweekday.push(option.value); 95 + } 96 + }) 97 + this.rrule.byweekday = byweekday; 98 + } 99 + 100 + updateIntervalLabel() { 101 + if (this.rrule.freq == RRule.YEARLY) this.intervalLabel = "años"; 102 + if (this.rrule.freq == RRule.MONTHLY) this.intervalLabel = "meses"; 103 + if (this.rrule.freq == RRule.WEEKLY) this.intervalLabel = "semanas"; 104 + if (this.rrule.freq == RRule.DAILY) this.intervalLabel = "días"; 105 + } 106 + 107 + updateRRule() { 108 + if (this.rrule !== null) { 109 + const rrule = new RRule(this.rrule); 110 + this.rruleString = rrule.toString(); 111 + this.rruleText = rrule.toText(gettext, SPANISH); 112 + } 113 + } 114 + 115 + initialRRule() { 116 + return { 117 + freq: this.freqOptions[2].value, 118 + byweekday: null, 119 + dtstart: this.dtstart, 120 + count: null, 121 + until: null 122 + } 123 + } 124 + hiddeByweekday() { 125 + if (this.rrule !== null) { 126 + return this.rrule.freq !== RRule.WEEKLY; 127 + } 128 + return false; 129 + } 130 + } 131 + 132 + RRulesController.$inject = ['$scope']; 133 + 134 + export default RRulesController;
+87
upvcarshare/static/src/js/journeys/controllers/rrules_i18n.js
··· 1 + // Translations for rrules.js 2 + 3 + export const SPANISH = { 4 + dayNames: [ 5 + 'domingo', 'lunes', 'martes', 'miércoles', 6 + 'jueves', 'viernes', 'sábado' 7 + ], 8 + monthNames: [ 9 + 'enero', 'febrero', 'marzo', 'abril', 'mayo', 10 + 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 11 + 'noviembre', 'diciembre' 12 + ], 13 + tokens: { 14 + 'SKIP': /^[ \r\n\t]+|^\.$/, 15 + 'number': /^[1-9][0-9]*/, 16 + 'numberAsText': /^(one|two|three)/i, 17 + 'every': /^every/i, 18 + 'day(s)': /^days?/i, 19 + 'weekday(s)': /^weekdays?/i, 20 + 'week(s)': /^weeks?/i, 21 + 'hour(s)': /^hours?/i, 22 + 'month(s)': /^months?/i, 23 + 'year(s)': /^years?/i, 24 + 'on': /^(on|in)/i, 25 + 'at': /^(at)/i, 26 + 'the': /^the/i, 27 + 'first': /^first/i, 28 + 'second': /^second/i, 29 + 'third': /^third/i, 30 + 'nth': /^([1-9][0-9]*)(\.|th|nd|rd|st)/i, 31 + 'last': /^last/i, 32 + 'for': /^for/i, 33 + 'time(s)': /^times?/i, 34 + 'until': /^(un)?til/i, 35 + 'monday': /^mo(n(day)?)?/i, 36 + 'tuesday': /^tu(e(s(day)?)?)?/i, 37 + 'wednesday': /^we(d(n(esday)?)?)?/i, 38 + 'thursday': /^th(u(r(sday)?)?)?/i, 39 + 'friday': /^fr(i(day)?)?/i, 40 + 'saturday': /^sa(t(urday)?)?/i, 41 + 'sunday': /^su(n(day)?)?/i, 42 + 'january': /^jan(uary)?/i, 43 + 'february': /^feb(ruary)?/i, 44 + 'march': /^mar(ch)?/i, 45 + 'april': /^apr(il)?/i, 46 + 'may': /^may/i, 47 + 'june': /^june?/i, 48 + 'july': /^july?/i, 49 + 'august': /^aug(ust)?/i, 50 + 'september': /^sep(t(ember)?)?/i, 51 + 'october': /^oct(ober)?/i, 52 + 'november': /^nov(ember)?/i, 53 + 'december': /^dec(ember)?/i, 54 + 'comma': /^(,\s*|(and|or)\s*)+/i 55 + } 56 + } 57 + 58 + const esStrings = { 59 + 'every': 'cada', 60 + 'day': 'día', 61 + 'days': 'días', 62 + 'weekday': 'día laborable', 63 + 'week': 'semana', 64 + 'weeks': 'semanas', 65 + 'hour': 'hora', 66 + 'hours': 'hora', 67 + 'month': 'mes', 68 + 'months': 'meses', 69 + 'year': 'año', 70 + 'years': 'años', 71 + 'on': 'los', 72 + 'at': 'en', 73 + 'the': 'el', 74 + 'first': 'primero', 75 + 'second': 'segundo', 76 + 'third': 'tercer', 77 + 'last': 'último', 78 + 'for': 'por', 79 + 'time': 'vez', 80 + 'times': 'veces', 81 + 'until': 'hasta', 82 + }; 83 + 84 + export function gettext(id) { 85 + // Returns es string, default to english 86 + return esStrings[id] || id; 87 + }
+18 -3
upvcarshare/static/src/js/journeys/index.js
··· 1 1 import angular from 'angular'; 2 2 import JourneyService from './journeys.service'; 3 - import {OriginDestinationSelectComponent, DatetimeComponent, DateComponent, TimeComponent, CalendarComponent, CircleMapComponent, RecurrenceCalendarComponent} from './journeys.component'; 4 - import {JourneyForm, JoinJourneyForm, SearchJourneyForm, ConfirmPassengerForm, 5 - RejectPassengerForm} from './journey.directive'; 3 + import { 4 + OriginDestinationSelectComponent, 5 + DatetimeComponent, 6 + DateComponent, 7 + TimeComponent, 8 + CalendarComponent, 9 + CircleMapComponent, 10 + RecurrenceCalendarComponent 11 + } from './journeys.component'; 12 + import RRulesComponent from './components/rrules'; 13 + import { 14 + JourneyForm, 15 + JoinJourneyForm, 16 + SearchJourneyForm, 17 + ConfirmPassengerForm, 18 + RejectPassengerForm 19 + } from './journey.directive'; 6 20 import JoinAllOneController from './journeys.controller'; 7 21 8 22 import 'lodash'; ··· 33 47 .component('calendar', CalendarComponent) 34 48 .component('circleMap', CircleMapComponent) 35 49 .component('recurrenceCalendar', RecurrenceCalendarComponent) 50 + .component('rRules', RRulesComponent) 36 51 37 52 .directive('journeyForm', JourneyForm) 38 53 .directive('searchJourneyForm', SearchJourneyForm)
+3 -2
upvcarshare/static/src/js/journeys/journey.directive.js
··· 21 21 link: (scope, element, attr) => { 22 22 scope.iAmDriver = "False"; 23 23 scope.newArrivalValue = null; 24 + scope.newDepartureValue = null; 24 25 25 26 scope.onUpdateDeparture = (value) => { 26 - // console.log("Updated departure: ", value); 27 - var newValue = moment(value).add(30, 'm'); 27 + scope.newDepartureValue = value; 28 + let newValue = moment(value).add(30, 'm'); 28 29 scope.newArrivalValue = newValue.toDate(); 29 30 }; 30 31
+9 -2
upvcarshare/static/src/js/journeys/journeys.component.js
··· 1 1 // Component for select origin and destination of a journey. 2 - import {OriginDestinationSelectController, DatetimeController, DateController, TimeController, CalendarController, CircleMapController, RecurrenceCalendarController} 3 - from './journeys.controller'; 2 + import { 3 + OriginDestinationSelectController, 4 + DatetimeController, 5 + DateController, 6 + TimeController, 7 + CalendarController, 8 + CircleMapController, 9 + RecurrenceCalendarController 10 + } from './journeys.controller'; 4 11 5 12 6 13 const OriginDestinationSelectComponent = {
+1 -1
upvcarshare/static/src/js/journeys/journeys.controller.js
··· 94 94 } 95 95 96 96 $onInit() { 97 - var date = this.value !== undefined ? moment(this.value).toDate() : new Date(); 97 + const date = this.value !== undefined ? moment(this.value).toDate() : new Date(); 98 98 this.picker = { 99 99 date: date, 100 100 open: false,
+1 -1
upvcarshare/static/src/sass/variables.scss
··· 21 21 sm: 540px, 22 22 md: 720px, 23 23 lg: 950px, 24 - xl: 950px 24 + xl: 1000px 25 25 ) !default;
+5 -1
upvcarshare/templates/header.html
··· 27 27 <nav class="navbar navbar-full navbar-dark bg-upv-dark-grey"> 28 28 {% if request.user.is_authenticated %} 29 29 <ul class="nav navbar-nav pull-xs-left"> 30 + <li class="nav-item {% add_active_class "journeys:list" %}"> 31 + <a class="nav-link" href="{% url "journeys:list" %}"> 32 + {% trans "Mis viajes" %} 33 + </a> 34 + </li> 30 35 <li class="nav-item {% add_active_class "notifications:list" %}"> 31 36 <a class="nav-link" href="{% url "notifications:list" %}"> 32 37 {% trans "Notificaciones" %} ··· 58 63 <div class="dropdown-menu"> 59 64 <a class="dropdown-item" href="{% url "users:edit" %}">{% trans "Editar perfil" %}</a> 60 65 <div class="dropdown-divider"></div> 61 - <a class="dropdown-item" href="{% url "journeys:list" %}">{% trans "Mis viajes" %}</a> 62 66 <a class="dropdown-item" href="{% url "journeys:residences" %}">{% trans "Mis lugares" %}</a> 63 67 <a class="dropdown-item" href="{% url "journeys:transports" %}">{% trans "Mis transportes" %}</a> 64 68 <div class="dropdown-divider"></div>
+4 -2
upvcarshare/templates/journeys/create.smart.html
··· 48 48 {% endblock %} 49 49 </div> 50 50 </div> 51 - {% elif field.name == "transport" %} 51 + {% elif field.name == "transport" %} 52 52 <div class="row"> 53 53 <div class="col-sm-7"> 54 54 {{ field }} ··· 61 61 </div> 62 62 {% elif field.name == "departure" %} 63 63 <journey-datetime field-name="{{ field.name }}" field-id="{{ field.auto_id }}" value="{{ field.value|date:"c" }}" on-update="onUpdateDeparture(value)"></journey-datetime> 64 + <r-rules field-id="id_recurrence" field-name="recurrence" override-departure="newDepartureValue"></r-rules> 64 65 {% elif field.name == "arrival" %} 65 66 <journey-datetime field-name="{{ field.name }}" field-id="{{ field.auto_id }}" value="{{ field.value|date:"c" }}" override-value="newArrivalValue"></journey-datetime> 67 + {% elif field.name == "recurrence" %} 66 68 {% else %} 67 69 {{ field }} 68 70 {% endif %} ··· 73 75 {% endif %} 74 76 {% endfor %} 75 77 <div class="form-group"> 76 - <div class="col-sm-2 pull-right"> 78 + <div class="offset-sm-4 col-sm-8"> 77 79 <button type="submit" class="btn btn-success">{% trans "Guardar" %}</button> 78 80 <a href="{% url "journeys:list" %}" class="btn btn-danger">{% trans "Volver" %}</a> 79 81 </div>
+64
upvcarshare/templates/partials/journeys/rrules.html
··· 1 + {% load i18n %} 2 + <div class="row"> 3 + <div class="col-xs-12"> 4 + <input type="hidden" name="[[$ctrl.fieldName]]" id="[[$ctrl.fieldId]]" ng-value="$ctrl.rruleString"/> 5 + <label class="form-check-label"> 6 + <input type="checkbox" ng-model="$ctrl.isActivated" /> {% trans "Quiero realizar el viaje más de una vez" %} 7 + </label> 8 + </div> 9 + </div> 10 + <div class="row" ng-class="{'hidden-xs-up': !$ctrl.isActivated}" style="margin-top: 15px"> 11 + <div class="col-xs-12"> 12 + <label>{% trans "Se repite:" %}</label> 13 + <select ng-init="$ctrl.rrule.freq = $ctrl.freqOptions[2].value" 14 + ng-model="$ctrl.rrule.freq" 15 + ng-options="freqOption.value as freqOption.label for freqOption in $ctrl.freqOptions"> 16 + > 17 + </select> 18 + </div> 19 + <div class="col-xs-12"> 20 + <label>{% trans "Repetir cada:" %}</label> 21 + <select ng-model="$ctrl.rrule.interval" 22 + ng-options="intervalOption as intervalOption for intervalOption in $ctrl.intervalOptions"> 23 + > 24 + </select> 25 + [[$ctrl.intervalLabel]] 26 + </div> 27 + <div class="col-xs-12" ng-class="{'hidden-xs-up': $ctrl.hiddeByweekday()}"> 28 + <label>{% trans "Repetir el:" %}</label> 29 + <label class="form-check-label" ng-repeat="byweekdayOption in $ctrl.byweekdayOptions"> 30 + <input type="checkbox" 31 + ng-model="byweekdayOption.model" 32 + /> [[byweekdayOption.label]] 33 + </label> 34 + </div> 35 + <div class="col-xs-12"> 36 + <div class="row"> 37 + <div class="col-xs-1"> 38 + <label>{% trans "Finaliza:" %}</label> 39 + </div> 40 + <div class="col-xs-11"> 41 + <ul class="list-unstyled"> 42 + <li> 43 + <label> 44 + <input type="radio" name="ends" ng-model="$ctrl.ends" value="never"> {% trans "Nunca" %} 45 + </label> 46 + </li> 47 + <li> 48 + <label> 49 + <input type="radio" name="ends" ng-model="$ctrl.ends" value="count"> {% trans "Al cabo de" %} <input type="text" ng-model="$ctrl.rrule.count" class="form-control" style="width: 40px; display: inline-block"> {% trans "repeticiones" %} 50 + </label> 51 + </li> 52 + <li> 53 + <label> 54 + <input type="radio" name="ends" ng-model="$ctrl.ends" value="until"> {% trans "El" %} <input type="date" ng-model="$ctrl.rrule.until" class="form-control" style="width: 150px; display: inline-block"> 55 + </label> 56 + </li> 57 + </ul> 58 + </div> 59 + </div> 60 + </div> 61 + <div class="col-xs-12"> 62 + <p><strong>Resumen: [[$ctrl.rruleText]]</strong></p> 63 + </div> 64 + </div>
+1 -1
upvcarshare/users/tests/test_api.py
··· 42 42 self.assertEqual(response.status_code, status.HTTP_200_OK) 43 43 user = User.objects.get(pk=user.pk) 44 44 self.assertEquals( 45 - "SRID=4326;POINT (-0.3767699999989411 39.46913999999702)", 45 + "SRID=4326;POINT (-0.3767699999999994 39.4691399999998)", 46 46 six.text_type(user.get_default_position_wgs84()) 47 47 ) 48 48 self.assertEquals("foo", user.default_address)