Coverage for src/api/models.py: 98%

42 statements  

« prev     ^ index     » next       coverage.py v7.11.1, created at 2025-11-08 10:41 +0000

1from django.conf import settings 

2from django.db import models 

3from django.utils import timezone 

4 

5class ParkingLot(models.Model): 

6 name = models.CharField(max_length=100) 

7 city = models.CharField(max_length=100) 

8 street = models.CharField(max_length=150) 

9 building = models.CharField(max_length=20, blank=True) 

10 

11 def __str__(self): 

12 return f"{self.name} ({self.city}, {self.street} {self.building or ''})" 

13 

14class Spot(models.Model): 

15 number = models.CharField(max_length=10) 

16 lot = models.ForeignKey(ParkingLot, related_name="spots", on_delete=models.CASCADE) 

17 is_ev = models.BooleanField(default=False) 

18 is_disabled = models.BooleanField(default=False) 

19 

20 created_by = models.ForeignKey( 

21 settings.AUTH_USER_MODEL, 

22 null=True, 

23 blank=True, 

24 on_delete=models.SET_NULL, 

25 related_name="created_spots" 

26 ) 

27 

28 class Meta: 

29 unique_together = ("lot", "number") 

30 

31 def __str__(self): 

32 return f"{self.lot.name} #{self.number}" 

33 

34class Booking(models.Model): 

35 user = models.ForeignKey( 

36 settings.AUTH_USER_MODEL, 

37 on_delete=models.SET_NULL, 

38 null=True, 

39 blank=True, 

40 ) 

41 spot = models.ForeignKey(Spot, on_delete=models.PROTECT, related_name="bookings") 

42 start_at = models.DateTimeField() 

43 end_at = models.DateTimeField() 

44 status = models.CharField(max_length=16, default="confirmed") 

45 created_at = models.DateTimeField(auto_now_add=True) 

46 cancellation_reason = models.CharField(max_length=255, blank=True, default="") 

47 payment_intent_id = models.CharField(max_length=100, blank=True) 

48 def check_cancellable_error(self) -> str | None: 

49 if self.status == 'cancelled': 

50 return 'Booking is already cancelled.' 

51 

52 if self.end_at <= timezone.now(): 

53 return 'Booking time has already completed and cannot be cancelled.' 

54 

55 return None 

56 

57 class Meta: 

58 indexes = [ 

59 models.Index(fields=["spot", "start_at", "end_at", "status"]) 

60 ] 

61 

62class OperatorProfile(models.Model): 

63 user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='operator_profile') 

64 lot = models.ForeignKey(ParkingLot, on_delete=models.SET_NULL, null=True, related_name='operators') 

65 

66 def __str__(self): 

67 return f"Operator {self.user.username} for {self.lot.name if self.lot else 'N/A'}" 

68