1import logging
2import json
3import asyncio
4import requests
5from playwright.async_api import async_playwright
6from apify import Actor
7
8logging.basicConfig(level=logging.INFO)
9
10async def main():
11
12 await Actor.init()
13
14
15 actor_input = await Actor.get_input() or {}
16
17
18 logging.info(f'Input data: {actor_input}')
19
20
21 start_url = actor_input.get('start_url')
22 company_id = actor_input.get('company_id')
23 username = actor_input.get('username')
24 password = actor_input.get('password')
25 appointment_details = {
26 'patient_id': actor_input.get('patient_id'),
27 'type_id': actor_input.get('type_id'),
28 'user_id': actor_input.get('user_id'),
29 'reason': actor_input.get('reason'),
30 'room': actor_input.get('room'),
31 'appointment_date': actor_input.get('appointment_date'),
32 'time': actor_input.get('time'),
33 'time_end': actor_input.get('time_end'),
34 'useRealEndTime': actor_input.get('useRealEndTime')
35 }
36
37
38 logging.info(f'Appointment Details: {appointment_details}')
39
40
41 if not all([start_url, company_id, username, password, appointment_details['patient_id'],
42 appointment_details['type_id'], appointment_details['user_id'],
43 appointment_details['reason'], appointment_details['room'],
44 appointment_details['appointment_date'], appointment_details['time'],
45 appointment_details['time_end']]):
46 logging.error("Missing required input fields!")
47 return
48
49 async with async_playwright() as p:
50 browser = await p.chromium.launch(headless=True)
51 context = await browser.new_context()
52 page = await context.new_page()
53
54
55 sec_ch_ua = None
56 sec_ch_ua_mobile = None
57
58
59 def extract_headers(headers):
60 nonlocal sec_ch_ua, sec_ch_ua_mobile
61 sec_ch_ua = headers.get('sec-ch-ua')
62 sec_ch_ua_mobile = headers.get('sec-ch-ua-mobile')
63
64 page.on('request', lambda request: extract_headers(request.headers))
65
66 logging.info(f'Navigating to {start_url}...')
67 await page.goto(start_url)
68
69
70 await page.fill('#company_id', company_id)
71 await page.fill('#username', username)
72 await page.fill('#password', password)
73 await page.click('#btnLogin')
74
75
76 await page.wait_for_url('**/dashboard', timeout=15000)
77 logging.info('Login successful! On the dashboard page.')
78
79
80 cookies = await context.cookies()
81 logging.info(f'Cookies: {cookies}')
82 cookie_string = "; ".join([f"{cookie['name']}={cookie['value']}" for cookie in cookies])
83 logging.info(f'Cookie String: {cookie_string}')
84
85
86 xsrf_token = next((cookie['value'] for cookie in cookies if cookie['name'] == 'XSRF-TOKEN'), None)
87 logging.info(f'XSRF Token: {xsrf_token}')
88
89
90 user_agent = await page.evaluate("() => navigator.userAgent")
91 logging.info(f'User Agent: {user_agent}')
92
93
94 logging.info(f'SEC-CH-UA: {sec_ch_ua}')
95 logging.info(f'SEC-CH-UA-Mobile: {sec_ch_ua_mobile}')
96
97
98 request_url = 'https://us.idexxneo.com/appointments/create'
99 headers = {
100 'accept': 'application/json, text/plain, */*',
101 'accept-encoding': 'gzip, deflate, br, zstd',
102 'accept-language': 'en-GB,en;q=0.9',
103 'cookie': cookie_string,
104 'origin': 'https://us.idexxneo.com',
105 'referer': 'https://us.idexxneo.com/schedule/appointments/new?date=' + appointment_details['appointment_date'] + '&time=' + appointment_details['time'],
106 'sec-ch-ua': sec_ch_ua if sec_ch_ua else '',
107 'sec-ch-ua-mobile': sec_ch_ua_mobile if sec_ch_ua_mobile else '',
108 'sec-fetch-dest': 'empty',
109 'sec-fetch-mode': 'cors',
110 'sec-fetch-site': 'same-origin',
111 'sec-gpc': '1',
112 'user-agent': user_agent,
113 'x-neo-core-version': '87.0.0',
114 'x-xsrf-token': xsrf_token
115 }
116
117
118 files = {
119 'patient_id': (None, appointment_details['patient_id']),
120 'type_id': (None, appointment_details['type_id']),
121 'user_id': (None, appointment_details['user_id']),
122 'reason': (None, appointment_details['reason']),
123 'room': (None, appointment_details['room']),
124 'appointment_date': (None, appointment_details['appointment_date']),
125 'time': (None, appointment_details['time']),
126 'time_end': (None, appointment_details['time_end']),
127 'useRealEndTime': (None, str(appointment_details['useRealEndTime']).lower())
128 }
129
130
131 response = requests.post(request_url, headers=headers, files=files)
132
133
134 logging.info(f'Status Code: {response.status_code}')
135 logging.info(f'Response: {response.text}')
136
137 await browser.close()
138
139if __name__ == '__main__':
140 asyncio.run(main())