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.

wip: journey template

+138 -87
+2 -2
upvcarshare/journeys/api/v1/resources.py
··· 77 77 def get_queryset(self): 78 78 queryset = super(JourneyResource, self).get_queryset() 79 79 if self.request.query_params.get('owned'): 80 - queryset = queryset.filter(user=self.request.user) 80 + queryset = queryset.filter(template__user=self.request.user) 81 81 if self.request.query_params.get('joined'): 82 82 queryset = queryset.filter(passengers__user=self.request.user) 83 83 return queryset ··· 231 231 def recurrence(self, request, **kwargs): 232 232 pk = kwargs.get('pk', None) 233 233 journey = get_object_or_404(Journey, pk=pk) 234 - queryset = journey.recurrence_journeys() 234 + queryset = journey.brothers() 235 235 page = self.paginate_queryset(queryset) 236 236 if page is not None: 237 237 serializer = self.get_serializer(page, many=True)
+33 -29
upvcarshare/journeys/managers.py
··· 21 21 :param journey: 22 22 :param override_distance: 23 23 """ 24 - key = "residence{}" if journey.kind == GOING else "campus{}" 24 + key = "template__residence{}" if journey.kind == GOING else "template__campus{}" 25 25 distance = getattr(journey, key.format("")).distance if override_distance is None else override_distance 26 26 return { 27 27 key.format("__position__distance_lte"): ( ··· 47 47 ) 48 48 49 49 50 - class JourneyQuerySet(models.QuerySet): 51 - 52 - def visible(self, user=None): 53 - """Journey visible for the given user.""" 54 - if user is not None: 55 - return self.filter(user__groups=user.groups.all()) 56 - return self 57 - 58 - 59 - class JourneyManager(models.GeoManager): 60 - """Manager for Journeys.""" 61 - 62 - def get_queryset(self): 63 - return JourneyQuerySet(self.model, using=self._db) 64 - 65 - def visible(self, user=None): 66 - """Journey visible for the given user.""" 67 - return self.get_queryset().visible(user=user) 50 + class JourneyTemplateManager(models.GeoManager): 68 51 69 52 def smart_create(self, user, origin, destination, departure, transport=None): 70 53 """Enhanced method to create journeys""" ··· 86 69 data["free_places"] = transport.default_places 87 70 return self.create(**data) 88 71 72 + 73 + class JourneyQuerySet(models.QuerySet): 74 + 75 + def visible(self, user=None): 76 + """Journey visible for the given user.""" 77 + if user is not None: 78 + return self.filter(user__groups=user.groups.all()) 79 + return self 80 + 81 + 82 + class JourneyManager(models.GeoManager): 83 + """Manager for Journeys.""" 84 + 85 + def get_queryset(self): 86 + return JourneyQuerySet(self.model, using=self._db) 87 + 88 + def visible(self, user=None): 89 + """Journey visible for the given user.""" 90 + return self.get_queryset().visible(user=user) 91 + 89 92 def available(self, user=None, kind=None, ignore_full=False): 90 93 """Gets all available journeys. 91 94 :param user: ··· 93 96 :param ignore_full: 94 97 """ 95 98 now = timezone.now() 96 - queryset = self.visible(user).filter(driver__isnull=False, departure__gt=now) 99 + queryset = self.visible(user).filter(template__driver__isnull=False, departure__gt=now) 97 100 if kind is not None: 98 - queryset = queryset.filter(kind=kind) 101 + queryset = queryset.filter(template__kind=kind) 99 102 if ignore_full: 100 103 return queryset 101 104 # NOTE: annotate QuerySet method has problems with Oracle, so, we have to ··· 117 120 nearby = nearby.filter(**{key: (geometry, distance)}) 118 121 else: 119 122 nearby = nearby.filter( 120 - Q(residence__position__distance_lte=(geometry, distance)) | 121 - Q(campus__position__distance_lte=(geometry, distance)) 123 + Q(template__residence__position__distance_lte=(geometry, distance)) | 124 + Q(template__campus__position__distance_lte=(geometry, distance)) 122 125 ) 123 126 return nearby 124 127 ··· 128 131 :param user: 129 132 """ 130 133 now = timezone.now() 131 - queryset = self.filter(user=user, driver__isnull=True, departure__gt=now) 134 + queryset = self.filter(template__user=user, template__driver__isnull=True, departure__gt=now) 132 135 if kind is not None: 133 136 queryset = queryset.filter(kind=kind) 134 137 return queryset ··· 152 155 if not conditions: 153 156 return self.none() 154 157 now = timezone.now() 155 - queryset = self.available(user=user, kind=kind, ignore_full=ignore_full).exclude(user=user, departure__lt=now)\ 158 + queryset = self.available(user=user, kind=kind, ignore_full=ignore_full)\ 159 + .exclude(template__user=user, departure__lt=now)\ 156 160 .filter(reduce(lambda x, y: x | y, conditions))\ 157 161 .order_by("departure") 158 162 return queryset ··· 177 181 kinds = [GOING, RETURN] 178 182 conditions = [] 179 183 for kind in kinds: 180 - key = "residence{}" if kind == GOING else "campus{}" 184 + key = "template__residence{}" if kind == GOING else "template__campus{}" 181 185 conditions.append(Q(**{ 182 186 key.format("__position__distance_lte"): ( 183 187 position, ··· 188 192 })) 189 193 now = timezone.now() 190 194 queryset = self.available(user=user, ignore_full=ignore_full)\ 191 - .exclude(user=user, departure__lt=now, disabled=True) \ 195 + .exclude(template__user=user, departure__lt=now, disabled=True) \ 192 196 .filter(reduce(lambda x, y: x | y, conditions)) \ 193 197 .order_by("departure") 194 198 return queryset 195 199 196 200 def overlaps(self, user, departure, time_window): 197 201 """Returns a queryset with the overlapping journeys.""" 198 - return self.filter(user=user).filter( 202 + return self.filter(template__user=user).filter( 199 203 departure__gte=(departure - datetime.timedelta(minutes=time_window)), 200 204 departure__lte=(departure + datetime.timedelta(minutes=time_window)) 201 205 ) ··· 214 218 :param journey: 215 219 """ 216 220 if journey is None: 217 - return self.filter(Q(journey__user=user) | Q(journey__passengers__user=user)) 221 + return self.filter(Q(journey__template__user=user) | Q(journey__passengers__user=user)) 218 222 if not journey.is_messenger_allowed(user): 219 223 return self.none() 220 224 return self.filter(journey=journey)
+103 -56
upvcarshare/journeys/models.py
··· 111 111 112 112 113 113 @python_2_unicode_compatible 114 - class Journey(GisTimeStampedModel): 115 - """A model class to represent a journey between two node.""" 114 + class JourneyTemplate(GisTimeStampedModel): 115 + """A journey template is used to create a serie of journeys using the reference of itself.""" 116 + 117 + # Owner of the template 116 118 user = models.ForeignKey( 117 119 settings.AUTH_USER_MODEL, related_name="journeys", help_text=_("user who creates the journey") 118 120 ) 121 + 122 + # Data about the driver 119 123 driver = models.ForeignKey( 120 124 settings.AUTH_USER_MODEL, null=True, blank=True, help_text=_("user who drives during the journey") 121 125 ) 126 + 127 + # Data about origin and destination, and time window to make its 128 + # journeys visibles. 122 129 residence = models.ForeignKey( 123 130 "journeys.Residence", 124 131 verbose_name=_("lugar"), ··· 129 136 verbose_name=_("campus"), 130 137 related_name="journeys" 131 138 ) 132 - kind = models.PositiveIntegerField(choices=JOURNEY_KINDS, verbose_name=_("tipo de viaje")) 133 - free_places = models.PositiveIntegerField(default=4, verbose_name=_("plazas libres"), blank=True, null=True) 134 - total_passengers = models.PositiveIntegerField(default=0, verbose_name=_("total pasajeros"), blank=True, null=True) 135 - departure = models.DateTimeField(verbose_name=_("fecha y hora de salida*")) 136 - arrival = models.DateTimeField(verbose_name=_("fecha y hora de llegada estimada*"), null=True, blank=True) 137 139 time_window = models.PositiveIntegerField( 138 140 verbose_name=_("ventana de tiempo"), 139 141 help_text=_("Se buscarán por los viajes que salgan hasta con estos minutos de antelación"), 140 142 default=DEFAULT_TIME_WINDOW, 141 143 blank=True 142 144 ) 145 + 146 + # Data about recurrence 147 + recurrence = RecurrenceField(verbose_name=_("¿Vas a realizar este viaje más de una vez?"), null=True, blank=True) 148 + 149 + # Kind of the journey 150 + kind = models.PositiveIntegerField(choices=JOURNEY_KINDS, verbose_name=_("tipo de viaje")) 151 + 152 + # Transport used in this journeys 143 153 transport = models.ForeignKey( 144 154 "journeys.Transport", 145 155 related_name="journeys", ··· 147 157 null=True, 148 158 blank=True 149 159 ) 160 + 161 + def create_journey(self, departure, arrival): 162 + """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 + ) 170 + journey.save() 171 + return journey 172 + 173 + def save(self, **kwargs): 174 + """Override save to set the default time window value. Default value not set in Oracle.""" 175 + if not self.time_window: 176 + self.time_window = DEFAULT_TIME_WINDOW 177 + return super(JourneyTemplate, self).save(**kwargs) 178 + 179 + 180 + @python_2_unicode_compatible 181 + class Journey(GisTimeStampedModel): 182 + """A model class to represent a journey between two node.""" 183 + 184 + # Reference to the template 185 + template = models.ForeignKey("journeys.JourneyTemplate", related_name="journeys") 186 + 187 + # Data about places and passengers 188 + free_places = models.PositiveIntegerField(default=4, verbose_name=_("plazas libres"), blank=True, null=True) 189 + total_passengers = models.PositiveIntegerField(default=0, verbose_name=_("total pasajeros"), blank=True, null=True) 190 + 191 + # Data about time of departure and arrival 192 + departure = models.DateTimeField(verbose_name=_("fecha y hora de salida*")) 193 + arrival = models.DateTimeField(verbose_name=_("fecha y hora de llegada estimada*"), null=True, blank=True) 194 + 150 195 disabled = models.BooleanField(default=False, verbose_name=_("marcar como deshabilitado")) 151 - recurrence = RecurrenceField(verbose_name=_("¿Vas a realizar este viaje más de una vez?"), null=True, blank=True) 152 - parent = models.ForeignKey("journeys.Journey", null=True, blank=True, related_name="children") 153 196 154 197 objects = JourneyManager() 155 198 156 199 @property 157 200 def origin(self): 158 201 """Origin of the journey.""" 159 - if self.kind == GOING: 160 - return self.residence 161 - return self.campus 202 + if self.template.kind == GOING: 203 + return self.template.residence 204 + return self.template.campus 162 205 163 206 @property 164 207 def destination(self): 165 208 """Destination of the journey.""" 166 - if self.kind == RETURN: 167 - return self.residence 168 - return self.campus 209 + if self.template.kind == RETURN: 210 + return self.template.residence 211 + return self.template.campus 169 212 170 213 @property 171 214 def has_recurrence(self): 172 - return self.children.exists() or self.parent is not None 215 + return self.template.journeys.count() > 1 216 + 217 + @property 218 + def user(self): 219 + return self.template.user 220 + 221 + @property 222 + def residence(self): 223 + return self.template.residence 224 + 225 + @property 226 + def campus(self): 227 + return self.template.campus 228 + 229 + @property 230 + def driver(self): 231 + return self.template.driver 173 232 174 233 def __str__(self): 175 234 return self.description(strip_html=True) ··· 188 247 return self.arrival.isoformat() 189 248 return (self.departure + datetime.timedelta(minutes=30)).isoformat() 190 249 191 - def recurrence_journeys(self): 192 - journeys = Journey.objects.filter(pk=self.pk) 193 - if self.children.exists(): 194 - journeys = Journey.objects.filter( 195 - Q(pk__in=self.children.all().values_list("pk", flat=True)) | 196 - Q(pk=self.pk) 197 - ) 198 - elif self.parent: 199 - journeys = self.parent.children.all() 200 - journeys = Journey.objects.filter( 201 - Q(pk__in=self.parent.children.all().values_list("pk", flat=True)) | 202 - Q(pk=self.parent.pk) 203 - ) 204 - return journeys 250 + def brothers(self, exclude_myself=False): 251 + """Gets the 'brothers' of this journey.""" 252 + if not exclude_myself: 253 + return self.template.journeys.all() 254 + return self.template.journeys.exclude(pk=self.pk) 205 255 206 256 def description(self, strip_html=False): 207 257 """Gets a human read description of the journey.""" 208 - if self.kind == GOING: 258 + if self.template.kind == GOING: 209 259 value = _("Viaje de <strong>%(kind)s</strong> a <strong>%(campus_name)s</strong>") % { 210 - "kind": self.get_kind_display(), 211 - "campus_name": self.campus.name 260 + "kind": self.template.get_kind_display(), 261 + "campus_name": self.template.campus.name 212 262 } 213 263 else: 214 264 value = _("Viaje de <strong>%(kind)s</strong> desde <strong>%(campus_name)s</strong>") % { 215 - "kind": self.get_kind_display(), 216 - "campus_name": self.campus.name 265 + "kind": self.template.get_kind_display(), 266 + "campus_name": self.template.campus.name 217 267 } 218 268 if strip_html: 219 269 value = strip_tags(value) ··· 249 299 # Join to recurrence 250 300 elif join_to is not None and join_to == "all": 251 301 if self.has_recurrence: 252 - journeys = self.recurrence_journeys() 302 + journeys = self.brothers() 253 303 journeys = journeys.filter(departure__gte=self.departure) 254 304 passengers = [] 255 305 for journey in journeys: ··· 262 312 elif join_to is not None and len(join_to.split("/")) > 0: 263 313 dates = map(lambda item: datetime.datetime.strptime(item, "%d/%m/%Y"), join_to.split(",")) 264 314 conditions = [Q(departure__day=date.day, departure__month=date.month, departure__year=date.year) for date in dates] 265 - journeys = self.recurrence_journeys() 315 + journeys = self.brothers() 266 316 journeys = journeys.filter(reduce(lambda x, y: x | y, conditions)) 267 - print(journeys) 268 317 passengers = [] 269 318 for journey in journeys: 270 319 try: ··· 328 377 """Gets recommended journeys for this journey. 329 378 :returns QuerySet: 330 379 """ 331 - if self.driver == self.user: 380 + if self.template.driver == self.template.user: 332 381 return Journey.objects.none() 333 - result = Journey.objects.recommended(user=self.user, kind=self.kind, journey=self, ignore_full=ignore_full) 382 + result = Journey.objects.recommended(user=self.template.user, kind=self.template.kind, journey=self, ignore_full=ignore_full) 334 383 return result 335 384 336 385 def needs_driver(self): 337 386 """Checks if the journey needs a driver.""" 338 - return self.driver is None 387 + return self.template.driver is None 339 388 340 389 def are_there_free_places(self): 341 390 """Check if there are free places.""" ··· 343 392 344 393 def is_fulfilled(self): 345 394 """Check if the journey is already fulfilled by the given user.""" 346 - return self.needs_driver() and self.recommended(ignore_full=True).filter(passengers__user=self.user).exists() 395 + return self.needs_driver() and self.recommended(ignore_full=True).\ 396 + filter(passengers__user=self.template.user).exists() 347 397 348 398 def fulfilled_by(self): 349 399 """Gets journeys who are fulfilling this one.""" 350 400 if not self.is_fulfilled(): 351 401 return None 352 - return self.recommended(ignore_full=True).filter(passengers__user=self.user) 402 + return self.recommended(ignore_full=True).filter(passengers__user=self.template.user) 353 403 354 404 def distance(self): 355 405 """Gets the journey distance.""" 356 - return self.residence.position.distance(self.campus.position) / 1000 406 + return self.template.residence.position.distance(self.campus.position) / 1000 357 407 358 408 def is_messenger_allowed(self, user): 359 409 """Check if the user is allowed to make messenger actions.""" 360 - return not(self.user != user and not self.is_passenger(user)) 410 + return not(self.template.user != user and not self.is_passenger(user)) 361 411 362 412 @dispatch(CANCEL) 363 413 def cancel(self): ··· 373 423 return self.passengers.filter(status=CONFIRMED) 374 424 return Passenger.objects.none() 375 425 376 - def save(self, **kwargs): 377 - """Override save to set the default time window value. Default value not set in Oracle.""" 378 - if not self.time_window: 379 - self.time_window = DEFAULT_TIME_WINDOW 380 - return super(Journey, self).save(**kwargs) 381 - 382 426 383 427 class Passenger(TimeStampedModel): 384 428 """A user who has joined a journey.""" ··· 389 433 class Meta: 390 434 unique_together = ["user", "journey"] 391 435 392 - def recurrence(self): 393 - journeys = self.journey.recurrence_journeys() 394 - conditions = [Q(journey__pk=journey.pk) for journey in journeys] 395 - passengers = Passenger.objects.filter(reduce(lambda x, y: x | y, conditions)) 436 + def brothers(self): 437 + """Gets the Passenger models from the same user to all the journeys of the 438 + recurrence. 439 + """ 440 + journeys = self.journey.brothers() 441 + journeys_pks = [journey.pk for journey in journeys] 442 + passengers = Passenger.objects.filter(journey__pk__in=journeys_pks) 396 443 return passengers 397 444 398 445 def has_recurrence(self): 399 446 """Check if there is more than one request for the recourrence of 400 447 the journey. 401 448 """ 402 - passengers = self.recurrence() 449 + passengers = self.brothers() 403 450 return passengers.count() > 1 404 451 405 452