Skip to content

Commit

Permalink
Merge branch 'develop' into modified_webdriver_and_updated_requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
dmytrokoren authored Aug 19, 2024
2 parents b96815c + 98152c0 commit e0a35de
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 40 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
rev: 24.8.0
hooks:
- id: black
- repo: https://github.com/codespell-project/codespell
Expand All @@ -11,7 +11,7 @@ repos:
- tomli
exclude: utils/airport_timezones.json
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
rev: 7.1.1
hooks:
- id: flake8
- repo: https://github.com/PyCQA/isort
Expand Down
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ When upgrading to a new version, make sure to follow the directions under the "U
If there is no "Upgrading" header for that version, no post-upgrade actions need to be performed.


## Upcoming
## 8.0 (2024-08-17)
### New Features
- A new [notification level](CONFIGURATION.md#notification-level) for notices (non-critical warnings) was added, which includes
driver timeouts and Too Many Requests errors during logins. This is the lowest notification level offered
Expand All @@ -20,12 +20,21 @@ checking many accounts and reservations at once
- The script now runs as a non-root user in Docker to improve a container's security
- Error messages during check-ins and reservation retrievals relating to reservation/passenger not found, invalid confirmation number
length, and airport check-in required are more detailed
- Add a backup NTP server to make retrieving time much more reliable
([#284](https://github.com/jdholtz/auto-southwest-check-in/pull/284) by [@dmytrokoren](https://github.com/dmytrokoren))

### Bug Fixes
- Adapt check-ins to use Southwest's new API
([#282](https://github.com/jdholtz/auto-southwest-check-in/pull/282) by [@dmytrokoren](https://github.com/dmytrokoren))
- Fix the webdriver failing to start when using Docker
([#291](https://github.com/jdholtz/auto-southwest-check-in/pull/291) by [@ne0ark](https://github.com/ne0ark))

### Upgrading
- If you manually set `notification_level` in your configuration, it will need to be adjusted accordingly.
- If it was set to `2` (error messages only), it needs to be set to `3`
- If it was set to `1` (all messages), it needs to be set to `2`
- Refer to the [notification level configuration](CONFIGURATION.md#notification-level) for more details on the levels
- Upgrade the dependencies to the latest versions by running `pip install -r requirements.txt`


## 7.5 (2024-06-07)
Expand Down
14 changes: 8 additions & 6 deletions lib/checkin_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,18 +189,20 @@ def _attempt_check_in(self) -> JSON:

def _check_in_to_flight(self) -> JSON:
"""
First, make a GET request to get the needed check-in information. Then, make
a POST request to submit the check in.
First, initiate a POST request to get the needed check-in information. Subsequently, execute
another POST request to submit the check in.
"""
headers = self.checkin_scheduler.headers
info = {
"first-name": self.first_name,
"last-name": self.last_name,
"firstName": self.first_name,
"lastName": self.last_name,
"passengerSearchToken": "",
"recordLocator": self.flight.confirmation_number,
}
site = CHECKIN_URL + self.flight.confirmation_number

logger.debug("Making GET request to check in")
response = make_request("GET", site, headers, info)
logger.debug("Making POST request to check in")
response = make_request("POST", site, headers, info, random_sleep=False)

info = response["checkInViewReservationPage"]["_links"]["checkIn"]
site = f"mobile-air-operations{info['href']}"
Expand Down
7 changes: 4 additions & 3 deletions lib/checkin_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,15 @@ def _get_flights(self, confirmation_number: str) -> List[Flight]:

def _get_reservation_info(self, confirmation_number: str) -> Dict[str, Any]:
info = {
"first-name": self.reservation_monitor.first_name,
"last-name": self.reservation_monitor.last_name,
"firstName": self.reservation_monitor.first_name,
"lastName": self.reservation_monitor.last_name,
"recordLocator": confirmation_number,
}
site = VIEW_RESERVATION_URL + confirmation_number

try:
logger.debug("Retrieving reservation information")
response = make_request("GET", site, self.headers, info)
response = make_request("POST", site, self.headers, info)
except RequestError as err:
# Don't send a notification if flights have already been scheduled and all flights
# from this reservation are old. This is how old flights are removed.
Expand Down
13 changes: 11 additions & 2 deletions lib/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,17 @@ def _convert_to_utc(self, flight_date: str, airport_timezone: Any) -> datetime:
return utc_time

def _get_flight_number(self, flights: JSON) -> str:
"""
Formats the flight number in the way that the fare checker expects it, which is with the
'WN' prefix removed and a slash separating each number with a zero-width space on either
side.
"""
flight_number = ""
for flight in flights:
flight_number += flight["number"] + "\u200b/\u200b"
# Remove the 'WN' prefix from each flight number
flight_number += flight["number"].replace("WN", "", 1)
# Add a slash with a zero-width space on either side
flight_number += "\u200b/\u200b"

return flight_number.rstrip("\u200b/\u200b")
# Remove any slashes and zero-width spaces from the end
return flight_number.rstrip("/\u200b")
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Unfortunately, flake8 doesn't support pyproject.toml
[flake8]
max-line-length = 100
extend-ignore = E203
statistics = true
2 changes: 1 addition & 1 deletion southwest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys
from typing import List

__version__ = "v7.5"
__version__ = "v8.0"

__doc__ = """
Schedule a check-in:
Expand Down
22 changes: 11 additions & 11 deletions tests/integration/test_check_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def handler(mocker: MockerFixture) -> None:
"departureAirport": {"code": "LAX", "name": "test_outbound"},
"departureDate": "2021-12-06",
"departureTime": "14:40",
"flights": [{"number": "100"}],
"flights": [{"number": "WN100"}],
}
flight = Flight(flight_info, {}, "TEST")
# Make sure it isn't affected by local time
Expand All @@ -49,13 +49,13 @@ def test_check_in(
handler.first_name = "Garry"
handler.last_name = "Lin"

get_response = {
post_response1 = {
"checkInViewReservationPage": {
"_links": {"checkIn": {"body": {"test": "checkin"}, "href": "/post_check_in"}}
}
}

post_response = {
post_response2 = {
"checkInConfirmationPage": {
"flights": [
{
Expand All @@ -68,25 +68,25 @@ def test_check_in(
}
}

requests_mock.get(
BASE_URL + CHECKIN_URL + "TEST?first-name=Garry&last-name=Lin",
[{"json": get_response, "status_code": 200}],
requests_mock.post(
BASE_URL + CHECKIN_URL + "TEST",
[{"json": post_response1, "status_code": 200}],
)
requests_mock.post(
BASE_URL + "mobile-air-operations/post_check_in",
[{"json": post_response, "status_code": 200}],
[{"json": post_response2, "status_code": 200}],
)

if same_day_flight:
# Add a flight before to make sure a same day flight selects the second flight
second_post_response = copy.deepcopy(post_response)
second_post_response["checkInConfirmationPage"]["flights"].insert(0, {})
same_day_post_response = copy.deepcopy(post_response2)
same_day_post_response["checkInConfirmationPage"]["flights"].insert(0, {})

requests_mock.post(
BASE_URL + "mobile-air-operations/post_check_in",
[
{"json": post_response, "status_code": 200},
{"json": second_post_response, "status_code": 200},
{"json": post_response2, "status_code": 200},
{"json": same_day_post_response, "status_code": 200},
],
)

Expand Down
10 changes: 5 additions & 5 deletions tests/integration/test_monitoring_and_scheduling.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def mock_get_driver(self) -> mock.Mock:
"departureAirport": {"code": "LAX", "name": "test_outbound"},
"departureDate": "2020-10-13",
"departureTime": "14:40",
"flights": [{"number": "100"}, {"number": "101"}],
"flights": [{"number": "WN100"}, {"number": "WN101"}],
},
],
}
Expand All @@ -104,7 +104,7 @@ def mock_get_driver(self) -> mock.Mock:
side_effect=[datetime(2020, 10, 5, 18, 29), datetime(2020, 10, 14, 18, 29)],
)

requests_mock.get(
requests_mock.post(
TEST_RESERVATION_URL,
[{"json": reservation1, "status_code": 200}, {"json": reservation1, "status_code": 200}],
)
Expand Down Expand Up @@ -199,20 +199,20 @@ def mock_get_driver(self) -> mock.Mock:
"departureAirport": {"code": "LAX", "name": "test_outbound"},
"departureDate": "2020-10-13",
"departureTime": "14:40",
"flights": [{"number": "100"}],
"flights": [{"number": "WN100"}],
},
{
"arrivalAirport": {"name": "test_outbound", "country": None},
"departureAirport": {"code": "SYD", "name": "test_inbound"},
"departureDate": "2020-10-16",
"departureTime": "07:20",
"flights": [{"number": "101"}],
"flights": [{"number": "WN101"}],
},
],
}
}

requests_mock.get(TEST_RESERVATION_URL, [{"json": reservation, "status_code": 200}])
requests_mock.post(TEST_RESERVATION_URL, [{"json": reservation, "status_code": 200}])

monitor = AccountMonitor(config.accounts[0], Lock())
with pytest.raises(StopIteration):
Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_checkin_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def test_get_flights_retrieves_all_flights_under_reservation(

def test_get_flights_retrieves_no_flights_on_request_error(self, mocker: MockerFixture) -> None:
mocker.patch("lib.checkin_scheduler.make_request", side_effect=RequestError(""))
mocker.patch("lib.checkin_scheduler.get_current_time")

flights = self.scheduler._get_flights("flight1")

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_fare_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_flight(mocker: MockerFixture) -> Flight:
"departureAirport": {"name": None},
"arrivalAirport": {"name": None, "country": None},
"departureTime": None,
"flights": [{"number": "100"}],
"flights": [{"number": "WN100"}],
}

reservation_info = {"bounds": [flight_info]}
Expand Down
13 changes: 7 additions & 6 deletions tests/unit/test_flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def _set_up_flight(self) -> None:
"arrivalAirport": {"name": None, "country": None},
"departureDate": "1971-06-18",
"departureTime": "07:00",
"flights": [{"number": "100"}],
"flights": [{"number": "WN100"}],
}

# Needs to be mocked so it is only run when Flight is instantiated
Expand All @@ -44,7 +44,7 @@ def test_flight_is_international_when_country_is_specified(
"departureAirport": {"name": None},
"arrivalAirport": {"name": None, "country": country},
"departureTime": None,
"flights": [{"number": "100"}],
"flights": [{"number": "WN100"}],
}
flight = Flight(flight_info, {}, "")

Expand All @@ -58,7 +58,7 @@ def test_flights_with_the_same_flight_numbers_and_departure_times_are_equal(
"departureAirport": {"name": None},
"arrivalAirport": {"name": None, "country": None},
"departureTime": None,
"flights": [{"number": "100"}],
"flights": [{"number": "WN100"}],
}
flight1 = Flight(flight_info, {}, "")
flight2 = Flight(flight_info, {}, "")
Expand All @@ -76,7 +76,7 @@ def test_flights_with_the_same_flight_numbers_and_departure_times_are_equal(
"departureAirport": {"name": None},
"arrivalAirport": {"name": None, "country": None},
"departureTime": None,
"flights": [{"number": "101"}],
"flights": [{"number": "WN101"}],
},
datetime(1999, 1, 1, 8, 59),
),
Expand All @@ -85,7 +85,7 @@ def test_flights_with_the_same_flight_numbers_and_departure_times_are_equal(
"departureAirport": {"name": None},
"arrivalAirport": {"name": None, "country": None},
"departureTime": None,
"flights": [{"number": "100"}],
"flights": [{"number": "WN100"}],
},
datetime(1999, 1, 1, 9, 59),
),
Expand Down Expand Up @@ -141,7 +141,8 @@ def test_convert_to_utc_converts_local_time_to_utc(self) -> None:
assert self.flight._local_departure_time == tz.localize(datetime(1999, 12, 31, 23, 59))

@pytest.mark.parametrize(
["numbers", "expected_num"], [(["100"], "100"), (["100", "101"], "100\u200b/\u200b101")]
["numbers", "expected_num"],
[(["WN100"], "100"), (["WN100", "WN101"], "100\u200b/\u200b101")],
)
def test_get_flight_number_creates_flight_number_correctly(
self, numbers: List[str], expected_num: str
Expand Down
10 changes: 9 additions & 1 deletion tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,16 @@ def test_get_current_time_returns_a_datetime_from_ntp_server(mocker: MockerFixtu
assert utils.get_current_time() == datetime(1999, 12, 31, 23, 59, 59)


def test_get_current_time_returns_a_datetime_from_backup_ntp_server(mocker: MockerFixture) -> None:
ntp_stats = ntplib.NTPStats()
ntp_stats.tx_timestamp = 3155673599
mocker.patch("ntplib.NTPClient.request", side_effect=[ntplib.NTPException, ntp_stats])

assert utils.get_current_time() == datetime(1999, 12, 31, 23, 59, 59)


@pytest.mark.parametrize("exception", [socket.gaierror, ntplib.NTPException])
def test_get_current_time_returns_local_datetime_on_failed_request(
def test_get_current_time_returns_local_datetime_on_failed_requests(
mocker: MockerFixture, exception: Exception
) -> None:
mocker.patch("ntplib.NTPClient.request", side_effect=exception)
Expand Down

0 comments on commit e0a35de

Please sign in to comment.