diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1985ff3 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +WIFI_PASSWORD = +DISCORD_WEBHOOK = +DEVICE_ID = +NETWORK_NAME = \ No newline at end of file diff --git a/.flake8 b/.flake8 index 3550ac1..3ae0aa4 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ [flake8] -max-line-length = 140 +max-line-length = 180 # Exclude certain file patterns from checking exclude = diff --git a/.gitignore b/.gitignore index b602694..0a51b30 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ Thumbs.db data/ .ecu_data -512311_xk3jq6ao_config.json \ No newline at end of file +512311_xk3jq6ao_config.json +512311__config.json \ No newline at end of file diff --git a/box_client/box_client.py b/box_client/box_client.py deleted file mode 100644 index ff6f2a7..0000000 --- a/box_client/box_client.py +++ /dev/null @@ -1,139 +0,0 @@ -import requests -import jwt -import time -import json -import os -import secrets -from cryptography.hazmat.primitives.serialization import load_pem_private_key -from cryptography.hazmat.backends import default_backend -import logging - -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') - -TOKEN_URL = "https://api.box.com/oauth2/token" -UPLOAD_URL = "https://upload.box.com/api/2.0/files/content" -USER_INFO_URL = "https://api.box.com/2.0/users/me" - -config = json.load( - open('512311_xk3jq6ao_config.json')) - -key_id = config['boxAppSettings']['appAuth']['publicKeyID'] - - -class Client: - - def __init__(self, client_id: str, client_secret: str, file_path: str, folder_id: int): - self.client_id = config['boxAppSettings']['clientID'] - self.client_secret = config['boxAppSettings']['clientSecret'] - self.file_path = file_path - self.folder_id = folder_id - - def retrieve_access_token(self) -> str: - appAuth = config['boxAppSettings']['appAuth'] - private_key = appAuth['privateKey'] - password = appAuth['passphrase'] - - key = load_pem_private_key( - data=private_key.encode('utf8'), - password=password.encode('utf8'), - backend=default_backend() - ) - - claims = { - 'iss': config['boxAppSettings']['clientID'], - 'sub': config['enterpriseID'], - 'box_sub_type': 'enterprise', - 'aud': TOKEN_URL, - 'jti': secrets.token_hex(64), - 'exp': int(time.time()) + 60 - } - - assertion = jwt.encode( - claims, - key, - algorithm='RS512', - headers={ - 'kid': key_id - } - ) - - params = { - 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', - 'assertion': assertion, - 'client_id': self.client_id, - 'client_secret': self.client_secret - } - - try: - logging.info("Retrieving box access token") - response = requests.post(TOKEN_URL, data=params) - - if response.status_code == 200: - - access_token = response.json().get("access_token") - - return access_token - - except requests.exceptions.RequestException as error: - logging.exception(f'Request Token Exception: {error}') - print(f'Request Exception: {error}') - else: - logging.error(f"Error: {response.status_code}") - print(f"Error: {response.status_code}") - print(response.text) - return None - - def send_files(self) -> bool: - access_token = self.retrieve_access_token() - file_name = os.path.basename(self.file_path) - - headers = { - "Authorization": f"Bearer {access_token}", - } - - with open(self.file_path, 'rb') as file_to_upload: - - files = { - 'file': (file_name, file_to_upload), - 'attributes': (None, json.dumps({'parent': {'id': '217403389478'}})), - } - - # print(json.dumps({'parent': {'id': '217403389478'}})) - - try: - logging.info("Attempting uploading file to Box") - response = requests.post( - UPLOAD_URL, headers=headers, files=files) - - if response.status_code == 201: - logging.info("File uploaded to Box") - return True - else: - logging.error(f"Error uploading file: {response.status_code}") - print(f"Error: {response.status_code}") - print(response.json()) - return False - - except requests.exceptions.RequestException as error: - logging.exception(f'Request Exception: {error}') - print(f'Request Exception: {error}') - return False - - def get_user_info(self): - - headers = { - "Authorization": f"Bearer {self.retrieve_access_token()}", - } - - try: - response = requests.get(USER_INFO_URL, headers=headers) - - if response.status_code == 200: - return response.json() - else: - print(f"Error: {response.status_code}") - print(response.json()) - return False - except requests.exceptions.RequestException as error: - print(f'Request Exception: {error}') - return False diff --git a/discord_client/__init__.py b/data_manipulation/__init__.py similarity index 100% rename from discord_client/__init__.py rename to data_manipulation/__init__.py diff --git a/data_deserializer.py b/data_manipulation/data_deserializer.py similarity index 100% rename from data_deserializer.py rename to data_manipulation/data_deserializer.py diff --git a/mongo_client/__init__.py b/data_uploader/__init__.py similarity index 100% rename from mongo_client/__init__.py rename to data_uploader/__init__.py diff --git a/box_client/__init__.py b/data_uploader/box_client/__init__.py similarity index 100% rename from box_client/__init__.py rename to data_uploader/box_client/__init__.py diff --git a/data_uploader/box_client/box_client.py b/data_uploader/box_client/box_client.py new file mode 100644 index 0000000..d0076d4 --- /dev/null +++ b/data_uploader/box_client/box_client.py @@ -0,0 +1,146 @@ +import requests +import jwt +import time +import json +import os +import secrets +from cryptography.hazmat.primitives.serialization import load_pem_private_key +from cryptography.hazmat.backends import default_backend + +TOKEN_URL = os.getenv("TOKEN_URL") +UPLOAD_URL = os.getenv("UPLOAD_URL") +USER_INFO_URL = os.getenv("USER_INFO_URL") +ROOT_FILE_PATH = os.getenv("ROOT_FILE_PATH") + + +class Client: + + def __init__(self, client_id: str, client_secret: str, enterprise_id: str, key_id: str, private_key: str, password: str, folder_path: str, folder_id: int): + self.client_id = client_id + self.client_secret = client_secret + self.enterprise_id = enterprise_id + self.key_id = key_id + self.private_key = private_key + self.password = password + self.folder_path = folder_path + self.folder_id = folder_id + + def retrieve_access_token(self) -> str: + key_id = self.key_id + private_key = self.private_key + password = self.password + + key = load_pem_private_key( + data=private_key.encode('utf8'), + password=password.encode('utf8'), + backend=default_backend() + ) + + claims = { + 'iss': self.client_id, + 'sub': self.enterprise_id, + 'box_sub_type': 'enterprise', + 'aud': TOKEN_URL, + 'jti': secrets.token_hex(64), + 'exp': int(time.time()) + 60 + } + + assertion = jwt.encode( + claims, + key, + algorithm='RS512', + headers={ + 'kid': key_id + } + ) + + params = { + 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', + 'assertion': assertion, + 'client_id': self.client_id, + 'client_secret': self.client_secret + } + + try: + response = requests.post(TOKEN_URL, data=params) + + if response.status_code == 200: + + access_token = response.json().get("access_token") + + return access_token + + except requests.exceptions.RequestException as error: + print(f'Request Exception: {error}') + else: + print(f"Error: {response.status_code}") + print(response.text) + return None + + def send_files(self, filenames: list) -> bool: + access_token = self.retrieve_access_token() + + for filename in filenames: + + file_name = os.path.basename(filename) + + headers = { + "Authorization": f"Bearer {access_token}", + } + + with open(filename, 'rb') as file_to_upload: + + files = { + 'file': (filename, file_to_upload), + 'attributes': (None, json.dumps({'parent': {'id': str(self.folder_id)}, 'name': file_name}), 'application/json'), + } + + try: + response = requests.post(UPLOAD_URL, headers=headers, files=files) + + if response.status_code == 201: + continue + else: + print(f"Error: {response.status_code}") + print(response.json()) + return False + + except requests.exceptions.RequestException as error: + print(f'Request Exception: {error}') + return False + + return True + + def get_user_info(self): + + headers = { + "Authorization": f"Bearer {self.retrieve_access_token()}", + } + + try: + response = requests.get(USER_INFO_URL, headers=headers) + + if response.status_code == 200: + return response.json() + else: + print(f"Error: {response.status_code}") + print(response.json()) + return False + except requests.exceptions.RequestException as error: + print(f'Request Exception: {error}') + return False + + def discover_files(self) -> list: + + list_of_files = [] + + if os.path.exists(self.folder_path) and os.path.isdir(self.folder_path): + files = os.listdir(self.folder_path) + + for file in files: + list_of_files.append(ROOT_FILE_PATH + file) + print(file) + + return list_of_files + + return None diff --git a/wifi_client/__init__.py b/data_uploader/discord_client/__init__.py similarity index 100% rename from wifi_client/__init__.py rename to data_uploader/discord_client/__init__.py diff --git a/discord_client/discord_client.py b/data_uploader/discord_client/discord_client.py similarity index 100% rename from discord_client/discord_client.py rename to data_uploader/discord_client/discord_client.py diff --git a/data_uploader/discord_client/messages.py b/data_uploader/discord_client/messages.py new file mode 100644 index 0000000..2a209fd --- /dev/null +++ b/data_uploader/discord_client/messages.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class Messages(str, Enum): + MONGO_SUCCESS_MESSAGE = "Docuemnts inserted Successfully" + MONGO_ERROR_MESSAGE = "An error occurred while trying to connect to MongoDB: {e}" + MONGO_CLOSE_MESSAGE = "MongoDB connection closed." + BOX_SUCCESS_MESSAGE = "Files uploaded successfully" + BOX_ERROR_MESSAGE = "Error occurred while trying to upload files to Box" + WIFI_SUCCESS_MESSAGE = "Connected to network successfully" + WIFI_ERROR_MESSAGE = "Error occurred while trying to connect to network" diff --git a/data_uploader/handler.py b/data_uploader/handler.py new file mode 100644 index 0000000..68180d6 --- /dev/null +++ b/data_uploader/handler.py @@ -0,0 +1,64 @@ +from data_uploader.box_client.box_client import Client as BoxClient +from data_uploader.discord_client.discord_client import Client as DiscordClient +from data_uploader.discord_client.messages import Messages as discord_messages +from data_uploader.mongo_client.mongo_client import Client as MongoClient +from data_uploader.wifi_client.wifi_client import Client as WifiClient + + +import os +from dotenv import load_dotenv +import json + + +config = json.load( + open('512311_xk3jq6ao_config.json') +) + + +class Handler: + + def handler(): + + load_dotenv() + NETWORK_NAME = os.getenv('NETWORK_NAME') + WIFI_PASSWORD = os.getenv('NETWORK_PASSWORD') + WEBHOOK_URL = os.getenv('DISCORD_WEBHOOK') + + wifi_client = WifiClient(NETWORK_NAME, WIFI_PASSWORD) + discord_client = DiscordClient(WEBHOOK_URL) + box_client = BoxClient(config['boxAppSettings']['clientID'], config['boxAppSettings']['clientSecret'], config['enterpriseID'], config['appAuth']['publicKeyID'], + config['appAuth']['privateKey'], config['boxAppSettings']['appAuth']['passphrase'], config['file_path'], config['folder_id']) + mongo_client = MongoClient('cluster0', 'dfr_sensor_data') + + files_for_upload = [] + + if wifi_client.connect_to_network(): + + discord_client.post_message(discord_messages.WIFI_SUCCESS_MESSAGE) + + try: + files_for_upload = box_client.discover_files() + box_client.send_files(files_for_upload) + + discord_client.post_message(discord_messages.BOX_SUCCESS_MESSAGE) + + mongo_client.check_connection() + mongo_client.insert_documents(files_for_upload) + discord_client.post_message(discord_messages.MONGO_SUCCESS_MESSAGE) + mongo_client.close_connection() + discord_client.post_message(discord_messages.MONGO_CLOSE_MESSAGE) + + discord_client.post_message("Documents inserted into MongoDB") + except Exception as e: + print.traceback(e) + discord_client.post_message(f"An error occurred: {e}") + return + + discord_client.post_message(discord_messages.WIFI_ERROR_MESSAGE) + + return None + + +if __name__ == "__main__": + handler = Handler() + handler.handler() diff --git a/data_uploader/mongo_client/__init__.py b/data_uploader/mongo_client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mongo_client/mongo_client.py b/data_uploader/mongo_client/mongo_client.py similarity index 100% rename from mongo_client/mongo_client.py rename to data_uploader/mongo_client/mongo_client.py diff --git a/data_uploader/wifi_client/__init__.py b/data_uploader/wifi_client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/data_uploader/wifi_client/wifi_client.py b/data_uploader/wifi_client/wifi_client.py new file mode 100644 index 0000000..f97dd02 --- /dev/null +++ b/data_uploader/wifi_client/wifi_client.py @@ -0,0 +1,73 @@ +import os +import subprocess +import re +import traceback + +''' +This script removes all saved networks to ensure that the specific +network passed is chosen when restarting the wifi adapter + +We could try testing if appending to the wpa_supplicant.conf file +works so other saved wifi networks can remain +''' + + +class Client: + def __init__(self, network_name: str, network_password: str): + self.network_name = network_name + self.network_password = network_password + + def scan_for_networks(self) -> list: + try: + devices = subprocess.check_output(['sudo', 'iwlist', 'wlan0', 'scan']) + network_names = devices.decode('utf-8') + network_names = re.findall(r'ESSID:"(.*?)"', devices) + + return network_names + + except subprocess.CalledProcessError as err: + traceback.print_exc(err.returncode) + return [] + + def find_network(self) -> bool: + found = False + + for network_ssid in self.scan_for_networks(): + print(self.network_name, network_ssid) + if network_ssid == self.network_name: + print("Found home network") + found = True + break + + return found + + def connect_to_network(self) -> bool: + if self.find_network(): + try: + network = subprocess.check_output(['wpa_passphrase', self.network_name, self.network_password], stderr=subprocess.STDOUT) + with open("/etc/wpa_supplicant/wpa_supplicant.conf", "w+") as fp: + fp.write("ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\ncountry=US\n") + fp.write(network.decode("utf8")) + + subprocess.check_output(["sudo", "ifconfig", "eth0", "down"]) + subprocess.check_output(["wpa_cli", "-i", "wlan0", "reconfigure"]) + return True + + except subprocess.CalledProcessError as err: + traceback.print_exc(err.returncode) + return False + else: + print("Could not find network") + return False + + +def main(): + name = os.getenv('NETWORK_NAME') + password = os.getenv('NETWORK_PASSWORD') + + client = Client(name, password) + print(client.connect_to_network()) + + +if __name__ == "__main__": + main() diff --git a/decrepicated/oauth_method.txt b/deprecated/oauth_method.txt similarity index 100% rename from decrepicated/oauth_method.txt rename to deprecated/oauth_method.txt diff --git a/handler.py b/handler.py deleted file mode 100644 index 9629299..0000000 --- a/handler.py +++ /dev/null @@ -1,65 +0,0 @@ -from box_client.box_client import Client as BoxClient -from discord_client.discord_client import Client as DiscordClient -from mongo_client.mongo_client import Client as MongoClient -from wifi_client.wifi_client import Client as WifiClient - -import logging -import os -from dotenv import load_dotenv -import pandas as pd - - -class Handler: - - def handler(): - - load_dotenv() - WIFI_PASSWORD = os.getenv('WIFI_PASSWORD') - WEBHOOK_URL = os.getenv('DISCORD_WEBHOOK') - - if not WIFI_PASSWORD or not WEBHOOK_URL: - logging.error("WIFI_PASSWORD or DISCORD_WEBHOOK is not set") - - wifi_client = WifiClient('device_id', 'home_network', WIFI_PASSWORD) - discord_client = DiscordClient(WEBHOOK_URL) - box_client = BoxClient() - mongo_client = MongoClient('cluster0', 'dfr_sensor_data') - - wifi_networks = wifi_client.get_wifi_networks() - - logging.info("Attempting to connect to wifi") - - if 'NETGEAR76' in wifi_networks: - logging.info("NETGEAR76 found in wifi networks") - - try: - wifi_client.connect() - box_client.send_files() - discord_client.post_message("File uploaded to Box") - Handler.mongo_upload_handler(mongo_client) - - discord_client.post_message("Documents inserted into MongoDB") - logging.info("Documents inserted into MongoDB") - except Exception as e: - logging.exception(f"An error occurred: {e}") - print.traceback(e) - discord_client.post_message(f"An error occurred: {e}") - return - - logging.error("Wifi network not found in wifi networks") - return None - - @staticmethod - def mongo_upload_handler(mongo_client): - mongo_client.check_connection() - - ecu_df = pd.read_csv("data/ecu_data/ecu_data.csv") - ecu_dict = ecu_df.to_dict("records") - - # mongo_client.insert_documents(ecu_dict) - mongo_client.close_connection() - - -if __name__ == "__main__": - handler = Handler() - handler.handler() diff --git a/network/identify_network.py b/network/identify_network.py deleted file mode 100644 index 89a1b19..0000000 --- a/network/identify_network.py +++ /dev/null @@ -1,25 +0,0 @@ -import platform -import subprocess - - -class Client: - - # make this return a boolean - def get_wifi_networks() -> bool: - try: - if platform.system().lower() == 'windows': - networks = subprocess.check_output( - ['netsh', 'wlan', 'show', 'network']) - elif platform.system().lower() == 'darwin': - networks = subprocess.check_output( - ['/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport', '-s']) - else: - print("This script is intended for Windows or macOS systems.") - return - - decoded_nw = networks.decode('ascii') - print(decoded_nw) - return decoded_nw - - except subprocess.CalledProcessError as e: - print(f"Error: {e}") diff --git a/tests/conftest.py b/tests/conftest.py index f859a91..49a79a8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,18 +1,27 @@ -from box_client.box_client import Client as Box_Client -from mongo_client.mongo_client import Client as Mongo_Client +from data_uploader.box_client.box_client import Client as Box_Client +from data_uploader.mongo_client.mongo_client import Client as Mongo_Client import pytest import os +import json uri = f"mongodb+srv://noel:${os.getenv('MONGO_PASSWORD')}@cluster0.gw7z3sn.mongodb.net/?retryWrites=true&w=majority" +config = json.load( + open('512311_xk3jq6ao_config.json') +) + @pytest.fixture def box_client(): return Box_Client( - client_id=os.getenv("CLIENT_ID"), - client_secret=os.getenv("CLIENT_SECRET"), - file_path="/Users/noeljohnson/Documents/file.txt", - folder_id="217403389478" + client_id=config['boxAppSettings']['clientID'], + client_secret=config['boxAppSettings']['clientSecret'], + enterprise_id=config['enterpriseID'], + key_id=config['boxAppSettings']['appAuth']['publicKeyID'], + private_key=config['boxAppSettings']['appAuth']['privateKey'], + password=config['boxAppSettings']['appAuth']['passphrase'], + folder_path=config['folder_path'], + folder_id=config['folder_id'] ) diff --git a/tests/test_handler.py b/tests/test_handler.py index e6bfb50..9b78560 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -3,8 +3,14 @@ def test_access_token(box_client): assert isinstance(request, str) -def test_send_file(box_client): - request = box_client.send_file() +def test_send_files(box_client): + + list_of_files = [ + r"data\ecu4.csv", + r"data\ecu2.csv" + ] + + request = box_client.send_files(list_of_files) assert request is True @@ -13,6 +19,11 @@ def test_get_user_info(box_client): assert isinstance(request, dict) +def test_discover_file(box_client): + file_exist = box_client.discover_files() + assert file_exist is True + + def test_mongo_connection(mongo_client): request = mongo_client.establish_connection() assert request is None diff --git a/wifi_client/wifi_client.py b/wifi_client/wifi_client.py deleted file mode 100644 index 9a0e127..0000000 --- a/wifi_client/wifi_client.py +++ /dev/null @@ -1,53 +0,0 @@ -from wifi import Cell, Scheme -import traceback -import logging - -# home network will be the network you want to connect to -# password is the password for that network - -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') - - -class Client: - def __init__(self, device_id: str, home_network: str, password: str): - self.device_id = device_id - self.home_network = home_network - self.password = password - - def get_wifi_networks(self) -> list: - try: - logging.info("Getting wifi networks") - return Cell.all(self.device_id) - except Exception as e: - logging.error(f"Error discovering wifi networks: {e}") - traceback.print_exc(e) - return [] - - def connect(self) -> bool: - - try: - logging.info("Attempting to connect to wifi") - networks = self.get_wifi_networks() - - for network in networks: - if network.ssid == self.home_network: - scheme = Scheme.for_cell(self.device_id, network.ssid, network, self.password) - scheme.save() - scheme.activate() - - print(f"Connected to wifi: {network.ssid}") - logging.info(f"Connected to wifi: {network.ssid}") - return True - except Exception as e: - logging.error(f"Error connecting to wifi: {e}") - traceback.print_exc(e) - return False - - -def main(): - client = Client('wlan0', 'home_network_ssid', 'home_network_password') - client.connect() - - -if __name__ == '__main__': - main()