Skip to content
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

Implement websockets #71

Merged
merged 25 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
30ea9c6
feat: replaced flask with websockets
sc4n1a471 Jul 28, 2023
4bfa694
feat: added status messages
sc4n1a471 Jul 29, 2023
4d0a2dd
Merge branch '3-implement-websockets' into main-branch3-merge
sc4n1a471 Oct 8, 2023
d4aae22
Merge pull request #59 from sc4n1a471/main-branch3-merge
sc4n1a471 Oct 8, 2023
ed9f2d8
Merge pull request #62 from sc4n1a471/main
sc4n1a471 Oct 8, 2023
5da6b4b
feat: added inspection image handling
sc4n1a471 Oct 8, 2023
037e6f6
Update docker_dev.yml
sc4n1a471 Oct 8, 2023
88eefbc
fix: replaced server start command in Dockerfile + changed exposed po…
sc4n1a471 Oct 8, 2023
645a49a
fix: inspection image path
sc4n1a471 Oct 8, 2023
b6b657f
Merge pull request #51 from sc4n1a471/3-implement-websockets
sc4n1a471 Oct 8, 2023
d721158
fix: websocket server host to listen on all interfaces
sc4n1a471 Oct 8, 2023
7041f96
Merge pull request #63 from sc4n1a471/3-implement-websockets
sc4n1a471 Oct 8, 2023
a8468ff
feat: removed inspections from response object
sc4n1a471 Oct 8, 2023
0520622
Merge pull request #64 from sc4n1a471/3-implement-websockets
sc4n1a471 Oct 8, 2023
eabfd84
Update README.md
sc4n1a471 Oct 8, 2023
d7d7502
refactor: test response
sc4n1a471 Oct 29, 2023
6f881b3
Merge pull request #66 from sc4n1a471/65-refactor-test-request
sc4n1a471 Oct 29, 2023
a1e13aa
feat: added data/percentage to websocket responses
sc4n1a471 Oct 29, 2023
e0de75f
Merge pull request #68 from sc4n1a471/67-update-websocket-responses-w…
sc4n1a471 Oct 29, 2023
945a6e1
feat: added test response with error message
sc4n1a471 Oct 31, 2023
59a3836
Merge pull request #70 from sc4n1a471/69-test-response-with-error-mes…
sc4n1a471 Oct 31, 2023
2a454cf
feat: added image removal if image upload failed + added error message
sc4n1a471 Nov 5, 2023
40128ff
Merge pull request #73 from sc4n1a471/72-delete-images-if-upload-failed
sc4n1a471 Nov 5, 2023
8592b3e
feat: added license plate to accidents and mileage
sc4n1a471 Nov 5, 2023
b211d8c
Merge pull request #75 from sc4n1a471/74-assign-license-plate-to-acci…
sc4n1a471 Nov 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/docker_dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
platforms: linux/amd64,linux/arm64
file: ./Dockerfile
push: true
tags: sc4n1a471/car-thingy_python:dev
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ RUN apt update

COPY . .

EXPOSE 5000
EXPOSE 3001

CMD ["flask", "--app", "server.py", "run", "--host=0.0.0.0"]
ENTRYPOINT ["python3", "./server.py"]
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# NodeJS-Thingy_Python
[![Build, Publish, Redeploy](https://github.com/sc4n1a471/NodeJS-Thingy_Python/actions/workflows/docker.yml/badge.svg)](https://github.com/sc4n1a471/NodeJS-Thingy_Python/actions/workflows/docker.yml)
[![Build, Publish, Redeploy Development](https://github.com/sc4n1a471/car-thingy_Python/actions/workflows/docker_dev.yml/badge.svg?branch=dev)](https://github.com/sc4n1a471/car-thingy_Python/actions/workflows/docker_dev.yml)
[![Build, Publish, Redeploy Production](https://github.com/sc4n1a471/car-thingy_Python/actions/workflows/docker_prod.yml/badge.svg?branch=main)](https://github.com/sc4n1a471/car-thingy_Python/actions/workflows/docker_prod.yml)
![Alt](https://repobeats.axiom.co/api/embed/fb6459316bd61876f71e174bfcbed2c37ac152c2.svg "Repobeats analytics image")

Ez lenni repository for NodeJS_Thingy_Python
Expand Down
115 changes: 85 additions & 30 deletions application/data/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,64 @@
TESTING = False
URL = "https://magyarorszag.hu/jszp_szuf"
COOKIES = "cookies.pkl"
STATUS_COUNTER = 0


# TODO: don't use init(), use global variables, so don't init the driver every time
# this way if multiple license plates are requested individually in a row, it would not login every time
def init():
async def init(websocket_param):
global driver
global websocket
websocket = websocket_param

if os.getenv("RUN_ON_SERVER") == 'True':
if os.getenv("RUN_ON_SERVER") == "True":
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

grid_ip = os.environ["APP_GRID_IP"]

if grid_ip == 'default':
return {
"error": 'Selenium Grid IP address has the default value',
"status": 'fail'
}
if grid_ip == "default":
await send_data(
"message", "Selenium Grid IP address has the default value", 100, "fail"
)
return

driver = webdriver.Remote(grid_ip, DesiredCapabilities.CHROME)
else:
from selenium.webdriver.chrome.service import Service

chromedriver = "/chromedriver"

# SETUP YOUR BROWSER CONFIGURATION HERE

option = webdriver.ChromeOptions()

try:
option.binary_location = '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser'
option.binary_location = (
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
)
except:
print('Brave browser was not found on Mac, set your own browser up in settings.py')
await send_data(
"message",
"Brave browser was not found on Mac, set your own browser up in settings.py",
100,
"fail",
)
return

s = Service(chromedriver)

driver = webdriver.Chrome(service=s, options=option)
# driver = webdriver.Safari()
load_cookies()
await load_cookies()


def save_cookie():
with open(COOKIES, 'wb') as filehandler:
with open(COOKIES, "wb") as filehandler:
pickle.dump(driver.get_cookies(), filehandler)


def load_cookies():
async def load_cookies():
"""
Loads cookies before the first GET to magyarorszag.hu as it redirects to .gov.hu
if no cookies were found for magyarorszag.hu domain and then these domains can't be
Expand All @@ -63,36 +76,37 @@ def load_cookies():
:return:
"""
if os.path.exists(COOKIES) and os.path.isfile(COOKIES):
print("Loading cookies from " + COOKIES)
await send_data("message", "Loading cookies...", 3, "pending")
cookies = pickle.load(open(COOKIES, "rb"))

# Enables network tracking so we may use Network.setCookie method
if os.getenv("RUN_ON_SERVER") == 'True':
send(driver, 'Network.enable', {})
if os.getenv("RUN_ON_SERVER") == "True":
send(driver, "Network.enable", {})
else:
driver.execute_cdp_cmd('Network.enable', {})
driver.execute_cdp_cmd("Network.enable", {})

# Iterate through pickle dict and add all the cookies
for cookie in cookies:
# Fix issue Chrome exports 'expiry' key but expects 'expire' on import
if 'expiry' in cookie:
cookie['expires'] = cookie['expiry']
del cookie['expiry']
if "expiry" in cookie:
cookie["expires"] = cookie["expiry"]
del cookie["expiry"]

# Set the actual cookie
if os.getenv("RUN_ON_SERVER") == 'True':
send(driver, 'Network.setCookie', cookie)
if os.getenv("RUN_ON_SERVER") == "True":
send(driver, "Network.setCookie", cookie)
else:
driver.execute_cdp_cmd('Network.setCookie', cookie)
driver.execute_cdp_cmd("Network.setCookie", cookie)

# Disable network tracking
if os.getenv("RUN_ON_SERVER") == 'True':
send(driver, 'Network.disable', {})
if os.getenv("RUN_ON_SERVER") == "True":
send(driver, "Network.disable", {})
else:
driver.execute_cdp_cmd('Network.disable', {})
print("Cookies loaded successfully")
driver.execute_cdp_cmd("Network.disable", {})
await send_data("message", "Cookies loaded successfully", 5, "pending")
return 1
print("Cookies were not found")
await send_data("message", "Cookies could not be loaded", 5, "pending")


def send(driver, cmd, params={}):
"""
Expand All @@ -105,6 +119,47 @@ def send(driver, cmd, params={}):
"""
resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
url = driver.command_executor._url + resource
body = json.dumps({'cmd': cmd, 'params': params})
response = driver.command_executor._request('POST', url, body)
return response.get('value')
body = json.dumps({"cmd": cmd, "params": params})
response = driver.command_executor._request("POST", url, body)
return response.get("value")


async def send_message(message):
global websocket
message_object = {
"status": "pending",
"percentage": 0.0,
"key": "message",
"message": message,
}
print(message_object)
await websocket.send(json.dumps(message_object))


async def send_data(key, value, percentage, status="pending", is_json=False):
global websocket
global STATUS_COUNTER

if percentage != -1:
STATUS_COUNTER = percentage

if status == "success":
message_object = {"status": status, "percentage": 100, "key": "message"}
else:
if is_json:
message_object = {
"status": status,
"percentage": STATUS_COUNTER,
"key": key,
"value": value,
}
else:
message_object = {
"status": status,
"percentage": STATUS_COUNTER,
"key": key,
"value": value,
}

print(message_object)
await websocket.send(json.dumps(message_object))
4 changes: 1 addition & 3 deletions application/models/Car.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from dataclasses import dataclass, field

from application.models.Accident import Accident
from application.models.Inspection import Inspection
from application.models.Mileage import Mileage


Expand All @@ -24,9 +23,8 @@ class Car:
restrictions: [str] = field(default_factory=list)
mileage: [Mileage] = field(default_factory=list)
accidents: [Accident] = field(default_factory=list)
inspections: [Inspection] = field(default_factory=list)
has_origin_record = True
has_restriction_record = True
has_inspection_record = True
has_mileage_record = True
has_accident_record = True
has_accident_record = True
7 changes: 6 additions & 1 deletion application/models/Mileage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from dataclasses import dataclass
import json


@dataclass
class Mileage:
mileage_date: str
mileage: int
mileage: int

def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
112 changes: 69 additions & 43 deletions application/request_car.py
Original file line number Diff line number Diff line change
@@ -1,86 +1,112 @@
import os
import time
import traceback

from selenium.common import TimeoutException, WebDriverException
from selenium.webdriver.support.wait import WebDriverWait


from .models.GetDataException import GetDataException
from .models.LoginException import LoginException
from .models.UnreleasedLPException import UnreleasedLPException
from .src.login import login
from .models.Car import Car
from .src.get_data import get_data
from .data import settings
from tests.test_response import RES


def request_car(license_plates):
"""Opens page and does the rest of the query
async def request_car(license_plates, websocket_param):
"""
Opens page and does the rest of the query

Attributes:
license_plates -- Requested license plate
:param license_plates: Requested license plate
"""

if len(license_plates[0]) < 6 or len(license_plates[0]) > 7:
return {
"error": 'License plate is not valid, should be 6 or 7 characters',
"status": 'fail'
}
await settings.send_data(
"message",
"License plate is not valid, should be 6 or 7 characters",
100,
"fail",
)
return

cars: [Car] = []

try:
settings.init()
await settings.init(websocket_param)
except WebDriverException as wde:
return {
"error": f'Settings init failed with the following error: {wde.msg}',
"status": 'fail'
}
await settings.send_data(
"message",
f"Settings init failed with the following error: {wde.msg}",
100,
"fail",
)
return

if license_plates[0].lower() == "test111":
settings.driver.quit()

num_of_keys = 17
counter = 0

# iterate through RES.get("data")[0] and its keys, values and send them one by one
for key, value in RES.get("data")[0].items():
time.sleep(0.25)
await settings.send_data(key, value, counter)
counter += 1
await settings.send_data("message", None, 17, "success")
return

if license_plates[0].lower() == "test112":
settings.driver.quit()

counter = 0

# iterate through RES.get("data")[0] and its keys, values and send them one by one
for key, value in RES.get("data")[0].items():
time.sleep(0.25)
await settings.send_data(key, value, counter)
counter += 5
await settings.send_data("message", "This is a test error message", 17, "fail")
return

settings.driver.get(settings.URL)

try:
login()
await login() # 13 %
except LoginException as exc:
print(f"LOGIN ERROR: {traceback.format_exc()}")
settings.driver.quit()
return {
"status": 'fail',
"error": exc.message
}
await settings.send_data("message", f"Login failed: {exc.message}", 100, "fail")
return
except TimeoutException as toexc:
settings.driver.quit()
return {
"status": 'fail',
"error": toexc.msg
}
await settings.send_data("message", f"Login failed: {toexc.msg}", 100, "fail")
return

try:
cars = get_data(license_plates)
cars = await get_data(license_plates)
except UnreleasedLPException as ulp:
print(f"GET_DATA ERROR: {ulp}")
settings.driver.quit()
return {
"status": 'fail',
"error": ulp.args[0]
}
await settings.send_data(
"message", f"GET_DATA ERROR: {ulp.args[0]}", 100, "fail"
)
return
except GetDataException as exc:
print(f"GET_DATA ERROR: {traceback.format_exc()}")
settings.driver.quit()
return {
"status": 'fail',
"error": exc.message
}
await settings.send_data(
"message", f"GET_DATA ERROR: {traceback.format_exc()}", 100, "fail"
)
return
except Exception as exc:
print(f"GET_DATA ERROR: {traceback.format_exc()}")
settings.driver.quit()
return {
"status": 'fail',
"error": exc.args
}
await settings.send_data(
"message", f"GET_DATA ERROR: {traceback.format_exc()}", 100, "fail"
)
return

settings.driver.quit()

return {
"message": cars,
"status": 'success'
}
await settings.send_data("message", None, 100, "success")
return
Loading
Loading