-
Notifications
You must be signed in to change notification settings - Fork 64
GaspardFouche Padam Test PR #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
gaspF
wants to merge
7
commits into
Optiways:master
Choose a base branch
from
gaspF:dev
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
90f6443
initial commit
1a70b8a
add tests
8a80b8c
add migrations
687bca8
update docstring
36fec72
remove unused imports
2dcdc05
fix test, set trip duration as a property
18b8031
remove unused imports, fix docstring
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,3 +20,5 @@ ENV/ | |
| # Editors stuff | ||
| .idea | ||
| .vscode | ||
|
|
||
| myenv | ||
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from django.contrib import admin | ||
| from .models import BusStop, BusShift | ||
| from .forms import BusShiftForm | ||
|
|
||
|
|
||
| class BusShiftAdmin(admin.ModelAdmin): | ||
| form = BusShiftForm | ||
| list_display = ["bus", "driver", "start_time", "end_time", "duration"] | ||
| search_fields = ["bus__licence_plate", "driver__user__username"] | ||
|
|
||
|
|
||
| admin.site.register(BusStop) | ||
| admin.site.register(BusShift, BusShiftAdmin) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from django.apps import AppConfig | ||
|
|
||
|
|
||
| class TripsConfig(AppConfig): | ||
| name = "padam_django.apps.trips" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| from django import forms | ||
| from django.core.exceptions import ValidationError | ||
| from django.db.models import Q | ||
| from .models import BusShift | ||
|
|
||
|
|
||
| class BusShiftForm(forms.ModelForm): | ||
| """ | ||
| A Django form for creating or updating `BusShift` instances, which represent | ||
| individual bus shifts that must contain a unique sequence of at least two bus stops. | ||
|
|
||
| This form performs validation on the set of bus stops provided to ensure: | ||
| - No duplicate stops are included within the shift. | ||
| - At least two unique bus stops are specified for a valid shift. | ||
|
|
||
| Attributes: | ||
| model: The model associated with this form, set to `BusShift`. | ||
| fields: Specifies that all fields in `BusShift` should be included in the form. | ||
| """ | ||
|
|
||
| class Meta: | ||
| model = BusShift | ||
| fields = "__all__" | ||
|
|
||
| def clean(self): | ||
| """ | ||
| Override the clean method to perform custom validation on bus stops. | ||
|
|
||
| This method checks: | ||
| - No duplicate bus stops are included in the shift. | ||
| - At least two unique bus stops are specified. | ||
|
|
||
| Raises: | ||
| ValidationError: If duplicate stops are found or fewer than two unique stops are provided. | ||
|
|
||
| Returns: | ||
| cleaned_data: The validated form data. | ||
| """ | ||
| cleaned_data = super().clean() | ||
| stops = self.cleaned_data.get("stops") | ||
|
|
||
| if stops: | ||
| unique_stops = set() | ||
| for stop in stops: | ||
| unique_stops.add(stop) | ||
|
|
||
| if len(unique_stops) < 2: | ||
| raise ValidationError("At least two bus stops are required.") | ||
|
|
||
| self._calculate_shift_times(unique_stops) | ||
| self._validate_unique_shift() | ||
| return cleaned_data | ||
|
|
||
| def _calculate_shift_times(self, unique_stops): | ||
| """ | ||
| Calculate start_time, end_time, and duration based on unique bus stops. | ||
| Assigns None values if no stops are assigned, and duration as zero. | ||
| """ | ||
| if unique_stops: | ||
| sorted_stops = sorted(unique_stops, key=lambda x: x.stop_time) | ||
| first_stop = sorted_stops[0] | ||
| last_stop = sorted_stops[-1] | ||
|
|
||
| self.instance.start_time = first_stop.stop_time | ||
| self.instance.end_time = last_stop.stop_time | ||
| else: | ||
| self.instance.start_time = self.instance.end_time = None | ||
|
|
||
| def _validate_unique_shift(self): | ||
| """ | ||
| Ensure there are no overlapping shifts for the same bus or driver. | ||
|
|
||
| Raises: | ||
| ValidationError: If any overlap exists with another shift's start or end times. | ||
| """ | ||
|
|
||
| if not self.instance.start_time or not self.instance.end_time: | ||
| return | ||
|
|
||
| bus = self.cleaned_data.get("bus") | ||
| driver = self.cleaned_data.get("driver") | ||
| if bus and driver and self.instance.start_time and self.instance.end_time: | ||
| overlapping_shifts = BusShift.objects.exclude(pk=self.instance.pk).filter( | ||
| Q(bus=bus) | Q(driver=driver), | ||
| Q(start_time__lt=self.instance.end_time), | ||
| Q(end_time__gt=self.instance.start_time), | ||
| ) | ||
| if overlapping_shifts.exists(): | ||
| raise ValidationError( | ||
| "This shift overlaps with an existing shift for the same bus or driver." | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Generated by Django 3.2.5 on 2024-11-04 11:24 | ||
|
|
||
| from django.db import migrations, models | ||
| import django.db.models.deletion | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| initial = True | ||
|
|
||
| dependencies = [ | ||
| ('fleet', '0002_auto_20211109_1456'), | ||
| ('geography', '0001_initial'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.CreateModel( | ||
| name='BusStop', | ||
| fields=[ | ||
| ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
| ('name', models.CharField(max_length=100)), | ||
| ('stop_time', models.DateTimeField(verbose_name='Stop Time')), | ||
| ('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bus_stops', to='geography.place')), | ||
| ], | ||
| options={ | ||
| 'ordering': ['stop_time'], | ||
| }, | ||
| ), | ||
| migrations.CreateModel( | ||
| name='BusShift', | ||
| fields=[ | ||
| ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
| ('duration', models.DurationField(blank=True, editable=False, null=True, verbose_name='Duration')), | ||
| ('start_time', models.DateTimeField(blank=True, editable=False, null=True, verbose_name='Start Time')), | ||
| ('end_time', models.DateTimeField(blank=True, editable=False, null=True, verbose_name='End Time')), | ||
| ('bus', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.bus')), | ||
| ('driver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shifts', to='fleet.driver')), | ||
| ('stops', models.ManyToManyField(to='trips.BusStop')), | ||
| ], | ||
| ), | ||
| migrations.AddConstraint( | ||
| model_name='busstop', | ||
| constraint=models.UniqueConstraint(fields=('name', 'place', 'stop_time'), name='unique_place_stop_time'), | ||
| ), | ||
| ] |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| from django.db import models | ||
|
|
||
|
|
||
| class BusStop(models.Model): | ||
| """ | ||
| Represents a bus stop with a specific place, stop time, and unique identifier (name). | ||
|
|
||
| Attributes: | ||
| name (str): The name of the bus stop. | ||
| place (ForeignKey): The geographical location of the stop. | ||
| stop_time (DateTimeField): The scheduled stop time at this place. | ||
|
|
||
| Meta: | ||
| constraints: Ensures that a unique combination of name, place, and stop_time exists. | ||
| ordering: Orders bus stops by their stop time by default. | ||
| """ | ||
|
|
||
| name = models.CharField(max_length=100) | ||
| place = models.ForeignKey( | ||
| "geography.Place", on_delete=models.CASCADE, related_name="bus_stops" | ||
| ) | ||
| stop_time = models.DateTimeField("Stop Time") | ||
|
|
||
| class Meta: | ||
| constraints = [ | ||
| models.UniqueConstraint( | ||
| fields=["name", "place", "stop_time"], | ||
| name="unique_place_stop_time", | ||
| ) | ||
| ] | ||
| ordering = ["stop_time"] | ||
|
|
||
| def __str__(self): | ||
| return f"{self.place} at {self.stop_time}" | ||
|
|
||
|
|
||
| class BusShift(models.Model): | ||
| """ | ||
| Represents a shift for a specific bus and driver with assigned stops, start and end times, and a duration. | ||
|
|
||
| Attributes: | ||
| bus (ForeignKey): The bus assigned to this shift. | ||
| driver (ForeignKey): The driver assigned to this shift. | ||
| stops (ManyToManyField): The sequence of bus stops for the shift. | ||
| start_time (DateTimeField): Calculated start time of the shift. | ||
| end_time (DateTimeField): Calculated end time of the shift. | ||
| """ | ||
|
|
||
| bus = models.ForeignKey( | ||
| "fleet.Bus", on_delete=models.CASCADE, related_name="shifts" | ||
| ) | ||
| driver = models.ForeignKey( | ||
| "fleet.Driver", on_delete=models.CASCADE, related_name="shifts" | ||
| ) | ||
| stops = models.ManyToManyField("BusStop") | ||
| start_time = models.DateTimeField( | ||
| "Start Time", null=True, blank=True, editable=False | ||
| ) | ||
| end_time = models.DateTimeField("End Time", null=True, blank=True, editable=False) | ||
|
|
||
| @property | ||
| def duration(self): | ||
| """ | ||
| Calculate the duration of the shift based on the start and end times. | ||
|
|
||
| Returns: | ||
| timedelta: The duration of the shift if both start_time and end_time are defined. | ||
| None: If either start_time or end_time is None, duration cannot be calculated. | ||
| """ | ||
| if self.end_time and self.start_time: | ||
| return self.end_time - self.start_time | ||
|
|
||
| def __str__(self): | ||
| return f"Bus shift with Bus {self.bus} and driver {self.driver}" |
Empty file.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unique_stops = set(stops.values())