simplex 1.2.1__py3-none-any.whl → 1.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of simplex might be problematic. Click here for more details.
- simplex/simplex.py +100 -21
- {simplex-1.2.1.dist-info → simplex-1.2.3.dist-info}/METADATA +1 -1
- simplex-1.2.3.dist-info/RECORD +10 -0
- simplex-1.2.1.dist-info/RECORD +0 -10
- {simplex-1.2.1.dist-info → simplex-1.2.3.dist-info}/LICENSE +0 -0
- {simplex-1.2.1.dist-info → simplex-1.2.3.dist-info}/WHEEL +0 -0
- {simplex-1.2.1.dist-info → simplex-1.2.3.dist-info}/entry_points.txt +0 -0
- {simplex-1.2.1.dist-info → simplex-1.2.3.dist-info}/top_level.txt +0 -0
simplex/simplex.py
CHANGED
|
@@ -26,12 +26,11 @@ class Simplex:
|
|
|
26
26
|
self.driver = self.browser.new_page()
|
|
27
27
|
else:
|
|
28
28
|
self.playwright = None
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
self.driver = self.browser.new_page()
|
|
29
|
+
self.driver = self.browser.contexts[0].pages[0]
|
|
30
|
+
self.driver.set_default_navigation_timeout(0)
|
|
31
|
+
self.driver.set_default_timeout(0)
|
|
33
32
|
|
|
34
|
-
def
|
|
33
|
+
def extract_bbox(self, element_description: str, state: Image.Image | None = None, annotate: bool = True) -> List[int]:
|
|
35
34
|
"""
|
|
36
35
|
Find an element in the screenshot using the element description
|
|
37
36
|
|
|
@@ -42,6 +41,7 @@ class Simplex:
|
|
|
42
41
|
Returns:
|
|
43
42
|
bounding_box (tuple): [x1, y1, x2, y2] bounding box of the found element
|
|
44
43
|
"""
|
|
44
|
+
print(f"[SIMPLEX] Finding element \"{element_description}\"...")
|
|
45
45
|
if state is None:
|
|
46
46
|
state = self.take_stable_screenshot()
|
|
47
47
|
|
|
@@ -58,7 +58,7 @@ class Simplex:
|
|
|
58
58
|
'element_description': (None, element_description),
|
|
59
59
|
'api_key': (None, self.api_key)
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
print("[SIMPLEX] Sending screenshot to server...")
|
|
62
62
|
# Make the request
|
|
63
63
|
response = requests.post(
|
|
64
64
|
endpoint,
|
|
@@ -67,7 +67,6 @@ class Simplex:
|
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
# Print the results
|
|
70
|
-
print(f"Status Code: {response.status_code}")
|
|
71
70
|
if response.status_code == 200:
|
|
72
71
|
res = response.json()
|
|
73
72
|
bbox = [int(res['x1']), int(res['y1']), int(res['x2']), int(res['y2'])]
|
|
@@ -87,7 +86,7 @@ class Simplex:
|
|
|
87
86
|
const overlay = document.createElement('div');
|
|
88
87
|
overlay.id = 'simplex-bbox-overlay';
|
|
89
88
|
overlay.style.position = 'fixed';
|
|
90
|
-
overlay.style.border = '2px dashed rgba(
|
|
89
|
+
overlay.style.border = '2px dashed rgba(0, 255, 0, 1)';
|
|
91
90
|
overlay.style.background = 'rgba(74, 144, 226, 0.1)';
|
|
92
91
|
overlay.style.animation = 'marching-ants 0.5s linear infinite';
|
|
93
92
|
overlay.style.left = bbox[0] + 'px';
|
|
@@ -124,7 +123,7 @@ class Simplex:
|
|
|
124
123
|
self.driver.wait_for_selector('#simplex-bbox-overlay')
|
|
125
124
|
return bbox
|
|
126
125
|
else:
|
|
127
|
-
print("Error:", response.text)
|
|
126
|
+
print("[SIMPLEX] Error:", response.text)
|
|
128
127
|
|
|
129
128
|
def step_to_action(self, step_description: str, state: Image.Image | None = None) -> List[List[str]]:
|
|
130
129
|
"""
|
|
@@ -168,7 +167,7 @@ class Simplex:
|
|
|
168
167
|
actions = [[action.strip() for action in action_pair] for action_pair in actions]
|
|
169
168
|
return actions
|
|
170
169
|
else:
|
|
171
|
-
print(f"Error: {response.status_code}")
|
|
170
|
+
print(f"[SIMPLEX] Error: {response.status_code}")
|
|
172
171
|
print(response.text)
|
|
173
172
|
return []
|
|
174
173
|
|
|
@@ -182,8 +181,40 @@ class Simplex:
|
|
|
182
181
|
"""
|
|
183
182
|
if new_tab:
|
|
184
183
|
self.driver = self.browser.new_page()
|
|
184
|
+
self.driver.wait_for_load_state()
|
|
185
|
+
print(f"[SIMPLEX] Navigating to URL {url}...")
|
|
185
186
|
self.driver.goto(url)
|
|
186
187
|
|
|
188
|
+
def click(self, element_description: str, annotate: bool = True) -> None:
|
|
189
|
+
"""
|
|
190
|
+
Click on an element
|
|
191
|
+
"""
|
|
192
|
+
self.execute_action(["CLICK", element_description], annotate=annotate)
|
|
193
|
+
|
|
194
|
+
def type(self, text: str) -> None:
|
|
195
|
+
"""
|
|
196
|
+
Type text into an element
|
|
197
|
+
"""
|
|
198
|
+
self.execute_action(["TYPE", text])
|
|
199
|
+
|
|
200
|
+
def press_enter(self, annotate: bool = True) -> None:
|
|
201
|
+
"""
|
|
202
|
+
Press enter
|
|
203
|
+
"""
|
|
204
|
+
self.execute_action(["ENTER", ""], annotate=annotate)
|
|
205
|
+
|
|
206
|
+
def scroll(self, scroll_amount: int, annotate: bool = True) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Scroll the page
|
|
209
|
+
"""
|
|
210
|
+
self.execute_action(["SCROLL", scroll_amount], annotate=annotate)
|
|
211
|
+
|
|
212
|
+
def wait(self, wait_time: int, annotate: bool = True) -> None:
|
|
213
|
+
"""
|
|
214
|
+
Wait for a given amount of time
|
|
215
|
+
"""
|
|
216
|
+
self.execute_action(["WAIT", wait_time], annotate=annotate)
|
|
217
|
+
|
|
187
218
|
def execute_action(self, action: List[List[str]], state: Image.Image | None = None, annotate: bool = True) -> None:
|
|
188
219
|
"""
|
|
189
220
|
Execute an action with playwright driver
|
|
@@ -192,34 +223,30 @@ class Simplex:
|
|
|
192
223
|
action (List[List[str]]): List of actions to perform
|
|
193
224
|
"""
|
|
194
225
|
action_type, description = action
|
|
195
|
-
if state is None:
|
|
196
|
-
state = self.take_stable_screenshot()
|
|
197
|
-
|
|
198
226
|
try:
|
|
199
227
|
if action_type == "CLICK":
|
|
200
|
-
bbox = self.
|
|
228
|
+
bbox = self.extract_bbox(description, state, annotate=annotate)
|
|
201
229
|
center_x, center_y = center_bbox(bbox)
|
|
202
230
|
self.driver.mouse.click(center_x, center_y)
|
|
203
|
-
|
|
204
|
-
elif action_type == "HOVER":
|
|
205
|
-
bbox = self.find_element(description, state, annotate=annotate)
|
|
206
|
-
center_x, center_y = center_bbox(bbox)
|
|
207
|
-
self.driver.mouse.move(center_x, center_y)
|
|
208
|
-
|
|
231
|
+
print(f"[SIMPLEX] Clicked on element \"{description}\"")
|
|
209
232
|
elif action_type == "TYPE":
|
|
210
233
|
self.driver.keyboard.type(description)
|
|
234
|
+
print(f"[SIMPLEX] Typed \"{description}\"")
|
|
211
235
|
|
|
212
236
|
elif action_type == "ENTER":
|
|
213
237
|
self.driver.keyboard.press("Enter")
|
|
238
|
+
print(f"[SIMPLEX] Pressed enter")
|
|
214
239
|
|
|
215
240
|
elif action_type == "SCROLL":
|
|
216
241
|
self.driver.mouse.wheel(0, int(description))
|
|
242
|
+
print(f"[SIMPLEX] Scrolled {description} pixels")
|
|
217
243
|
|
|
218
244
|
elif action_type == "WAIT":
|
|
219
245
|
self.driver.wait_for_timeout(int(description))
|
|
246
|
+
print(f"[SIMPLEX] Waited {description} seconds")
|
|
220
247
|
|
|
221
248
|
except Exception as e:
|
|
222
|
-
print(f"Error executing action: {e}")
|
|
249
|
+
print(f"[SIMPLEX] Error executing action: {e}")
|
|
223
250
|
return None
|
|
224
251
|
|
|
225
252
|
def do(self, step_description: str, annotate: bool = True) -> None:
|
|
@@ -230,6 +257,57 @@ class Simplex:
|
|
|
230
257
|
actions = self.step_to_action(step_description, state)
|
|
231
258
|
for action in actions:
|
|
232
259
|
self.execute_action(action, annotate=annotate)
|
|
260
|
+
|
|
261
|
+
def extract_text(self, element_description: str, state: Image.Image | None = None) -> List[str] | None:
|
|
262
|
+
"""
|
|
263
|
+
Extract an element text from the page
|
|
264
|
+
"""
|
|
265
|
+
print(f"[SIMPLEX] Finding element \"{element_description}\"...")
|
|
266
|
+
if state is None:
|
|
267
|
+
state = self.take_stable_screenshot()
|
|
268
|
+
|
|
269
|
+
endpoint = f"{BASE_URL}/extract-text"
|
|
270
|
+
|
|
271
|
+
# Convert PIL Image to bytes
|
|
272
|
+
img_byte_arr = io.BytesIO()
|
|
273
|
+
state.save(img_byte_arr, format='PNG')
|
|
274
|
+
img_byte_arr = img_byte_arr.getvalue()
|
|
275
|
+
|
|
276
|
+
# Prepare multipart form data
|
|
277
|
+
files = {
|
|
278
|
+
'image_data': ('screenshot.png', img_byte_arr, 'image/png'),
|
|
279
|
+
'element_description': (None, element_description),
|
|
280
|
+
'api_key': (None, self.api_key)
|
|
281
|
+
}
|
|
282
|
+
print("[SIMPLEX] Sending screenshot to server...")
|
|
283
|
+
|
|
284
|
+
# Make the request
|
|
285
|
+
response = requests.post(
|
|
286
|
+
endpoint,
|
|
287
|
+
files=files
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Print the results
|
|
291
|
+
if response.status_code == 200:
|
|
292
|
+
res = response.json()
|
|
293
|
+
text = res['text']
|
|
294
|
+
return text
|
|
295
|
+
else:
|
|
296
|
+
print("[SIMPLEX] Error:", response.text)
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
def extract_image(self, element_description: str, state: Image.Image | None = None) -> Image.Image | None:
|
|
300
|
+
"""
|
|
301
|
+
Extract an element image from the page
|
|
302
|
+
"""
|
|
303
|
+
if state is None:
|
|
304
|
+
state = self.take_stable_screenshot()
|
|
305
|
+
|
|
306
|
+
bbox = self.extract_bbox(element_description, state)
|
|
307
|
+
cropped_state = state.crop((bbox[0], bbox[1], bbox[2], bbox[3]))
|
|
308
|
+
return cropped_state
|
|
309
|
+
|
|
310
|
+
|
|
233
311
|
|
|
234
312
|
def take_stable_screenshot(self) -> Image.Image:
|
|
235
313
|
"""
|
|
@@ -238,6 +316,7 @@ class Simplex:
|
|
|
238
316
|
Returns:
|
|
239
317
|
PIL.Image.Image: Screenshot of the current page
|
|
240
318
|
"""
|
|
319
|
+
print("[SIMPLEX] Taking screenshot of the page...")
|
|
241
320
|
self.driver.wait_for_load_state('networkidle')
|
|
242
321
|
return screenshot_to_image(self.driver.screenshot())
|
|
243
322
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
simplex/__init__.py,sha256=1mbM4XUk0FNW161WOkM4ayC1s_QSsaBEls6PZ0iBScY,74
|
|
2
|
+
simplex/constants.py,sha256=nIXF2oVNNNknXweXAlmE-KBM9QjJtYw9osXVYjvloN0,59
|
|
3
|
+
simplex/simplex.py,sha256=c1kWt8KvOIu-vhVmQga1BH-sRNFHvmWBrfP9t-93iBE,12101
|
|
4
|
+
simplex/utils.py,sha256=UrD4Ena3yk0POmxxyiqMszzPbTscTCJpMP4xZFDAuOc,339
|
|
5
|
+
simplex-1.2.3.dist-info/LICENSE,sha256=Xh0SJjYZfNI71pCNMB40aKlBLLuOB0blx5xkTtufFNQ,1075
|
|
6
|
+
simplex-1.2.3.dist-info/METADATA,sha256=HM_H6qRoBXMEtKsO6Ish0NU5V62a7JnugRZmSvGYNMs,1114
|
|
7
|
+
simplex-1.2.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
8
|
+
simplex-1.2.3.dist-info/entry_points.txt,sha256=3veL2w3c5vxb3dm8I_M8Fs-370n1ZnvD8uu1nSsL7z8,45
|
|
9
|
+
simplex-1.2.3.dist-info/top_level.txt,sha256=cbMH1bYpN0A3gP-ecibPRHasHoqB-01T_2BUFS8p0CE,8
|
|
10
|
+
simplex-1.2.3.dist-info/RECORD,,
|
simplex-1.2.1.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
simplex/__init__.py,sha256=1mbM4XUk0FNW161WOkM4ayC1s_QSsaBEls6PZ0iBScY,74
|
|
2
|
-
simplex/constants.py,sha256=nIXF2oVNNNknXweXAlmE-KBM9QjJtYw9osXVYjvloN0,59
|
|
3
|
-
simplex/simplex.py,sha256=8LTbvMJUbIWdyAqRKkius-R4PFHZBf4RW_bQZmwd5AY,9275
|
|
4
|
-
simplex/utils.py,sha256=UrD4Ena3yk0POmxxyiqMszzPbTscTCJpMP4xZFDAuOc,339
|
|
5
|
-
simplex-1.2.1.dist-info/LICENSE,sha256=Xh0SJjYZfNI71pCNMB40aKlBLLuOB0blx5xkTtufFNQ,1075
|
|
6
|
-
simplex-1.2.1.dist-info/METADATA,sha256=u9gF-wpSwjx0DfsrE5XM-7aRrmFyO5X-6LQ1RagF0tc,1114
|
|
7
|
-
simplex-1.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
8
|
-
simplex-1.2.1.dist-info/entry_points.txt,sha256=3veL2w3c5vxb3dm8I_M8Fs-370n1ZnvD8uu1nSsL7z8,45
|
|
9
|
-
simplex-1.2.1.dist-info/top_level.txt,sha256=cbMH1bYpN0A3gP-ecibPRHasHoqB-01T_2BUFS8p0CE,8
|
|
10
|
-
simplex-1.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|