From 1cf1fee7850e12e9ff7041d731c68d6b7a151c5b Mon Sep 17 00:00:00 2001 From: James Green Date: Fri, 1 Dec 2023 15:48:03 +0000 Subject: [PATCH 01/16] seperated add_geocoding_config into own function --- src/main.py | 30 ++++++++++++++++-------------- test/test_geocode.py | 0 test/test_main.py | 0 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 test/test_geocode.py create mode 100644 test/test_main.py diff --git a/src/main.py b/src/main.py index 61c9ccd..2377c31 100644 --- a/src/main.py +++ b/src/main.py @@ -4,28 +4,20 @@ # Convert CSV file to JSON object. -def csv_to_json(file_path): +def convert_csv_to_json(file_path): csv_rows = [] with open(file_path) as csvfile: reader = csv.DictReader(csvfile, skipinitialspace=True, quotechar='"') title = reader.fieldnames for row in reader: - csv_rows.extend([ {title[i]: row[title[i]] for i in range(len(title))} ]) + csv_rows.extend([{title[i]: row[title[i]] for i in range(len(title))}]) - return csv_rows - -if __name__ == '__main__': - - csv_file_path=os.getcwd() + '/../test_data/sites_with_clients.csv' - - # Convert CSV to valid JSON - data = csv_to_json(csv_file_path) - if data == None or data == []: + if csv_rows == None or csv_rows == []: raise ValueError('Failed to convert CSV file to JSON. Exiting script.') + return csv_rows - - # Create each site from the CSV file +def add_geocoding_to_json(data): for d in data: # Variables site_id = None @@ -41,9 +33,19 @@ def csv_to_json(file_path): d['gps'] = gps d['country_code'] = country_code d['time_zone'] = time_zone + return data + +if __name__ == '__main__': + + csv_file_path=os.getcwd() + '/../test_data/sites_with_clients.csv' + + # Convert CSV to valid JSON + json_data_without_geocoding = convert_csv_to_json(csv_file_path) + + json_data_with_geocoding = add_geocoding_to_json(json_data_without_geocoding) juniper_script( mist_api_token=os.environ['MIST_API_TOKEN'], org_id=os.environ['ORG_ID'], - data=data + data=json_data_with_geocoding ) diff --git a/test/test_geocode.py b/test/test_geocode.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_main.py b/test/test_main.py new file mode 100644 index 0000000..e69de29 From 1d54bf8de6afe79c183a2183984ab3e051844cdc Mon Sep 17 00:00:00 2001 From: James Green Date: Fri, 1 Dec 2023 15:48:27 +0000 Subject: [PATCH 02/16] added some basic unit tests for main.py --- test/test_main.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/test/test_main.py b/test/test_main.py index e69de29..2e797da 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -0,0 +1,81 @@ +import unittest +import tempfile +import csv +from unittest.mock import patch +from src.main import convert_csv_to_json, add_geocoding_to_json + +class TestCsvToJson(unittest.TestCase): + + def setUp(self): + # Create a temporary CSV file for testing + self.csv_data = [ + {'Site Name': 'Test location 1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'Enable GovWifi': ' "TRUE"', 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '00000DD0000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'}, + {'Site Name': 'Test location 2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'Enable GovWifi': ' "TRUE"', 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'}, + {'Site Name': 'Test location 3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'Enable GovWifi': ' "TRUE"', 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE080BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'} + ] + self.csv_file = tempfile.NamedTemporaryFile(mode='w', delete=False, newline='', suffix='.csv') + self.csv_writer = csv.DictWriter(self.csv_file, fieldnames=[ + 'Site Name', + 'Site Address', + 'Enable GovWifi', + 'Enable MoJWifi', + 'GovWifi Radius Key', + 'Wired NACS Radius Key' + ]) + self.csv_writer.writeheader() + self.csv_writer.writerows(self.csv_data) + self.csv_file.close() + + def test_given_csv_file_when_csv_valid_then_convert_to_json(self): + expected_json = self.csv_data + actual_json = convert_csv_to_json(self.csv_file.name) + self.assertEqual(actual_json, expected_json) + + def test_given_csv_file_when_csv_file_empty_then_raise_value_error(self): + empty_csv_file = tempfile.NamedTemporaryFile(mode='w', delete=False, newline='', suffix='.csv') + empty_csv_file.close() + with self.assertRaises(ValueError) as error: + convert_csv_to_json(empty_csv_file.name) + self.assertEqual(str(error.exception), 'Failed to convert CSV file to JSON. Exiting script.') + + def test_given_file_path_when_csv_file_not_found_then_raise_FileNotFoundError(self): + # Test if the function handles a nonexistent CSV file correctly + nonexistent_file_path = 'nonexistent.csv' + with self.assertRaises(FileNotFoundError): + convert_csv_to_json(nonexistent_file_path) + +class TestAddGeocodingToJson(unittest.TestCase): + + @patch('src.main.geocode', side_effect=[ + {'latitude': 50.3868633, 'longitude': -4.1539256}, + {'latitude': 51.499929300000005, 'longitude': -0.13477761285315926}, + {'latitude': 50.727350349999995, 'longitude': -3.4744726127760086}, + ]) + @patch('src.main.find_country_code', return_value='GB') + @patch('src.main.find_timezone', return_value='Europe/London') + def test_given_site_name_and_site_address_in_json_format_when_function_called_then_add_geocode_country_code_and_time_zone( + self, + find_timezone, + mock_find_country_code, + mock_geocode + ): + # Test if the function adds geocoding information correctly + data = [ + {'Site Name': 'Site1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG'}, + {'Site Name': 'Site2', 'Site Address': '102 Petty France, London SW1H 9AJ'}, + {'Site Name': 'Site3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB'} + ] + + expected_data = [ + {'Site Name': 'Site1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'gps': {'latitude': 50.3868633, 'longitude': -4.1539256}, 'country_code': 'GB', 'time_zone': 'Europe/London'}, + {'Site Name': 'Site2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'gps': {'latitude': 51.499929300000005, 'longitude': -0.13477761285315926}, 'country_code': 'GB', 'time_zone': 'Europe/London'}, + {'Site Name': 'Site3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'gps': {'latitude': 50.727350349999995, 'longitude': -3.4744726127760086}, 'country_code': 'GB', 'time_zone': 'Europe/London'} + ] + + actual_data = add_geocoding_to_json(data) + + self.assertEqual(actual_data, expected_data) + find_timezone.assert_called() + mock_find_country_code.assert_called() + mock_geocode.assert_called() + From 4d3522f9084f4bb8ed0492ca354842ef7a5cbaf0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Dec 2023 16:02:23 +0000 Subject: [PATCH 03/16] Commit changes made by code formatters --- src/main.py | 7 +++++-- test/test_main.py | 33 ++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/main.py b/src/main.py index be0337d..072797f 100644 --- a/src/main.py +++ b/src/main.py @@ -19,6 +19,7 @@ def convert_csv_to_json(file_path): raise ValueError('Failed to convert CSV file to JSON. Exiting script.') return csv_rows + def add_geocoding_to_json(data): for d in data: # Variables @@ -37,14 +38,16 @@ def add_geocoding_to_json(data): d['time_zone'] = time_zone return data + if __name__ == '__main__': - csv_file_path=os.getcwd() + '/../test_data/sites_with_clients.csv' + csv_file_path = os.getcwd() + '/../test_data/sites_with_clients.csv' # Convert CSV to valid JSON json_data_without_geocoding = convert_csv_to_json(csv_file_path) - json_data_with_geocoding = add_geocoding_to_json(json_data_without_geocoding) + json_data_with_geocoding = add_geocoding_to_json( + json_data_without_geocoding) juniper_script( mist_api_token=os.environ['MIST_API_TOKEN'], diff --git a/test/test_main.py b/test/test_main.py index 2e797da..8da86ba 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -4,16 +4,21 @@ from unittest.mock import patch from src.main import convert_csv_to_json, add_geocoding_to_json + class TestCsvToJson(unittest.TestCase): def setUp(self): # Create a temporary CSV file for testing self.csv_data = [ - {'Site Name': 'Test location 1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'Enable GovWifi': ' "TRUE"', 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '00000DD0000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'}, - {'Site Name': 'Test location 2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'Enable GovWifi': ' "TRUE"', 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'}, - {'Site Name': 'Test location 3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'Enable GovWifi': ' "TRUE"', 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE080BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'} + {'Site Name': 'Test location 1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'Enable GovWifi': ' "TRUE"', + 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '00000DD0000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'}, + {'Site Name': 'Test location 2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'Enable GovWifi': ' "TRUE"', + 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'}, + {'Site Name': 'Test location 3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'Enable GovWifi': ' "TRUE"', + 'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE080BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'} ] - self.csv_file = tempfile.NamedTemporaryFile(mode='w', delete=False, newline='', suffix='.csv') + self.csv_file = tempfile.NamedTemporaryFile( + mode='w', delete=False, newline='', suffix='.csv') self.csv_writer = csv.DictWriter(self.csv_file, fieldnames=[ 'Site Name', 'Site Address', @@ -32,11 +37,13 @@ def test_given_csv_file_when_csv_valid_then_convert_to_json(self): self.assertEqual(actual_json, expected_json) def test_given_csv_file_when_csv_file_empty_then_raise_value_error(self): - empty_csv_file = tempfile.NamedTemporaryFile(mode='w', delete=False, newline='', suffix='.csv') + empty_csv_file = tempfile.NamedTemporaryFile( + mode='w', delete=False, newline='', suffix='.csv') empty_csv_file.close() with self.assertRaises(ValueError) as error: convert_csv_to_json(empty_csv_file.name) - self.assertEqual(str(error.exception), 'Failed to convert CSV file to JSON. Exiting script.') + self.assertEqual(str(error.exception), + 'Failed to convert CSV file to JSON. Exiting script.') def test_given_file_path_when_csv_file_not_found_then_raise_FileNotFoundError(self): # Test if the function handles a nonexistent CSV file correctly @@ -44,6 +51,7 @@ def test_given_file_path_when_csv_file_not_found_then_raise_FileNotFoundError(se with self.assertRaises(FileNotFoundError): convert_csv_to_json(nonexistent_file_path) + class TestAddGeocodingToJson(unittest.TestCase): @patch('src.main.geocode', side_effect=[ @@ -63,13 +71,17 @@ def test_given_site_name_and_site_address_in_json_format_when_function_called_th data = [ {'Site Name': 'Site1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG'}, {'Site Name': 'Site2', 'Site Address': '102 Petty France, London SW1H 9AJ'}, - {'Site Name': 'Site3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB'} + {'Site Name': 'Site3', + 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB'} ] expected_data = [ - {'Site Name': 'Site1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'gps': {'latitude': 50.3868633, 'longitude': -4.1539256}, 'country_code': 'GB', 'time_zone': 'Europe/London'}, - {'Site Name': 'Site2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'gps': {'latitude': 51.499929300000005, 'longitude': -0.13477761285315926}, 'country_code': 'GB', 'time_zone': 'Europe/London'}, - {'Site Name': 'Site3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'gps': {'latitude': 50.727350349999995, 'longitude': -3.4744726127760086}, 'country_code': 'GB', 'time_zone': 'Europe/London'} + {'Site Name': 'Site1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'gps': { + 'latitude': 50.3868633, 'longitude': -4.1539256}, 'country_code': 'GB', 'time_zone': 'Europe/London'}, + {'Site Name': 'Site2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'gps': { + 'latitude': 51.499929300000005, 'longitude': -0.13477761285315926}, 'country_code': 'GB', 'time_zone': 'Europe/London'}, + {'Site Name': 'Site3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'gps': { + 'latitude': 50.727350349999995, 'longitude': -3.4744726127760086}, 'country_code': 'GB', 'time_zone': 'Europe/London'} ] actual_data = add_geocoding_to_json(data) @@ -78,4 +90,3 @@ def test_given_site_name_and_site_address_in_json_format_when_function_called_th find_timezone.assert_called() mock_find_country_code.assert_called() mock_geocode.assert_called() - From 9f852bcbb21872e15faebc1811f0b66a2e25eaca Mon Sep 17 00:00:00 2001 From: James Green Date: Fri, 1 Dec 2023 18:40:59 +0000 Subject: [PATCH 04/16] added tests for geocode module --- src/__init__.py | 0 src/geocode.py | 12 +++++++++--- test/__init__.py | 0 test/test_geocode.py | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/__init__.py create mode 100644 test/__init__.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/geocode.py b/src/geocode.py index 7a20589..7049dd1 100644 --- a/src/geocode.py +++ b/src/geocode.py @@ -7,9 +7,15 @@ def geocode(address) -> str: location = geolocator.geocode(address) try: latitude, longitude = location.latitude, location.longitude - except ValueError: - raise ValueError( - 'geocode unable to find latitude & longitude for {address}'.format(address=address)) + except AttributeError as e: + if "'NoneType' object has no attribute 'latitude'" in str(e): + raise AttributeError( + 'geocode unable to find latitude & longitude for {address}'.format(address=address)) + if "'NoneType' object has no attribute 'longitude'" in str(e): + raise AttributeError( + 'geocode unable to find latitude & longitude for {address}'.format(address=address)) + else: + raise # Re-raise the original AttributeError if the message doesn't match return latitude, longitude diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_geocode.py b/test/test_geocode.py index e69de29..2a9f55d 100644 --- a/test/test_geocode.py +++ b/test/test_geocode.py @@ -0,0 +1,38 @@ +import unittest +from unittest.mock import patch, Mock +from src.geocode import geocode + +class TestGeocode(unittest.TestCase): + + @patch('src.geocode.Nominatim.geocode') + def test_given_list_of_valid_addresses_when_geocode_called_then_return_relevant_list_of_gps_locations(self, mock_nominatim): + # Define a list of addresses and their expected results + addresses = [ + ("40 Mayflower Dr, Plymouth PL2 3DG", (50.3868633, -4.1539256)), + ("102 Petty France, London SW1H 9AJ", (51.499929300000005, -0.13477761285315926)), + ("London", (51.4893335, -0.14405508452768728)), + ("Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB", (50.727350349999995, -3.4744726127760086)) + ] + # Mock the geocode method to return the corresponding latitude and longitude + for address, (lat, lon) in addresses: + mock_nominatim.return_value.latitude = lat + mock_nominatim.return_value.longitude = lon + + # Call the geocode function for each address + result = geocode(address) + + # Assert that the result matches the expected latitude and longitude + self.assertEqual(result, (lat, lon)) + + @patch('src.geocode.Nominatim.geocode') + def test_geocode_invalid_address(self, mock_geocode): + # Arrange + address = "Invalid Address" + mock_geocode.return_value = None # Simulate geocode returning None + + # Act & Assert + with self.assertRaises(AttributeError) as context: + geocode(address) + + expected_error_message = 'geocode unable to find latitude & longitude for {address}'.format(address=address) + self.assertEqual(str(context.exception), expected_error_message) \ No newline at end of file From 798298010bbdd8e26f10684df7bc6986a6ea5aae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Dec 2023 18:41:48 +0000 Subject: [PATCH 05/16] Commit changes made by code formatters --- test/test_geocode.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/test_geocode.py b/test/test_geocode.py index 2a9f55d..4fb8ffe 100644 --- a/test/test_geocode.py +++ b/test/test_geocode.py @@ -2,6 +2,7 @@ from unittest.mock import patch, Mock from src.geocode import geocode + class TestGeocode(unittest.TestCase): @patch('src.geocode.Nominatim.geocode') @@ -9,9 +10,11 @@ def test_given_list_of_valid_addresses_when_geocode_called_then_return_relevant_ # Define a list of addresses and their expected results addresses = [ ("40 Mayflower Dr, Plymouth PL2 3DG", (50.3868633, -4.1539256)), - ("102 Petty France, London SW1H 9AJ", (51.499929300000005, -0.13477761285315926)), + ("102 Petty France, London SW1H 9AJ", + (51.499929300000005, -0.13477761285315926)), ("London", (51.4893335, -0.14405508452768728)), - ("Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB", (50.727350349999995, -3.4744726127760086)) + ("Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB", + (50.727350349999995, -3.4744726127760086)) ] # Mock the geocode method to return the corresponding latitude and longitude for address, (lat, lon) in addresses: @@ -34,5 +37,6 @@ def test_geocode_invalid_address(self, mock_geocode): with self.assertRaises(AttributeError) as context: geocode(address) - expected_error_message = 'geocode unable to find latitude & longitude for {address}'.format(address=address) - self.assertEqual(str(context.exception), expected_error_message) \ No newline at end of file + expected_error_message = 'geocode unable to find latitude & longitude for {address}'.format( + address=address) + self.assertEqual(str(context.exception), expected_error_message) From 317bd269bfa6fb424ad2e4661e9726c3564eb019 Mon Sep 17 00:00:00 2001 From: James Green Date: Fri, 1 Dec 2023 18:45:16 +0000 Subject: [PATCH 06/16] added scripts to test juniper module --- test/test_juniper.py | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/test_juniper.py diff --git a/test/test_juniper.py b/test/test_juniper.py new file mode 100644 index 0000000..361330c --- /dev/null +++ b/test/test_juniper.py @@ -0,0 +1,55 @@ +import unittest +from unittest.mock import patch, MagicMock +from src.juniper import juniper_script, Admin + +class TestJuniperScript(unittest.TestCase): + + @patch('src.juniper.Admin.post') + @patch('src.juniper.Admin.put') + def test_juniper_script(self, mock_put, mock_post): + # Mock Mist API responses + mock_post.return_value = {'id': '123', 'name': 'TestSite'} + mock_put.return_value = {'status': 'success'} + + # Sample input data + data = [ + {'Site Name': 'TestSite', 'Site Address': '123 Main St', + 'gps': [1.23, 4.56], 'country_code': 'US', 'time_zone': 'UTC', + 'Enable GovWifi': 'true', 'Enable MoJWifi': 'false', + 'Wired NACS Radius Key': 'key1', 'GovWifi Radius Key': 'key2'} + ] + + # Call the function + juniper_script(data, mist_api_token='your_token', org_id='your_org_id') + + # Assertions + mock_post.assert_called_once_with('/api/v1/orgs/your_org_id/sites', { + 'name': 'TestSite', + 'address': '123 Main St', + 'latlng': {'lat': 1.23, 'lng': 4.56}, + 'country_code': 'US', + 'timezone': 'UTC' + }) + + mock_put.assert_called_once_with('/api/v1/sites/123/setting', { + 'vars': { + 'Enable GovWifi': 'true', + 'Enable MoJWifi': 'false', + 'Wired NACS Radius Key': 'key1', + 'GovWifi Radius Key': 'key2' + } + }) + + def test_juniper_script_missing_token(self): + # Test when mist_api_token is missing + with self.assertRaises(SystemExit) as cm: + juniper_script([], org_id='your_org_id') + + self.assertEqual(cm.exception.code, 1) + + def test_juniper_script_missing_org_id(self): + # Test when org_id is missing + with self.assertRaises(SystemExit) as cm: + juniper_script([], mist_api_token='your_token') + + self.assertEqual(cm.exception.code, 1) From fdb78e8cd0ad045040953eaddd5b35951c1bf107 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Dec 2023 18:46:24 +0000 Subject: [PATCH 07/16] Commit changes made by code formatters --- test/test_juniper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_juniper.py b/test/test_juniper.py index 361330c..42cbdc9 100644 --- a/test/test_juniper.py +++ b/test/test_juniper.py @@ -2,6 +2,7 @@ from unittest.mock import patch, MagicMock from src.juniper import juniper_script, Admin + class TestJuniperScript(unittest.TestCase): @patch('src.juniper.Admin.post') From 7660b7a3ece198c63dff4c0094294eb88f9e31d9 Mon Sep 17 00:00:00 2001 From: James Green Date: Mon, 4 Dec 2023 09:23:25 +0000 Subject: [PATCH 08/16] added more unit tests --- src/geocode.py | 2 +- test/test_geocode.py | 68 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/geocode.py b/src/geocode.py index 7049dd1..de0bf82 100644 --- a/src/geocode.py +++ b/src/geocode.py @@ -36,7 +36,7 @@ def find_timezone(gps) -> tuple: try: timezone_name = tf_init.timezone_at(lat=latitude, lng=longitude) except ValueError: - raise ValueError('The coordinates were out of bounds {latitude}:{longitude}'.format( + raise ValueError('The coordinates were out of bounds {lat}:{lng}'.format( lat=latitude, lng=longitude)) if timezone_name is None: raise ValueError('GPS coordinates did not match a time_zone') diff --git a/test/test_geocode.py b/test/test_geocode.py index 4fb8ffe..a903f49 100644 --- a/test/test_geocode.py +++ b/test/test_geocode.py @@ -1,6 +1,6 @@ import unittest -from unittest.mock import patch, Mock -from src.geocode import geocode +from unittest.mock import patch, MagicMock +from src.geocode import geocode, find_timezone, find_country_code class TestGeocode(unittest.TestCase): @@ -40,3 +40,67 @@ def test_geocode_invalid_address(self, mock_geocode): expected_error_message = 'geocode unable to find latitude & longitude for {address}'.format( address=address) self.assertEqual(str(context.exception), expected_error_message) + + @patch('src.geocode.TimezoneFinder') + def test_find_timezone_valid_coordinates(self, mock_timezone_finder): + tf_instance = MagicMock() + tf_instance.timezone_at.return_value = 'America/New_York' + mock_timezone_finder.return_value = tf_instance + + gps_coordinates = (40.7128, -74.0060) + result = find_timezone(gps_coordinates) + + self.assertEqual(result, 'America/New_York') + + @patch('src.geocode.TimezoneFinder') + def test_find_timezone_out_of_bounds(self, mock_timezone_finder): + tf_instance = MagicMock() + tf_instance.timezone_at.side_effect = ValueError('The coordinates were out of bounds 40.7128:-74.0060') + mock_timezone_finder.return_value = tf_instance + + gps_coordinates = (40.7128, -74.0060) + + with self.assertRaises(ValueError) as context: + find_timezone(gps_coordinates) + + self.assertEqual(str(context.exception), 'The coordinates were out of bounds 40.7128:-74.006') + + @patch('src.geocode.TimezoneFinder') + def test_find_timezone_no_matching_timezone(self, mock_timezone_finder): + tf_instance = MagicMock() + tf_instance.timezone_at.return_value = None + mock_timezone_finder.return_value = tf_instance + + gps_coordinates = (40.7128, -74.0060) + + with self.assertRaises(ValueError) as context: + find_timezone(gps_coordinates) + + self.assertEqual(str(context.exception), 'GPS coordinates did not match a time_zone') + +class TestFindCountryCode(unittest.TestCase): + @patch('src.geocode.Nominatim.geocode') + def test_find_country_code_valid_coordinates(self, mock_nominatim): + geolocator_instance = MagicMock() + location_instance = MagicMock() + location_instance.raw = {'address': {'country_code': 'us'}} + geolocator_instance.reverse.return_value = location_instance + mock_nominatim.return_value = geolocator_instance + + gps_coordinates = (40.7128, -74.0060) + result = find_country_code(gps_coordinates) + + self.assertEqual(result, 'US') + + @patch('src.geocode.Nominatim.geocode') + def test_find_country_code_invalid_coordinates(self, mock_nominatim): + geolocator_instance = MagicMock() + geolocator_instance.reverse.side_effect = Exception('Invalid coordinates') + mock_nominatim.return_value = geolocator_instance + + gps_coordinates = (1000.0, 2000.0) # Invalid coordinates + + with self.assertRaises(Exception) as context: + find_country_code(gps_coordinates) + + self.assertEqual(str(context.exception), 'Must be a coordinate pair or Point') \ No newline at end of file From ecd8cd85c5ecdda4411a72dc05e6b8743d4e9628 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 4 Dec 2023 09:24:08 +0000 Subject: [PATCH 09/16] Commit changes made by code formatters --- test/test_geocode.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test/test_geocode.py b/test/test_geocode.py index a903f49..480ed44 100644 --- a/test/test_geocode.py +++ b/test/test_geocode.py @@ -55,7 +55,8 @@ def test_find_timezone_valid_coordinates(self, mock_timezone_finder): @patch('src.geocode.TimezoneFinder') def test_find_timezone_out_of_bounds(self, mock_timezone_finder): tf_instance = MagicMock() - tf_instance.timezone_at.side_effect = ValueError('The coordinates were out of bounds 40.7128:-74.0060') + tf_instance.timezone_at.side_effect = ValueError( + 'The coordinates were out of bounds 40.7128:-74.0060') mock_timezone_finder.return_value = tf_instance gps_coordinates = (40.7128, -74.0060) @@ -63,7 +64,8 @@ def test_find_timezone_out_of_bounds(self, mock_timezone_finder): with self.assertRaises(ValueError) as context: find_timezone(gps_coordinates) - self.assertEqual(str(context.exception), 'The coordinates were out of bounds 40.7128:-74.006') + self.assertEqual(str(context.exception), + 'The coordinates were out of bounds 40.7128:-74.006') @patch('src.geocode.TimezoneFinder') def test_find_timezone_no_matching_timezone(self, mock_timezone_finder): @@ -76,7 +78,9 @@ def test_find_timezone_no_matching_timezone(self, mock_timezone_finder): with self.assertRaises(ValueError) as context: find_timezone(gps_coordinates) - self.assertEqual(str(context.exception), 'GPS coordinates did not match a time_zone') + self.assertEqual(str(context.exception), + 'GPS coordinates did not match a time_zone') + class TestFindCountryCode(unittest.TestCase): @patch('src.geocode.Nominatim.geocode') @@ -95,7 +99,8 @@ def test_find_country_code_valid_coordinates(self, mock_nominatim): @patch('src.geocode.Nominatim.geocode') def test_find_country_code_invalid_coordinates(self, mock_nominatim): geolocator_instance = MagicMock() - geolocator_instance.reverse.side_effect = Exception('Invalid coordinates') + geolocator_instance.reverse.side_effect = Exception( + 'Invalid coordinates') mock_nominatim.return_value = geolocator_instance gps_coordinates = (1000.0, 2000.0) # Invalid coordinates @@ -103,4 +108,5 @@ def test_find_country_code_invalid_coordinates(self, mock_nominatim): with self.assertRaises(Exception) as context: find_country_code(gps_coordinates) - self.assertEqual(str(context.exception), 'Must be a coordinate pair or Point') \ No newline at end of file + self.assertEqual(str(context.exception), + 'Must be a coordinate pair or Point') From e098a200443dfd3b40ce8de89b291a7e9463078c Mon Sep 17 00:00:00 2001 From: James Green Date: Mon, 4 Dec 2023 13:07:21 +0000 Subject: [PATCH 10/16] added dockerfile to enable write once run anywhere testing --- Dockerfile | 17 +++++++++++++++++ makefile | 5 +++++ 2 files changed, 22 insertions(+) create mode 100644 Dockerfile create mode 100644 makefile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..89387f0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.11.5 + +# Set the working directory to /app +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . /app + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r src/requirements.txt + +# Set the src directory +ENV PYTHONPATH=/app/src + +# Run tests when the container launches +CMD ["python", "-m", "unittest", "discover", "-s", "/app", "-t", "/app"] + diff --git a/makefile b/makefile new file mode 100644 index 0000000..0c018c7 --- /dev/null +++ b/makefile @@ -0,0 +1,5 @@ +docker-build: + docker build -t juniper-mist-tests . + +docker-run: + docker run -v src:/app/src -v test:/app/tests juniper-mist-tests \ No newline at end of file From a1285ffe21d798de8661f7421764dd2c30cfe8e3 Mon Sep 17 00:00:00 2001 From: James Green Date: Tue, 5 Dec 2023 15:03:32 +0000 Subject: [PATCH 11/16] added the ability to run docker --- .gitignore | 2 +- README.md | 72 +++++++------------------------- {test_data => data_src}/.gitkeep | 0 Dockerfile => docker/Dockerfile | 13 ++++-- docker/entrypoint.sh | 7 ++++ makefile | 47 +++++++++++++++++++-- src/main.py | 2 +- 7 files changed, 76 insertions(+), 67 deletions(-) rename {test_data => data_src}/.gitkeep (100%) rename Dockerfile => docker/Dockerfile (50%) create mode 100644 docker/entrypoint.sh diff --git a/.gitignore b/.gitignore index 3aa3450..460222a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,6 @@ env/ *.sha256 terraform.tfstate -test_data/**.csv +data_src/**.csv ./idea/* **/__pycache__/ diff --git a/README.md b/README.md index 967ab38..88f8305 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,20 @@ -# Ministry of Justice Template Repository +# Juniper mist integration script -[![repo standards badge](https://img.shields.io/endpoint?labelColor=231f20&color=005ea5&style=for-the-badge&label=MoJ%20Compliant&url=https%3A%2F%2Foperations-engineering-reports.cloud-platform.service.justice.gov.uk%2Fapi%2Fv1%2Fcompliant_public_repositories%2Fendpoint%2Ftemplate-repository&logo=data:image/png;base64,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)](https://operations-engineering-reports.cloud-platform.service.justice.gov.uk/public-report/template-repository) +This repo has been developed by the DevOps Lan&Wi-Fi to automate site creation on juniper mist. -This template repository equips you with the default initial files required for a Ministry of Justice GitHub repository. +## Run script -## Included Files +## Development setup -The repository comes with the following preset files: +### Prerequisites +* Docker +* IDE that integrates with docker (We use IntelliJ in this example) -- LICENSE -- .gitignore -- CODEOWNERS -- dependabot.yml -- GitHub Actions example file -- Ministry of Justice Compliance Badge (Public repositories only) +### Setup +* Run `make build-dev` +* Integrate built docker container with IDE. [here](https://www.jetbrains.com/help/idea/configuring-remote-python-sdks.html#2546d02c) is the example for intelliJ +* mark src directory & test directory within the IDE. [here](https://www.jetbrains.com/help/idea/content-roots.html) -## Setup Instructions - -Once you've created your repository using this template, ensure the following steps: - -### Update README - -Edit this README.md file to document your project accurately. Take the time to create a clear, engaging, and informative README.md file. Include information like what your project does, how to install and run it, how to contribute, and any other pertinent details. - -### Update repository description - -After you've created your repository, GitHub provides a brief description field that appears on the top of your repository's main page. This is a summary that gives visitors quick insight into the project. Using this field to provide a succinct overview of your repository is highly recommended. - -This description and your README.md will be one of the first things people see when they visit your repository. It's a good place to make a strong, concise first impression. Remember, this is often visible in search results on GitHub and search engines, so it's also an opportunity to help people discover your project. - -### Grant Team Permissions - -Assign permissions to the appropriate Ministry of Justice teams. Ensure at least one team is granted Admin permissions. Whenever possible, assign permissions to teams rather than individual users. - -### Read about the GitHub repository standards - -Familiarise yourself with the Ministry of Justice GitHub Repository Standards. These standards ensure consistency, maintainability, and best practices across all our repositories. - -You can find the standards [here](https://operations-engineering.service.justice.gov.uk/documentation/services/repository-standards.html). - -Please read and understand these standards thoroughly and enable them when you feel comfortable. - -### Modify the GitHub Standards Badge - -Once you've ensured that all the [GitHub Repository Standards](https://operations-engineering.service.justice.gov.uk/documentation/services/repository-standards.html) have been applied to your repository, it's time to update the Ministry of Justice (MoJ) Compliance Badge located in the README file. - -The badge demonstrates that your repository is compliant with MoJ's standards. Please follow these [instructions](https://operations-engineering.service.justice.gov.uk/documentation/runbooks/services/add-repo-badge.html) to modify the badge URL to reflect the status of your repository correctly. - -**Please note** the badge will not function correctly if your repository is internal or private. In this case, you may remove the badge from your README. - -### Manage Outside Collaborators - -To add an Outside Collaborator to the repository, follow the guidelines detailed [here](https://github.com/ministryofjustice/github-collaborators). - -### Update CODEOWNERS - -(Optional) Modify the CODEOWNERS file to specify the teams or users authorized to approve pull requests. - -### Configure Dependabot - -Adapt the dependabot.yml file to match your project's [dependency manager](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem) and to enable [automated pull requests for package updates](https://docs.github.com/en/code-security/supply-chain-security). - -If your repository is private with no GitHub Advanced Security license, remove the .github/workflows/dependency-review.yml file. +# Notes +* To see options run `make help` +* If you update [requirements.txt](src/requirements.txt), you will need to rebuild the docker container. diff --git a/test_data/.gitkeep b/data_src/.gitkeep similarity index 100% rename from test_data/.gitkeep rename to data_src/.gitkeep diff --git a/Dockerfile b/docker/Dockerfile similarity index 50% rename from Dockerfile rename to docker/Dockerfile index 89387f0..c07a078 100644 --- a/Dockerfile +++ b/docker/Dockerfile @@ -4,14 +4,21 @@ FROM python:3.11.5 WORKDIR /app # Copy the current directory contents into the container at /app -COPY . /app +COPY ../src/requirements.txt /tmp/requirements.txt + +# Copy entrypoint +COPY docker/entrypoint.sh /entrypoint.sh +COPY src/ /app/src + +# Make entrypoint executable +RUN chmod +x /entrypoint.sh # Install any needed packages specified in requirements.txt -RUN pip install --no-cache-dir -r src/requirements.txt +RUN pip install --no-cache-dir -r ../tmp/requirements.txt # Set the src directory ENV PYTHONPATH=/app/src # Run tests when the container launches -CMD ["python", "-m", "unittest", "discover", "-s", "/app", "-t", "/app"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..d4f210b --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ "$RUN_UNIT_TESTS" = True ] ; then + python -m unittest discover -s /app -t /app +else + python /app/src/main.py +fi \ No newline at end of file diff --git a/makefile b/makefile index 0c018c7..c64de3a 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,44 @@ -docker-build: - docker build -t juniper-mist-tests . +#!make +.DEFAULT_GOAL := help +SHELL := '/bin/bash' -docker-run: - docker run -v src:/app/src -v test:/app/tests juniper-mist-tests \ No newline at end of file +.PHONY: build +build: ## Build the docker container + docker build -t juniper-mist -f docker/Dockerfile . + +.PHONO: create-dir +make create-dir: ## Creates a directory for end user to put CSV file into + mkdir data_src; + echo "Please put csv file into data_src then run 'make-prod'"; + +.PHONY: run-prod +run-prod: ## Run the python script only mounting the host for csv-file. + docker run -v $(shell pwd)/data_src:/data_src \ + -e MIST_API_TOKEN=$$MIST_API_TOKEN \ + -e ORG_ID=$$ORG_ID \ + juniper-mist + +.PHONY: run-dev +run-dev: ## Run the python script while mounting the host. This enables using the latest local src code without needing to wait for a container build. + docker run -v $(shell pwd)/src:/app/src \ + -v $(shell pwd)/data_src:/data_src \ + -e MIST_API_TOKEN=$$MIST_API_TOKEN \ + -e ORG_ID=$$ORG_ID \ + juniper-mist + +.PHONY: tests +tests: ## Run unit tests for the python app + docker run -v $(shell pwd)/src:/app/src \ + -v $(shell pwd)/test:/app/test \ + -e RUN_UNIT_TESTS=True juniper-mist + +.PHONY: shell +shell: ## Make interactive docker container + docker run -it --entrypoint /bin/bash \ + -v $(shell pwd)/src:/app/src \ + -v $(shell pwd)/test:/app/test \ + -v $(shell pwd)/data_src:/data_src \ + -e RUN_UNIT_TESTS=True juniper-mist + +help: + @grep -h -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' \ No newline at end of file diff --git a/src/main.py b/src/main.py index 072797f..de7b708 100644 --- a/src/main.py +++ b/src/main.py @@ -41,7 +41,7 @@ def add_geocoding_to_json(data): if __name__ == '__main__': - csv_file_path = os.getcwd() + '/../test_data/sites_with_clients.csv' + csv_file_path = os.getcwd() + '/../data_src/sites_with_clients.csv' # Convert CSV to valid JSON json_data_without_geocoding = convert_csv_to_json(csv_file_path) From ec8dd157bd546332d2ef10abb44823cea3269903 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 5 Dec 2023 15:04:21 +0000 Subject: [PATCH 12/16] Commit changes made by code formatters --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 88f8305..8d2adc3 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,17 @@ This repo has been developed by the DevOps Lan&Wi-Fi to automate site creation o ## Development setup ### Prerequisites -* Docker -* IDE that integrates with docker (We use IntelliJ in this example) + +- Docker +- IDE that integrates with docker (We use IntelliJ in this example) ### Setup -* Run `make build-dev` -* Integrate built docker container with IDE. [here](https://www.jetbrains.com/help/idea/configuring-remote-python-sdks.html#2546d02c) is the example for intelliJ -* mark src directory & test directory within the IDE. [here](https://www.jetbrains.com/help/idea/content-roots.html) + +- Run `make build-dev` +- Integrate built docker container with IDE. [here](https://www.jetbrains.com/help/idea/configuring-remote-python-sdks.html#2546d02c) is the example for intelliJ +- mark src directory & test directory within the IDE. [here](https://www.jetbrains.com/help/idea/content-roots.html) # Notes -* To see options run `make help` -* If you update [requirements.txt](src/requirements.txt), you will need to rebuild the docker container. + +- To see options run `make help` +- If you update [requirements.txt](src/requirements.txt), you will need to rebuild the docker container. From fa0c0243a7f3c1b09add3b845d99995a0bbb79e1 Mon Sep 17 00:00:00 2001 From: James Green Date: Tue, 5 Dec 2023 15:07:13 +0000 Subject: [PATCH 13/16] updated requests to get rid of CVE --- src/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/requirements.txt b/src/requirements.txt index cf38b7e..f6f697c 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,3 +1,3 @@ -requests==2.21.0 +requests==2.31.0 geopy==2.4.1 timezonefinder==6.2.0 \ No newline at end of file From 6503853a291bf6cea726e01860189b1ce16ee90b Mon Sep 17 00:00:00 2001 From: James Green Date: Tue, 5 Dec 2023 15:08:59 +0000 Subject: [PATCH 14/16] pr comment --- test/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_main.py b/test/test_main.py index 8da86ba..e2b06a9 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -31,7 +31,7 @@ def setUp(self): self.csv_writer.writerows(self.csv_data) self.csv_file.close() - def test_given_csv_file_when_csv_valid_then_convert_to_json(self): + def test_convert_csv_to_json_valid_csv(self): expected_json = self.csv_data actual_json = convert_csv_to_json(self.csv_file.name) self.assertEqual(actual_json, expected_json) From edf8aaca6131e314a32981c66c6cae4c0cf129bd Mon Sep 17 00:00:00 2001 From: James Green Date: Tue, 5 Dec 2023 15:19:30 +0000 Subject: [PATCH 15/16] corrected mistake in README --- README.md | 2 +- makefile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d2adc3..83ddd1e 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This repo has been developed by the DevOps Lan&Wi-Fi to automate site creation o ### Setup -- Run `make build-dev` +- Run `make build` - Integrate built docker container with IDE. [here](https://www.jetbrains.com/help/idea/configuring-remote-python-sdks.html#2546d02c) is the example for intelliJ - mark src directory & test directory within the IDE. [here](https://www.jetbrains.com/help/idea/content-roots.html) diff --git a/makefile b/makefile index c64de3a..ed4b329 100644 --- a/makefile +++ b/makefile @@ -12,14 +12,14 @@ make create-dir: ## Creates a directory for end user to put CSV file into echo "Please put csv file into data_src then run 'make-prod'"; .PHONY: run-prod -run-prod: ## Run the python script only mounting the host for csv-file. +run-prod: ## Run the python script only mounting the host for csv-file. Format: MIST_API_TOKEN=foo ORG_ID=bar make run-prod docker run -v $(shell pwd)/data_src:/data_src \ -e MIST_API_TOKEN=$$MIST_API_TOKEN \ -e ORG_ID=$$ORG_ID \ juniper-mist .PHONY: run-dev -run-dev: ## Run the python script while mounting the host. This enables using the latest local src code without needing to wait for a container build. +run-dev: ## Run the python script while mounting the host. This enables using the latest local src code without needing to wait for a container build. Format: MIST_API_TOKEN=foo ORG_ID=bar make run-dev docker run -v $(shell pwd)/src:/app/src \ -v $(shell pwd)/data_src:/data_src \ -e MIST_API_TOKEN=$$MIST_API_TOKEN \ From 59bd50c21dc7e5b67facda3e2e021d36593490d7 Mon Sep 17 00:00:00 2001 From: James Green Date: Tue, 5 Dec 2023 16:15:25 +0000 Subject: [PATCH 16/16] added line break at the end of bash and make files --- docker/entrypoint.sh | 2 +- makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index d4f210b..cc0f248 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -4,4 +4,4 @@ if [ "$RUN_UNIT_TESTS" = True ] ; then python -m unittest discover -s /app -t /app else python /app/src/main.py -fi \ No newline at end of file +fi diff --git a/makefile b/makefile index ed4b329..a3108f9 100644 --- a/makefile +++ b/makefile @@ -41,4 +41,4 @@ shell: ## Make interactive docker container -e RUN_UNIT_TESTS=True juniper-mist help: - @grep -h -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' \ No newline at end of file + @grep -h -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'