···2121 :param journey:
2222 :param override_distance:
2323 """
2424- key = "residence{}" if journey.kind == GOING else "campus{}"
2424+ key = "template__residence{}" if journey.kind == GOING else "template__campus{}"
2525 distance = getattr(journey, key.format("")).distance if override_distance is None else override_distance
2626 return {
2727 key.format("__position__distance_lte"): (
···4747 )
484849495050-class JourneyQuerySet(models.QuerySet):
5151-5252- def visible(self, user=None):
5353- """Journey visible for the given user."""
5454- if user is not None:
5555- return self.filter(user__groups=user.groups.all())
5656- return self
5757-5858-5959-class JourneyManager(models.GeoManager):
6060- """Manager for Journeys."""
6161-6262- def get_queryset(self):
6363- return JourneyQuerySet(self.model, using=self._db)
6464-6565- def visible(self, user=None):
6666- """Journey visible for the given user."""
6767- return self.get_queryset().visible(user=user)
5050+class JourneyTemplateManager(models.GeoManager):
68516952 def smart_create(self, user, origin, destination, departure, transport=None):
7053 """Enhanced method to create journeys"""
···8669 data["free_places"] = transport.default_places
8770 return self.create(**data)
88717272+7373+class JourneyQuerySet(models.QuerySet):
7474+7575+ def visible(self, user=None):
7676+ """Journey visible for the given user."""
7777+ if user is not None:
7878+ return self.filter(user__groups=user.groups.all())
7979+ return self
8080+8181+8282+class JourneyManager(models.GeoManager):
8383+ """Manager for Journeys."""
8484+8585+ def get_queryset(self):
8686+ return JourneyQuerySet(self.model, using=self._db)
8787+8888+ def visible(self, user=None):
8989+ """Journey visible for the given user."""
9090+ return self.get_queryset().visible(user=user)
9191+8992 def available(self, user=None, kind=None, ignore_full=False):
9093 """Gets all available journeys.
9194 :param user:
···9396 :param ignore_full:
9497 """
9598 now = timezone.now()
9696- queryset = self.visible(user).filter(driver__isnull=False, departure__gt=now)
9999+ queryset = self.visible(user).filter(template__driver__isnull=False, departure__gt=now)
97100 if kind is not None:
9898- queryset = queryset.filter(kind=kind)
101101+ queryset = queryset.filter(template__kind=kind)
99102 if ignore_full:
100103 return queryset
101104 # NOTE: annotate QuerySet method has problems with Oracle, so, we have to
···117120 nearby = nearby.filter(**{key: (geometry, distance)})
118121 else:
119122 nearby = nearby.filter(
120120- Q(residence__position__distance_lte=(geometry, distance)) |
121121- Q(campus__position__distance_lte=(geometry, distance))
123123+ Q(template__residence__position__distance_lte=(geometry, distance)) |
124124+ Q(template__campus__position__distance_lte=(geometry, distance))
122125 )
123126 return nearby
124127···128131 :param user:
129132 """
130133 now = timezone.now()
131131- queryset = self.filter(user=user, driver__isnull=True, departure__gt=now)
134134+ queryset = self.filter(template__user=user, template__driver__isnull=True, departure__gt=now)
132135 if kind is not None:
133136 queryset = queryset.filter(kind=kind)
134137 return queryset
···152155 if not conditions:
153156 return self.none()
154157 now = timezone.now()
155155- queryset = self.available(user=user, kind=kind, ignore_full=ignore_full).exclude(user=user, departure__lt=now)\
158158+ queryset = self.available(user=user, kind=kind, ignore_full=ignore_full)\
159159+ .exclude(template__user=user, departure__lt=now)\
156160 .filter(reduce(lambda x, y: x | y, conditions))\
157161 .order_by("departure")
158162 return queryset
···177181 kinds = [GOING, RETURN]
178182 conditions = []
179183 for kind in kinds:
180180- key = "residence{}" if kind == GOING else "campus{}"
184184+ key = "template__residence{}" if kind == GOING else "template__campus{}"
181185 conditions.append(Q(**{
182186 key.format("__position__distance_lte"): (
183187 position,
···188192 }))
189193 now = timezone.now()
190194 queryset = self.available(user=user, ignore_full=ignore_full)\
191191- .exclude(user=user, departure__lt=now, disabled=True) \
195195+ .exclude(template__user=user, departure__lt=now, disabled=True) \
192196 .filter(reduce(lambda x, y: x | y, conditions)) \
193197 .order_by("departure")
194198 return queryset
195199196200 def overlaps(self, user, departure, time_window):
197201 """Returns a queryset with the overlapping journeys."""
198198- return self.filter(user=user).filter(
202202+ return self.filter(template__user=user).filter(
199203 departure__gte=(departure - datetime.timedelta(minutes=time_window)),
200204 departure__lte=(departure + datetime.timedelta(minutes=time_window))
201205 )
···214218 :param journey:
215219 """
216220 if journey is None:
217217- return self.filter(Q(journey__user=user) | Q(journey__passengers__user=user))
221221+ return self.filter(Q(journey__template__user=user) | Q(journey__passengers__user=user))
218222 if not journey.is_messenger_allowed(user):
219223 return self.none()
220224 return self.filter(journey=journey)
+103-56
upvcarshare/journeys/models.py
···111111112112113113@python_2_unicode_compatible
114114-class Journey(GisTimeStampedModel):
115115- """A model class to represent a journey between two node."""
114114+class JourneyTemplate(GisTimeStampedModel):
115115+ """A journey template is used to create a serie of journeys using the reference of itself."""
116116+117117+ # Owner of the template
116118 user = models.ForeignKey(
117119 settings.AUTH_USER_MODEL, related_name="journeys", help_text=_("user who creates the journey")
118120 )
121121+122122+ # Data about the driver
119123 driver = models.ForeignKey(
120124 settings.AUTH_USER_MODEL, null=True, blank=True, help_text=_("user who drives during the journey")
121125 )
126126+127127+ # Data about origin and destination, and time window to make its
128128+ # journeys visibles.
122129 residence = models.ForeignKey(
123130 "journeys.Residence",
124131 verbose_name=_("lugar"),
···129136 verbose_name=_("campus"),
130137 related_name="journeys"
131138 )
132132- kind = models.PositiveIntegerField(choices=JOURNEY_KINDS, verbose_name=_("tipo de viaje"))
133133- free_places = models.PositiveIntegerField(default=4, verbose_name=_("plazas libres"), blank=True, null=True)
134134- total_passengers = models.PositiveIntegerField(default=0, verbose_name=_("total pasajeros"), blank=True, null=True)
135135- departure = models.DateTimeField(verbose_name=_("fecha y hora de salida*"))
136136- arrival = models.DateTimeField(verbose_name=_("fecha y hora de llegada estimada*"), null=True, blank=True)
137139 time_window = models.PositiveIntegerField(
138140 verbose_name=_("ventana de tiempo"),
139141 help_text=_("Se buscarán por los viajes que salgan hasta con estos minutos de antelación"),
140142 default=DEFAULT_TIME_WINDOW,
141143 blank=True
142144 )
145145+146146+ # Data about recurrence
147147+ recurrence = RecurrenceField(verbose_name=_("¿Vas a realizar este viaje más de una vez?"), null=True, blank=True)
148148+149149+ # Kind of the journey
150150+ kind = models.PositiveIntegerField(choices=JOURNEY_KINDS, verbose_name=_("tipo de viaje"))
151151+152152+ # Transport used in this journeys
143153 transport = models.ForeignKey(
144154 "journeys.Transport",
145155 related_name="journeys",
···147157 null=True,
148158 blank=True
149159 )
160160+161161+ def create_journey(self, departure, arrival):
162162+ """Creates a Journey using the template and the data given."""
163163+ journey = Journey(
164164+ template=self,
165165+ departure=departure,
166166+ arrival=arrival,
167167+ free_places=self.transport.default_places,
168168+ total_passengers=0,
169169+ )
170170+ journey.save()
171171+ return journey
172172+173173+ def save(self, **kwargs):
174174+ """Override save to set the default time window value. Default value not set in Oracle."""
175175+ if not self.time_window:
176176+ self.time_window = DEFAULT_TIME_WINDOW
177177+ return super(JourneyTemplate, self).save(**kwargs)
178178+179179+180180+@python_2_unicode_compatible
181181+class Journey(GisTimeStampedModel):
182182+ """A model class to represent a journey between two node."""
183183+184184+ # Reference to the template
185185+ template = models.ForeignKey("journeys.JourneyTemplate", related_name="journeys")
186186+187187+ # Data about places and passengers
188188+ free_places = models.PositiveIntegerField(default=4, verbose_name=_("plazas libres"), blank=True, null=True)
189189+ total_passengers = models.PositiveIntegerField(default=0, verbose_name=_("total pasajeros"), blank=True, null=True)
190190+191191+ # Data about time of departure and arrival
192192+ departure = models.DateTimeField(verbose_name=_("fecha y hora de salida*"))
193193+ arrival = models.DateTimeField(verbose_name=_("fecha y hora de llegada estimada*"), null=True, blank=True)
194194+150195 disabled = models.BooleanField(default=False, verbose_name=_("marcar como deshabilitado"))
151151- recurrence = RecurrenceField(verbose_name=_("¿Vas a realizar este viaje más de una vez?"), null=True, blank=True)
152152- parent = models.ForeignKey("journeys.Journey", null=True, blank=True, related_name="children")
153196154197 objects = JourneyManager()
155198156199 @property
157200 def origin(self):
158201 """Origin of the journey."""
159159- if self.kind == GOING:
160160- return self.residence
161161- return self.campus
202202+ if self.template.kind == GOING:
203203+ return self.template.residence
204204+ return self.template.campus
162205163206 @property
164207 def destination(self):
165208 """Destination of the journey."""
166166- if self.kind == RETURN:
167167- return self.residence
168168- return self.campus
209209+ if self.template.kind == RETURN:
210210+ return self.template.residence
211211+ return self.template.campus
169212170213 @property
171214 def has_recurrence(self):
172172- return self.children.exists() or self.parent is not None
215215+ return self.template.journeys.count() > 1
216216+217217+ @property
218218+ def user(self):
219219+ return self.template.user
220220+221221+ @property
222222+ def residence(self):
223223+ return self.template.residence
224224+225225+ @property
226226+ def campus(self):
227227+ return self.template.campus
228228+229229+ @property
230230+ def driver(self):
231231+ return self.template.driver
173232174233 def __str__(self):
175234 return self.description(strip_html=True)
···188247 return self.arrival.isoformat()
189248 return (self.departure + datetime.timedelta(minutes=30)).isoformat()
190249191191- def recurrence_journeys(self):
192192- journeys = Journey.objects.filter(pk=self.pk)
193193- if self.children.exists():
194194- journeys = Journey.objects.filter(
195195- Q(pk__in=self.children.all().values_list("pk", flat=True)) |
196196- Q(pk=self.pk)
197197- )
198198- elif self.parent:
199199- journeys = self.parent.children.all()
200200- journeys = Journey.objects.filter(
201201- Q(pk__in=self.parent.children.all().values_list("pk", flat=True)) |
202202- Q(pk=self.parent.pk)
203203- )
204204- return journeys
250250+ def brothers(self, exclude_myself=False):
251251+ """Gets the 'brothers' of this journey."""
252252+ if not exclude_myself:
253253+ return self.template.journeys.all()
254254+ return self.template.journeys.exclude(pk=self.pk)
205255206256 def description(self, strip_html=False):
207257 """Gets a human read description of the journey."""
208208- if self.kind == GOING:
258258+ if self.template.kind == GOING:
209259 value = _("Viaje de <strong>%(kind)s</strong> a <strong>%(campus_name)s</strong>") % {
210210- "kind": self.get_kind_display(),
211211- "campus_name": self.campus.name
260260+ "kind": self.template.get_kind_display(),
261261+ "campus_name": self.template.campus.name
212262 }
213263 else:
214264 value = _("Viaje de <strong>%(kind)s</strong> desde <strong>%(campus_name)s</strong>") % {
215215- "kind": self.get_kind_display(),
216216- "campus_name": self.campus.name
265265+ "kind": self.template.get_kind_display(),
266266+ "campus_name": self.template.campus.name
217267 }
218268 if strip_html:
219269 value = strip_tags(value)
···249299 # Join to recurrence
250300 elif join_to is not None and join_to == "all":
251301 if self.has_recurrence:
252252- journeys = self.recurrence_journeys()
302302+ journeys = self.brothers()
253303 journeys = journeys.filter(departure__gte=self.departure)
254304 passengers = []
255305 for journey in journeys:
···262312 elif join_to is not None and len(join_to.split("/")) > 0:
263313 dates = map(lambda item: datetime.datetime.strptime(item, "%d/%m/%Y"), join_to.split(","))
264314 conditions = [Q(departure__day=date.day, departure__month=date.month, departure__year=date.year) for date in dates]
265265- journeys = self.recurrence_journeys()
315315+ journeys = self.brothers()
266316 journeys = journeys.filter(reduce(lambda x, y: x | y, conditions))
267267- print(journeys)
268317 passengers = []
269318 for journey in journeys:
270319 try:
···328377 """Gets recommended journeys for this journey.
329378 :returns QuerySet:
330379 """
331331- if self.driver == self.user:
380380+ if self.template.driver == self.template.user:
332381 return Journey.objects.none()
333333- result = Journey.objects.recommended(user=self.user, kind=self.kind, journey=self, ignore_full=ignore_full)
382382+ result = Journey.objects.recommended(user=self.template.user, kind=self.template.kind, journey=self, ignore_full=ignore_full)
334383 return result
335384336385 def needs_driver(self):
337386 """Checks if the journey needs a driver."""
338338- return self.driver is None
387387+ return self.template.driver is None
339388340389 def are_there_free_places(self):
341390 """Check if there are free places."""
···343392344393 def is_fulfilled(self):
345394 """Check if the journey is already fulfilled by the given user."""
346346- return self.needs_driver() and self.recommended(ignore_full=True).filter(passengers__user=self.user).exists()
395395+ return self.needs_driver() and self.recommended(ignore_full=True).\
396396+ filter(passengers__user=self.template.user).exists()
347397348398 def fulfilled_by(self):
349399 """Gets journeys who are fulfilling this one."""
350400 if not self.is_fulfilled():
351401 return None
352352- return self.recommended(ignore_full=True).filter(passengers__user=self.user)
402402+ return self.recommended(ignore_full=True).filter(passengers__user=self.template.user)
353403354404 def distance(self):
355405 """Gets the journey distance."""
356356- return self.residence.position.distance(self.campus.position) / 1000
406406+ return self.template.residence.position.distance(self.campus.position) / 1000
357407358408 def is_messenger_allowed(self, user):
359409 """Check if the user is allowed to make messenger actions."""
360360- return not(self.user != user and not self.is_passenger(user))
410410+ return not(self.template.user != user and not self.is_passenger(user))
361411362412 @dispatch(CANCEL)
363413 def cancel(self):
···373423 return self.passengers.filter(status=CONFIRMED)
374424 return Passenger.objects.none()
375425376376- def save(self, **kwargs):
377377- """Override save to set the default time window value. Default value not set in Oracle."""
378378- if not self.time_window:
379379- self.time_window = DEFAULT_TIME_WINDOW
380380- return super(Journey, self).save(**kwargs)
381381-382426383427class Passenger(TimeStampedModel):
384428 """A user who has joined a journey."""
···389433 class Meta:
390434 unique_together = ["user", "journey"]
391435392392- def recurrence(self):
393393- journeys = self.journey.recurrence_journeys()
394394- conditions = [Q(journey__pk=journey.pk) for journey in journeys]
395395- passengers = Passenger.objects.filter(reduce(lambda x, y: x | y, conditions))
436436+ def brothers(self):
437437+ """Gets the Passenger models from the same user to all the journeys of the
438438+ recurrence.
439439+ """
440440+ journeys = self.journey.brothers()
441441+ journeys_pks = [journey.pk for journey in journeys]
442442+ passengers = Passenger.objects.filter(journey__pk__in=journeys_pks)
396443 return passengers
397444398445 def has_recurrence(self):
399446 """Check if there is more than one request for the recourrence of
400447 the journey.
401448 """
402402- passengers = self.recurrence()
449449+ passengers = self.brothers()
403450 return passengers.count() > 1
404451405452