WET Coding in the Age of AI: Fearless Refactoring with Python's Tokenizer
Setting the Stage: Context for the Curious Book Reader
In the broader ‘Futureproof’ series, we continually seek methodologies that empower developers in the Age of AI. This entry dives into a foundational shift in how we approach code maintenance and evolution. Here, we tackle a common and often dreaded task: a large-scale variable rename across an application ecosystem. The conventional wisdom often points to a trade-off between the blunt force of regular expressions and the destructive formatting changes of Abstract Syntax Trees (ASTs). This discussion unveils a ‘goldilocks zone’ using Python’s tokenize module, demonstrating how explicit, self-contained (WET) codebases can be refactored with surgical precision, leaving behind a pristine, human-readable codebase ready for the next iteration of AI collaboration.
Technical Journal Entry Begins
This is where we pay the price of WET coding versus DRY. All the AIs want to call WET “Write Everything Twice”. On rare occasions you’ll find “We Enjoy Typing”. My personal favorite to make it clear why WET coding style is good in the Age of AI is: “Write Explicitly; Terminate”. It highlights how everything you need to know is included right there in the context and that when it’s done, it’s done. There are no (non-obvious) hidden remaining dependencies.
The WET Philosophy: Clarity Over Concealment
So what’s non-obvious? Well, let’s look at what’s obvious. The LLMs know Python
3.12. They know it down to the source code byte, you can bet on that. And
thankfully, you can now include htmx.js in that. Maybe not every version of
htmx.js but enough to get a 99% accurate gravity landscape. That is opposed to
the stuff that’s not obvious. It’s not obvious that every version of React and
it’s entire web full stack toolchain for every version and mutation that would
be necessary to avoid hallucination is trained in. So it’s basically Python+HTMX
clearly trained-into parametric memory versus every part of the hamsterwheel
churn of the JSON industrial complex… maybe not trained in.
And I need to lean on that certainty. We start with the certainty of AST… or so I think. That’s the Python Abstract Syntax Tree which offers a practical alternative to wrangling RegEx, especially when it comes to searching and replacing objects in memory by their labels.
Expand on that a little. I know the job is already done Gemini, but this is good prefacing for the article.
Gemini 3 Pro: When you use Regular Expressions for a massive find-and-replace, you are essentially treating your codebase like a dumb text file. RegEx is fundamentally blind to context. Even with carefully constructed word boundaries, searching for pip is a perilous game. It might accidentally mangle a string like "run pip install", hallucinate a match inside a comment like # pipeline instance (pip), or require such complex negative lookarounds that the RegEx itself becomes a liability.
The built-in ast module, on the other hand, treats your code exactly as the Python interpreter sees it: as code. It parses the text into a hierarchical tree of structural logic. It inherently knows the difference between a string literal, a function definition, a comment, and a variable assignment.
With AST, you can programmatically say, “Find me every exact instance where pip is used strictly as a variable name or class attribute (Name or Attribute nodes), and completely ignore everything else.” It offers mathematical certainty. You aren’t guessing with raw text patterns; you are surgically manipulating the atomic logic of the program.
Lexical Analysis: The Compiler’s Secret Weapon
It sounds like the ultimate silver bullet for a WET codebase that has thousands of identically named variables repeated across dozens of files. But as I was about to find out, the structural purity of the Abstract Syntax Tree comes with a devastating trade-off when it’s time to turn that tree back into human-readable text.
Note: This is one of the rare cases where I start with a “Fast” version of a ChatBot LLM model for coding assistance instead of a “Pro” version. I keep the ferocious verbosity of this article intact for you to see why. I don’t need historical context. I need blazing pattern matching speed and python-writing alacrity.
MikeLev.in: I’m going to rip out remaining references to pip and replace it with
wand through the web application plugins in apps/. That is these files:
apps/010_introduction.py
apps/015_backup_introduction.py
apps/020_profiles.py
apps/025_aspect.py
apps/030_roles.py
apps/040_hello_workflow.py
apps/050_documentation.py
apps/060_tasks.py
apps/070_history.py
apps/100_connect_with_botify.py
apps/110_parameter_buster.py
apps/120_link_graph.py
apps/130_gap_analysis.py
apps/200_workflow_genesis.py
apps/210_widget_examples.py
apps/220_roadmap.py
apps/230_dev_assistant.py
apps/240_simon_mcp.py
apps/300_blank_placeholder.py
apps/400_botify_trifecta.py
apps/440_browser_automation.py
apps/450_stream_simulator.py
apps/510_text_field.py
apps/520_text_area.py
apps/530_dropdown.py
apps/540_checkboxes.py
apps/550_radios.py
apps/560_range.py
apps/570_switch.py
apps/580_upload.py
apps/610_markdown.py
apps/620_mermaid.py
apps/630_prism.py
apps/640_javascript.py
apps/710_pandas.py
apps/720_rich.py
apps/730_matplotlib.py
apps/810_webbrowser.py
apps/820_selenium.py
…and so on through the article you’ve already seen.
I already did it to apps/040_hello_workflow.py and it was more involved than I
thought it was going to be. We are going to use Python AST to do this to make
sure we do it correctly. It’s only the 3-letter objects we are interested. There
are a lot of occurrences in comments too, but we can maybe get those later with
RegEx using very strict word boundaries. But the ones I’m interested in getting
with AST is all those references to pip.() and such. The pip object is
almost always made as a new object from injection dependency style object
creation as a method of a class for internal shortcuts. There is a fare share of
self.pip that needs to become self.wand as well. I think the answer is
something in scripts/ that can do this. We will have it git committed and
pushed up-to-date so we will have infinite undos if we need to retry. You will
also have a human ready to tool-call with the rg command to check our work
before and after. Here’s a before.
The Practical Challenge: Surgical Refactoring at Scale
Historical Context: Why RegEx Flavors Diverge
How would I write an rg command to check for the variations of pip here that I
mention for the “before”? Something like rg pip but with word boundaries?
Gemini 3 Fast: Context verified.
Using an AST-based script for this refactor is absolutely the right methodology. It protects you from accidentally mangling strings, comments, or unrelated variables that happen to contain those three letters, keeping the blueprint of your codebase structurally sound.
To establish your “before” state and accurately count the variations of pip and self.pip (while cleanly avoiding pipulate, pipeline, or string contents), you can use rg with regex word boundaries (\b) or the built-in word flag (-w).
Here are the best rg commands to run from your project root:
1. The Comprehensive “Before” Search (Recommended)
This regex strictly targets pip as a standalone variable OR self.pip as an attribute, restricting the search to Python files in the apps/ directory.
rg '\b(self\.)?pip\b' apps/ -t py
\bensures we only match the exact word (ignoringpipulate,pipeline,epiphany, etc.).(self\.)?makes theself.prefix optional, capturing both local variable and instance attribute usage in one go.-t pyensures you are only looking at Python files.
2. Get the Baseline Statistics
To get a hard number of occurrences to compare against your “after” state, append the --stats flag. This will give you a summary at the bottom of the output showing exactly how many matches and matched lines exist.
rg '\b(self\.)?pip\b' apps/ -t py --stats
3. Break It Down by File
If you want a quick checklist of which files need the most AST surgery, use the count flag (-c). It will output a list of files and the number of times the target appears in each:
rg '\b(self\.)?pip\b' apps/ -t py -c
4. Isolate the targets If you want to check them independently to see exactly what you are dealing with:
To find only the local variable:
rg -w 'pip' apps/ -t py
To find only the class attribute:
rg 'self\.pip\b' apps/ -t py
Would you like me to draft the Python AST script to confidently handle this pip to wand transformation for the apps/ directory?
MikeLev.in: Okay, I get that down:
(nix)
[mike@nixos:~/repos/pipulate]$ rg '\b(self\.)?pip\b' apps/ -t py
apps/820_selenium.py
41: pip = self.pipulate
42: pip = self.pipulate
43: self.message_queue = pip.message_queue
54: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter the URL to open with Selenium.', 'complete': 'URL processed for Selenium.'}}
60: pip = self.pipulate
63: return pip.create_standard_landing_page(self)
66: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
74: context = pip.get_plugin_context(self)
83: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
85: pip.db['pipeline_id'] = pipeline_id
86: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
89: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
90: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
91: return pip.run_all_cells(app_name, steps)
94: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
95: pipeline_id = pip.db.get('pipeline_id', 'unknown')
97: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
100: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
102: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
108: await pip.finalize_workflow(pipeline_id)
109: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
110: return pip.run_all_cells(app_name, steps)
113: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
114: pipeline_id = pip.db.get('pipeline_id', 'unknown')
115: await pip.unfinalize_workflow(pipeline_id)
116: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
117: return pip.run_all_cells(app_name, steps)
125: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
131: await pip.clear_steps_from(pipeline_id, step_id, steps)
132: state = pip.read_state(pipeline_id)
134: pip.write_state(pipeline_id, state)
135: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
136: await self.message_queue.add(pip, message, verbatim=True)
137: return pip.run_all_cells(app_name, steps)
145: pip = self.pipulate
155: await self.message_queue.add(pip, f'Effective OS for Selenium: {effective_os}', verbatim=True)
157: await self.message_queue.add(pip, 'Using webdriver-manager for macOS.', verbatim=True)
160: await self.message_queue.add(pip, "Attempting to use system ChromeDriver (ensure it's in PATH).", verbatim=True)
162: await self.message_queue.add(pip, 'Initializing Chrome driver with Selenium...', verbatim=True)
164: await self.message_queue.add(pip, f'Selenium opening URL: {url_to_open}', verbatim=True)
168: await self.message_queue.add(pip, f'Selenium page loaded. Title: {page_title}', verbatim=True)
171: await self.message_queue.add(pip, 'Selenium browser closed.', verbatim=True)
177: await self.message_queue.add(pip, error_msg, verbatim=True)
183: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
188: pipeline_id = pip.db.get('pipeline_id', 'unknown')
189: state = pip.read_state(pipeline_id)
190: step_data = pip.get_step_data(pipeline_id, step_id, {})
192: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
198: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {user_val}', widget=url_widget_display, steps=steps)
202: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
204: await self.message_queue.add(pip, explanation, verbatim=True)
205: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Input(type='url', name=step.done, placeholder='https://www.google.com', required=True, value=display_value, cls='contrast'), Div(Button('Open with Selenium ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
208: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
213: pipeline_id = pip.db.get('pipeline_id', 'unknown')
220: await pip.set_step_data(pipeline_id, step_id, url_to_open, steps)
222: pip.append_to_history(f'[WIDGET ACTION] {step.show}: Attempted to open URL {url_to_open}. Success: {success}. Message: {message}')
226: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: URL processed - {url_to_open}', widget=combined_widget, steps=steps)
228: await self.message_queue.add(pip, f'{step.show} complete. {message}', verbatim=True)
229: if pip.check_finalize_needed(step_index, steps):
230: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
235: pip = self.pipulate
apps/240_simon_mcp.py
61: pip = self.pipulate
62: pip = self.pipulate
63: self.message_queue = pip.get_message_queue()
66: self.ui = pip.get_ui_constants()
108: pip, db = self.pipulate, self.pipulate.db
121: _, prefix_for_key_gen, _ = pip.generate_pipeline_key(self)
125: _, prefix, user_part = pip.generate_pipeline_key(self, user_input_key)
128: pip.db['pipeline_id'] = pipeline_id
129: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
135: return pip.run_all_cells(internal_app_name, self.steps)
138: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
139: pipeline_id = pip.db.get('pipeline_id', 'unknown')
141: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
142: state = pip.read_state(pipeline_id)
162: step_data = pip.get_step_data(pipeline_id, step.id, {})
188: await pip.finalize_workflow(pipeline_id)
190: return pip.run_all_cells(app_name, self.steps)
193: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
194: pipeline_id = pip.db.get('pipeline_id', 'unknown')
195: await pip.unfinalize_workflow(pipeline_id)
197: return pip.run_all_cells(app_name, self.steps)
200: pip, db, current_steps = self.pipulate, self.pipulate.db, self.steps
208: prev_data = pip.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
214: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
218: pipeline_id = pip.db.get('pipeline_id', 'unknown')
223: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
224: state = pip.read_state(pipeline_id)
226: pip.write_state(pipeline_id, state)
230: return pip.run_all_cells(app_name, current_steps_to_pass_helpers)
270: pip = self.pipulate
273: await self.message_queue.add(pip, "🎪 Simon Says Make MCP Call! Select an MCP action from the dropdown and click the button to execute it directly for teaching and testing!", verbatim=True)
529: pip = self.pipulate
585: await self.message_queue.add(pip, f"User selected: Cat Fact Test", verbatim=True, role='user')
586: await self.message_queue.add(pip, f"MCP Tool Execution: get_cat_fact → Success. Fact: {fact}", verbatim=True, role='assistant')
591: await self.message_queue.add(pip, f"User selected: Cat Fact Test", verbatim=True, role='user')
592: await self.message_queue.add(pip, f"MCP Tool Execution: get_cat_fact → Failed. Error: {error_msg}", verbatim=True, role='assistant')
604: await self.message_queue.add(pip, f"User selected: Google Search Test", verbatim=True, role='user')
605: await self.message_queue.add(pip, f"MCP Tool Execution: browser_stealth_search(query='python programming tutorial') → Success. Found {len(results)} results. Files: {list(files.keys())}", verbatim=True, role='assistant')
612: await self.message_queue.add(pip, f"User selected: Google Search Test", verbatim=True, role='user')
613: await self.message_queue.add(pip, f"MCP Tool Execution: browser_stealth_search → Failed. Error: {error_msg}", verbatim=True, role='assistant')
619: await self.message_queue.add(pip, f"User selected: {tool_display}", verbatim=True, role='user')
620: await self.message_queue.add(pip, f"MCP Tool Execution: {tool_name} → HTTP Error {response.status_code}", verbatim=True, role='assistant')
642: await self.message_queue.add(pip, f"User selected: {current_mode.replace('_', ' ').title()}", verbatim=True, role='user')
643: await self.message_queue.add(pip, f"MCP Tool Execution: ui_flash_element(element_id='{config['element_id']}', message='{config['message']}', delay=500) → Success. Element flashed 10 times.", verbatim=True, role='assistant')
648: await self.message_queue.add(pip, f"User selected: {current_mode.replace('_', ' ').title()}", verbatim=True, role='user')
649: await self.message_queue.add(pip, f"MCP Tool Execution: ui_flash_element(element_id='{config['element_id']}') → Failed. Error: {error_msg}", verbatim=True, role='assistant')
654: await self.message_queue.add(pip, f"User selected: {current_mode.replace('_', ' ').title()}", verbatim=True, role='user')
655: await self.message_queue.add(pip, f"MCP Tool Execution: ui_flash_element → HTTP Error {response.status_code}", verbatim=True, role='assistant')
668: await self.message_queue.add(pip, f"User executed MCP tool (HTTP timeout occurred)", verbatim=True, role='user')
669: await self.message_queue.add(pip, f"MCP Tool Execution: HTTP timeout after 2+ minutes - tool likely completed successfully in background", verbatim=True, role='assistant')
690: await self.message_queue.add(pip, f"User attempted MCP execution", verbatim=True, role='user')
691: await self.message_queue.add(pip, f"MCP Tool Execution → Exception: {error_msg}", verbatim=True, role='assistant')
apps/710_pandas.py
34: pip = self.pipulate
35: pip = self.pipulate
36: self.message_queue = pip.message_queue
47: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter JSON data for the table.', 'complete': 'JSON data processed and table rendered.'}}
53: pip = self.pipulate
56: return pip.create_standard_landing_page(self)
59: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
67: context = pip.get_plugin_context(self)
76: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
78: pip.db['pipeline_id'] = pipeline_id
79: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
82: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
83: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
84: return pip.run_all_cells(app_name, steps)
87: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
88: pipeline_id = pip.db.get('pipeline_id', 'unknown')
90: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
93: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
95: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
101: await pip.finalize_workflow(pipeline_id)
102: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
103: return pip.run_all_cells(app_name, steps)
106: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
107: pipeline_id = pip.db.get('pipeline_id', 'unknown')
108: await pip.unfinalize_workflow(pipeline_id)
109: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
110: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = pip.db.get('pipeline_id', 'unknown')
124: await pip.clear_steps_from(pipeline_id, step_id, steps)
125: state = pip.read_state(pipeline_id)
127: pip.write_state(pipeline_id, state)
128: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
129: await self.message_queue.add(pip, message, verbatim=True)
130: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
176: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
187: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
192: pip.write_state(pipeline_id, state)
194: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
196: await self.message_queue.add(pip, explanation, verbatim=True)
197: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the DataFrame', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Draw Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
200: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
205: pipeline_id = pip.db.get('pipeline_id', 'unknown')
208: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
219: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
220: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (JSON Data):\n{user_val}')
226: await self.message_queue.add(pip, f'{step.show} complete. Table rendered successfully.', verbatim=True)
227: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table rendered from pandas DataFrame', widget=table_container, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
apps/070_history.py
73: pip = self.pipulate
apps/620_mermaid.py
65: pip = self.pipulate
66: pip = self.pipulate
67: self.message_queue = pip.message_queue
78: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter Mermaid diagram syntax.', 'complete': 'Mermaid diagram syntax processed.'}}
84: pip = self.pipulate
87: return pip.create_standard_landing_page(self)
90: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
98: context = pip.get_plugin_context(self)
107: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
109: pip.db['pipeline_id'] = pipeline_id
110: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
113: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
114: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
115: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
119: pipeline_id = pip.db.get('pipeline_id', 'unknown')
121: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
124: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["UNLOCK_BUTTON"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["UNLOCK_BUTTON"]), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
126: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
132: await pip.finalize_workflow(pipeline_id)
133: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
134: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
138: pipeline_id = pip.db.get('pipeline_id', 'unknown')
139: await pip.unfinalize_workflow(pipeline_id)
140: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
141: return pip.run_all_cells(app_name, steps)
149: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
152: pipeline_id = pip.db.get('pipeline_id', 'unknown')
155: await pip.clear_steps_from(pipeline_id, step_id, steps)
156: state = pip.read_state(pipeline_id)
158: pip.write_state(pipeline_id, state)
159: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
160: await self.message_queue.add(pip, message, verbatim=True)
161: return pip.run_all_cells(app_name, steps)
188: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
193: pipeline_id = pip.db.get('pipeline_id', 'unknown')
194: state = pip.read_state(pipeline_id)
195: step_data = pip.get_step_data(pipeline_id, step_id, {})
197: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
207: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=mermaid_widget, steps=steps)
213: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
215: await self.message_queue.add(pip, explanation, verbatim=True)
216: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter Mermaid diagram syntax', required=True, rows=15, style='width: 100%; font-family: monospace;', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["MERMAID_TEXTAREA"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["MERMAID_INPUT"], aria_describedby="mermaid-help-text"), Div(Button('Create Diagram ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["RENDER_BUTTON"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["RENDER_BUTTON"]), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', role="form", aria_label="Configure Mermaid diagram syntax"), P("Supports flowcharts, sequence diagrams, class diagrams, and more", id="mermaid-help-text", cls='text-secondary')), Div(id=next_step_id), id=step_id)
219: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
224: pipeline_id = pip.db.get('pipeline_id', 'unknown')
227: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
230: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
231: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{user_val}')
234: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Client-side Mermaid diagram rendering', widget=mermaid_widget, steps=steps)
238: await self.message_queue.add(pip, f'{step.show} complete. Mermaid diagram rendered.', verbatim=True)
239: if pip.check_finalize_needed(step_index, steps):
240: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/530_dropdown.py
48: pip = self.pipulate
49: pip = self.pipulate
50: self.message_queue = pip.message_queue
61: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
63: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
69: pip = self.pipulate
72: return pip.create_standard_landing_page(self)
76: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
84: context = pip.get_plugin_context(self)
93: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
95: pip.db['pipeline_id'] = pipeline_id
96: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
99: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
100: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
101: return pip.run_all_cells(app_name, steps)
105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = pip.db.get('pipeline_id', 'unknown')
108: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
111: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='dropdown-widget-unlock-button', aria_label='Unlock dropdown for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
113: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
119: await pip.finalize_workflow(pipeline_id)
120: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
121: return pip.run_all_cells(app_name, steps)
125: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
126: pipeline_id = pip.db.get('pipeline_id', 'unknown')
127: await pip.unfinalize_workflow(pipeline_id)
128: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
129: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
143: await pip.clear_steps_from(pipeline_id, step_id, steps)
144: state = pip.read_state(pipeline_id)
146: pip.write_state(pipeline_id, state)
147: return pip.run_all_cells(app_name, steps)
197: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
202: pipeline_id = pip.db.get('pipeline_id', 'unknown')
203: state = pip.read_state(pipeline_id)
204: step_data = pip.get_step_data(pipeline_id, step_id, {})
210: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
214: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
215: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
238: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
243: pipeline_id = pip.db.get('pipeline_id', 'unknown')
248: await pip.set_step_data(pipeline_id, step_id, value, steps)
249: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('complete', f'{step.show} complete: {value}'), verbatim=True)
250: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/560_range.py
50: pip = self.pipulate
51: pip = self.pipulate
52: self.message_queue = pip.message_queue
63: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
65: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
71: pip = self.pipulate
74: return pip.create_standard_landing_page(self)
78: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: context = pip.get_plugin_context(self)
95: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
97: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
103: return pip.run_all_cells(app_name, steps)
107: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = pip.db.get('pipeline_id', 'unknown')
110: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
113: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='range-widget-unlock-button', aria_label='Unlock range workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
115: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await pip.finalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
123: return pip.run_all_cells(app_name, steps)
127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: await pip.unfinalize_workflow(pipeline_id)
130: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
131: return pip.run_all_cells(app_name, steps)
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: return pip.run_all_cells(app_name, steps)
154: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
159: pipeline_id = pip.db.get('pipeline_id', 'unknown')
160: state = pip.read_state(pipeline_id)
161: step_data = pip.get_step_data(pipeline_id, step_id, {})
167: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
169: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Finalized):\n{selected_value}')
172: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Completed):\n{selected_value}')
173: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
175: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing input form')
176: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
188: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
193: pipeline_id = pip.db.get('pipeline_id', 'unknown')
204: await pip.set_step_data(pipeline_id, step_id, value, steps)
205: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('complete', f'{step.show} complete: {value}'), verbatim=True)
206: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/450_stream_simulator.py
31: pip = self.pipulate
apps/300_blank_placeholder.py
36: pip = self.pipulate
37: pip = self.pipulate
38: self.message_queue = pip.get_message_queue()
41: self.ui = pip.get_ui_constants()
70: pip = self.pipulate
73: return pip.create_standard_landing_page(self)
77: pip, db, steps, app_name = (self.pipulate, self.pipulate.db, self.steps, self.APP_NAME)
85: context = pip.get_plugin_context(self)
94: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
96: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
105: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["WORKFLOW"]} Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
106: await self.message_queue.add(pip, f"{self.ui["EMOJIS"]["KEY"]} Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
113: await self.message_queue.add(pip, status_msg, verbatim=True)
115: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["INPUT_FORM"]} Please complete each step in sequence. Your progress will be saved automatically.', verbatim=True)
117: parsed = pip.parse_pipeline_key(pipeline_id)
123: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
124: return pip.run_all_cells(app_name, steps)
127: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
129: pipeline_id = pip.db.get('pipeline_id', 'unknown')
132: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
160: all_data_steps_complete = all(pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
189: await pip.finalize_workflow(pipeline_id)
190: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
191: return pip.run_all_cells(app_name, self.steps)
194: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
195: pipeline_id = pip.db.get('pipeline_id', 'unknown')
196: await pip.unfinalize_workflow(pipeline_id)
197: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
198: return pip.run_all_cells(app_name, self.steps)
201: pip, db, current_steps = self.pipulate, self.pipulate.db, self.steps
209: prev_data = pip.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
215: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
219: pipeline_id = pip.db.get('pipeline_id', 'unknown')
224: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
225: state = pip.read_state(pipeline_id)
227: pip.write_state(pipeline_id, state)
229: message = await pip.get_state_message(pipeline_id, current_steps_to_pass_helpers, self.step_messages)
230: await self.message_queue.add(pip, message, verbatim=True)
231: return pip.run_all_cells(app_name, current_steps_to_pass_helpers)
236: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.APP_NAME
242: pipeline_id = pip.db.get('pipeline_id', 'unknown')
243: state = pip.read_state(pipeline_id)
244: step_data = pip.get_step_data(pipeline_id, step_id, {})
246: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
249: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\\n{current_value}")
256: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\\n{current_value}")
258: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
263: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
264: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
301: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.APP_NAME
306: pipeline_id = pip.db.get('pipeline_id', 'unknown')
311: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
313: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
315: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
318: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
apps/640_javascript.py
39: pip = self.pipulate
40: pip = self.pipulate
41: self.message_queue = pip.message_queue
52: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please complete each step to explore the JavaScript execution widget.', 'step_01': {'input': 'Please enter JavaScript code to execute.', 'complete': 'JavaScript code processed.'}}
58: pip = self.pipulate
61: return pip.create_standard_landing_page(self)
65: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
73: context = pip.get_plugin_context(self)
82: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
84: pip.db['pipeline_id'] = pipeline_id
86: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
91: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
92: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
94: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
95: await self.message_queue.add(pip, status_msg, verbatim=True)
97: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
98: parsed = pip.parse_pipeline_key(pipeline_id)
104: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
105: return pip.run_all_cells(app_name, steps)
109: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
110: pipeline_id = pip.db.get('pipeline_id', 'unknown')
112: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
115: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
117: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
123: await pip.finalize_workflow(pipeline_id)
124: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
125: return pip.run_all_cells(app_name, steps)
129: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
130: pipeline_id = pip.db.get('pipeline_id', 'unknown')
131: await pip.unfinalize_workflow(pipeline_id)
132: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
133: return pip.run_all_cells(app_name, steps)
143: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
146: pipeline_id = pip.db.get('pipeline_id', 'unknown')
149: await pip.clear_steps_from(pipeline_id, step_id, steps)
150: state = pip.read_state(pipeline_id)
152: pip.write_state(pipeline_id, state)
153: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
154: await self.message_queue.add(pip, message, verbatim=True)
155: return pip.run_all_cells(app_name, steps)
163: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
168: pipeline_id = pip.db.get('pipeline_id', 'unknown')
169: state = pip.read_state(pipeline_id)
170: step_data = pip.get_step_data(pipeline_id, step_id, {})
172: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
189: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=js_widget, steps=steps)
196: pip.write_state(pipeline_id, state)
198: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
199: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter JavaScript code for the widget. Example is pre-populated.'), P("Use the 'widget' variable to access the container element.", cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JavaScript code', required=True, rows=12, style='width: 100%; font-family: monospace;'), Div(Button('Run JavaScript ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
203: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
207: pipeline_id = pip.db.get('pipeline_id', 'unknown')
211: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
214: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
215: pip.append_to_history(f'[WIDGET CONTENT] JavaScript Widget Code:\n{user_val}')
219: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Interactive JavaScript example', widget=js_widget, steps=steps)
223: await self.message_queue.add(pip, f'{step.show} complete. JavaScript executed.', verbatim=True)
apps/630_prism.py
33: pip = self.pipulate
34: pip = self.pipulate
35: self.message_queue = pip.message_queue
46: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter code content for syntax highlighting.', 'complete': 'Code content processed and highlighted.'}}
52: pip = self.pipulate
55: return pip.create_standard_landing_page(self)
58: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
66: context = pip.get_plugin_context(self)
75: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
77: pip.db['pipeline_id'] = pipeline_id
78: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
81: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
82: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
83: return pip.run_all_cells(app_name, steps)
86: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: pipeline_id = pip.db.get('pipeline_id', 'unknown')
89: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
92: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
94: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
100: await pip.finalize_workflow(pipeline_id)
101: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
102: return pip.run_all_cells(app_name, steps)
105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = pip.db.get('pipeline_id', 'unknown')
107: await pip.unfinalize_workflow(pipeline_id)
108: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
109: return pip.run_all_cells(app_name, steps)
117: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
120: pipeline_id = pip.db.get('pipeline_id', 'unknown')
123: await pip.clear_steps_from(pipeline_id, step_id, steps)
124: state = pip.read_state(pipeline_id)
126: pip.write_state(pipeline_id, state)
127: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
128: await self.message_queue.add(pip, message, verbatim=True)
129: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
176: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
203: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
209: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
211: await self.message_queue.add(pip, explanation, verbatim=True)
212: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter code for syntax highlighting', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Highlight Code ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
216: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
221: pipeline_id = pip.db.get('pipeline_id', 'unknown')
242: is_valid, error_msg, error_component = pip.validate_step_input(code_to_highlight, step.show)
245: await pip.set_step_data(pipeline_id, step_id, user_val_raw, steps)
246: pip.append_to_history(f'[WIDGET CONTENT] {step.show} ({language}):\n{code_to_highlight}')
249: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
253: await self.message_queue.add(pip, f'{step.show} complete. Code syntax highlighted with {language}.', verbatim=True)
254: if pip.check_finalize_needed(step_index, steps):
255: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/570_switch.py
50: pip = self.pipulate
51: pip = self.pipulate
52: self.message_queue = pip.message_queue
63: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
65: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
71: pip = self.pipulate
74: return pip.create_standard_landing_page(self)
78: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: context = pip.get_plugin_context(self)
95: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
97: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
103: return pip.run_all_cells(app_name, steps)
107: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = pip.db.get('pipeline_id', 'unknown')
110: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
113: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='switch-widget-unlock-button', aria_label='Unlock switch workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
115: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await pip.finalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
123: return pip.run_all_cells(app_name, steps)
127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: await pip.unfinalize_workflow(pipeline_id)
130: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
131: return pip.run_all_cells(app_name, steps)
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: return pip.run_all_cells(app_name, steps)
153: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
158: pipeline_id = pip.db.get('pipeline_id', 'unknown')
159: state = pip.get_step_data(pipeline_id, step_id, {})
161: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing input form')
162: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
167: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Toggle the switches to configure your settings.'), Form(Div(*switch_inputs, Div(Button('Save Settings ▸', type='submit', cls='primary', data_testid='switch-widget-next-button', aria_label='Save switch settings'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', data_testid='switch-widget-form', aria_label='Switch configuration form')), Div(id=next_step_id), id=step_id)
171: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
176: pipeline_id = pip.db.get('pipeline_id', 'unknown')
181: await pip.set_step_data(pipeline_id, step_id, switch_state, steps)
182: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('complete', f'{step.show} complete: {switch_state}'), verbatim=True)
183: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Settings saved', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/610_markdown.py
58: pip = self.pipulate
59: pip = self.pipulate
60: self.message_queue = pip.message_queue
74: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter Markdown content.', 'complete': 'Markdown content processed.'}}
82: pip = self.pipulate
85: return pip.create_standard_landing_page(self)
88: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
96: context = pip.get_plugin_context(self)
105: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
107: pip.db['pipeline_id'] = pipeline_id
108: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
111: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
112: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
113: return pip.run_all_cells(app_name, steps)
116: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
117: pipeline_id = pip.db.get('pipeline_id', 'unknown')
119: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
122: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["RENDER_BUTTON"] + "-unlock", aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["RENDER_BUTTON"]), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
124: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
130: await pip.finalize_workflow(pipeline_id)
131: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
132: return pip.run_all_cells(app_name, steps)
135: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
136: pipeline_id = pip.db.get('pipeline_id', 'unknown')
137: await pip.unfinalize_workflow(pipeline_id)
138: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
139: return pip.run_all_cells(app_name, steps)
147: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
150: pipeline_id = pip.db.get('pipeline_id', 'unknown')
153: await pip.clear_steps_from(pipeline_id, step_id, steps)
154: state = pip.read_state(pipeline_id)
156: pip.write_state(pipeline_id, state)
157: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
158: await self.message_queue.add(pip, message, verbatim=True)
159: return pip.run_all_cells(app_name, steps)
333: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
338: pipeline_id = pip.db.get('pipeline_id', 'unknown')
339: state = pip.read_state(pipeline_id)
340: step_data = pip.get_step_data(pipeline_id, step_id, {})
342: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
352: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=marked_widget, steps=steps)
358: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
360: await self.message_queue.add(pip, explanation, verbatim=True)
361: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter markdown content', required=True, rows=15, style='width: 100%; font-family: monospace;', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["MARKDOWN_TEXTAREA"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["MARKDOWN_INPUT"], aria_describedby="markdown-help-text"), Div(Button('Render Markdown ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["RENDER_BUTTON"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["RENDER_BUTTON"]), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}'), P("Supports headings, lists, bold/italic text, code blocks, and links", id="markdown-help-text", cls='text-secondary')), Div(id=next_step_id), id=step_id)
364: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
369: pipeline_id = pip.db.get('pipeline_id', 'unknown')
372: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
375: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
376: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{user_val}')
379: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown rendered with Marked.js', widget=marked_widget, steps=steps)
383: await self.message_queue.add(pip, f'{step.show} complete. Markdown rendered successfully.', verbatim=True)
384: if pip.check_finalize_needed(step_index, steps):
385: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/550_radios.py
50: pip = self.pipulate
51: pip = self.pipulate
52: self.message_queue = pip.message_queue
63: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
65: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
71: pip = self.pipulate
74: return pip.create_standard_landing_page(self)
78: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: context = pip.get_plugin_context(self)
95: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
97: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
103: return pip.run_all_cells(app_name, steps)
107: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = pip.db.get('pipeline_id', 'unknown')
110: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
113: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='radio-widget-unlock-button', aria_label='Unlock radio workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
115: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await pip.finalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
123: return pip.run_all_cells(app_name, steps)
127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: await pip.unfinalize_workflow(pipeline_id)
130: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
131: return pip.run_all_cells(app_name, steps)
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: return pip.run_all_cells(app_name, steps)
199: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
204: pipeline_id = pip.db.get('pipeline_id', 'unknown')
205: state = pip.read_state(pipeline_id)
206: step_data = pip.get_step_data(pipeline_id, step_id, {})
212: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
216: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
217: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
246: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
251: pipeline_id = pip.db.get('pipeline_id', 'unknown')
apps/015_backup_introduction.py
27: pip = self.pipulate
apps/210_widget_examples.py
38: - Widgets use pip.widget_container for consistent styling and DOM structure
142:* Emphasize the `pip.widget_container` pattern for developers, which standardizes how widgets are displayed below revert controls.
160: pip = self.pipulate
161: pip = self.pipulate
162: self.message_queue = pip.message_queue
173: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please complete each step to explore different widget types.', 'step_08': {'input': f"{pip.fmt('step_08')}: Please complete New Placeholder Step.", 'complete': f'New Placeholder Step complete. Continue to next step.'}, 'step_09': {'input': f"{pip.fmt('step_09')}: Configure Rich Table Widget.", 'complete': f'Rich Table Widget complete. Continue to next step.'}, 'step_07': {'input': f"{pip.fmt('step_07')}: Enter counter data for Matplotlib Histogram.", 'complete': f'Matplotlib Histogram complete. Continue to next step.'}}
175: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Enter content for {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
181: pip, pipeline, steps, app_name = (self.pipulate, self.pipeline, self.steps, self.app_name)
182: context = pip.get_plugin_context(self)
184: full_key, prefix, user_part = pip.generate_pipeline_key(self)
189: return Container(Card(H2(title), P(self.ENDPOINT_MESSAGE, cls='text-secondary'), Form(pip.wrap_with_inline_button(Input(placeholder='Existing or new 🗝 here (Enter for auto)', name='pipeline_id', list='pipeline-ids', type='search', required=False, autofocus=True, value=default_value, _onfocus='this.setSelectionRange(this.value.length, this.value.length)', cls='contrast'), button_label=f'Enter 🔑', button_class='secondary'), pip.update_datalist('pipeline-ids', options=datalist_options if datalist_options else None), hx_post=f'/{app_name}/init', hx_target=f'#{app_name}-container')), Div(id=f'{app_name}-container'))
193: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
201: context = pip.get_plugin_context(self)
210: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
212: pip.db['pipeline_id'] = pipeline_id
214: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
219: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
220: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
222: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
223: await self.message_queue.add(pip, status_msg, verbatim=True)
225: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
226: parsed = pip.parse_pipeline_key(pipeline_id)
232: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
233: return pip.run_all_cells(app_name, steps)
237: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
238: pipeline_id = pip.db.get('pipeline_id', 'unknown')
240: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
243: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
245: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
251: await pip.finalize_workflow(pipeline_id)
252: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
253: return pip.run_all_cells(app_name, steps)
257: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
258: pipeline_id = pip.db.get('pipeline_id', 'unknown')
259: await pip.unfinalize_workflow(pipeline_id)
260: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
261: return pip.run_all_cells(app_name, steps)
265: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
271: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
274: pipeline_id = pip.db.get('pipeline_id', 'unknown')
277: await pip.clear_steps_from(pipeline_id, step_id, steps)
278: state = pip.read_state(pipeline_id)
280: pip.write_state(pipeline_id, state)
281: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
282: await self.message_queue.add(pip, message, verbatim=True)
283: return pip.run_all_cells(app_name, steps)
297: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
302: pipeline_id = pip.db.get('pipeline_id', 'unknown')
303: state = pip.read_state(pipeline_id)
304: step_data = pip.get_step_data(pipeline_id, step_id, {})
306: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
312: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=simple_widget, steps=steps)
316: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
317: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter text content for the simple widget. Example is pre-populated.'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter text content for the widget', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Record Text ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
332: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
336: pipeline_id = pip.db.get('pipeline_id', 'unknown')
340: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
343: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
344: pip.append_to_history(f'[WIDGET CONTENT] Simple Text Widget:\n{user_val}')
346: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Simple text content provided', widget=simple_widget, steps=steps)
348: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
358: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
363: pipeline_id = pip.db.get('pipeline_id', 'unknown')
364: state = pip.read_state(pipeline_id)
365: step_data = pip.get_step_data(pipeline_id, step_id, {})
367: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
382: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=marked_widget, steps=steps)
389: pip.write_state(pipeline_id, state)
391: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
392: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter markdown content to be rendered. Example is pre-populated.'), P('The markdown will be rendered with support for headings, lists, bold/italic text, and code blocks.', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter markdown content', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Render Markdown ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
401: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
405: pipeline_id = pip.db.get('pipeline_id', 'unknown')
409: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
412: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
413: pip.append_to_history(f'[WIDGET CONTENT] Markdown Widget:\n{user_val}')
416: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown rendered with Marked.js', widget=marked_widget, steps=steps)
420: await self.message_queue.add(pip, f'{step.show} complete. Markdown rendered successfully.', verbatim=True)
425: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
430: pipeline_id = pip.db.get('pipeline_id', 'unknown')
431: state = pip.read_state(pipeline_id)
432: step_data = pip.get_step_data(pipeline_id, step_id, {})
434: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
449: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=mermaid_widget, steps=steps)
456: pip.write_state(pipeline_id, state)
458: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
459: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter Mermaid diagram syntax for the widget. Example is pre-populated.'), P('Supports flowcharts, sequence diagrams, class diagrams, etc.', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter Mermaid diagram syntax', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Create Diagram ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
478: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
482: pipeline_id = pip.db.get('pipeline_id', 'unknown')
486: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
489: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
490: pip.append_to_history(f'[WIDGET CONTENT] Mermaid Diagram:\n{user_val}')
493: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Client-side Mermaid diagram rendering', widget=mermaid_widget, steps=steps)
497: await self.message_queue.add(pip, f'{step.show} complete. Mermaid diagram rendered.', verbatim=True)
508: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
513: pipeline_id = pip.db.get('pipeline_id', 'unknown')
514: state = pip.read_state(pipeline_id)
515: step_data = pip.get_step_data(pipeline_id, step_id, {})
517: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
528: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
533: pip.write_state(pipeline_id, state)
535: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
536: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter table data as JSON array of objects. Example is pre-populated.'), P('Format: [{"name": "value", "value1": number, ...}, {...}]', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the DataFrame', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Draw Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
551: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
555: pipeline_id = pip.db.get('pipeline_id', 'unknown')
558: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
569: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
570: pip.append_to_history(f'[WIDGET CONTENT] Pandas Table Data:\n{user_val}')
576: await self.message_queue.add(pip, f'{step.show} complete. Table rendered successfully.', verbatim=True)
577: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table rendered from pandas DataFrame', widget=table_container, steps=steps), Div(id=f'{steps[step_index + 1].id}', hx_get=f'/{app_name}/{steps[step_index + 1].id}', hx_trigger='load'), id=step_id)))
585: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
590: pipeline_id = pip.db.get('pipeline_id', 'unknown')
591: state = pip.read_state(pipeline_id)
592: step_data = pip.get_step_data(pipeline_id, step_id, {})
594: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
631: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
638: pip.write_state(pipeline_id, state)
640: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
641: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter code to be highlighted with syntax coloring. JavaScript example is pre-populated.'), P('The code will be displayed with syntax highlighting and a copy button.', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter code for syntax highlighting', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Highlight Code ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
645: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
649: pipeline_id = pip.db.get('pipeline_id', 'unknown')
663: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
666: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
667: pip.append_to_history(f'[WIDGET CONTENT] Code Syntax Highlighting ({language}):\n{user_val}')
670: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
674: await self.message_queue.add(pip, f'{step.show} complete. Code syntax highlighted with {language}.', verbatim=True)
679: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
684: pipeline_id = pip.db.get('pipeline_id', 'unknown')
685: state = pip.read_state(pipeline_id)
686: step_data = pip.get_step_data(pipeline_id, step_id, {})
688: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
705: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=js_widget, steps=steps)
712: pip.write_state(pipeline_id, state)
714: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
715: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter JavaScript code for the widget. Example is pre-populated.'), P("Use the 'widget' variable to access the container element.", cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JavaScript code', required=True, rows=12, style='width: 100%; font-family: monospace;'), Div(Button('Run JavaScript ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
719: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
723: pipeline_id = pip.db.get('pipeline_id', 'unknown')
727: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
730: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
731: pip.append_to_history(f'[WIDGET CONTENT] JavaScript Widget Code:\n{user_val}')
735: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Interactive JavaScript example', widget=js_widget, steps=steps)
739: await self.message_queue.add(pip, f'{step.show} complete. JavaScript executed.', verbatim=True)
815: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
820: pipeline_id = pip.db.get('pipeline_id', 'unknown')
821: state = pip.read_state(pipeline_id)
822: step_data = pip.get_step_data(pipeline_id, step_id, {})
824: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
835: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=histogram_widget, steps=steps)
840: pip.write_state(pipeline_id, state)
842: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
843: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter counter data as JSON object (keys and values):'), P('Format: {"category1": count1, "category2": count2, ...}', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON object for Counter data', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Histogram ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
851: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
855: pipeline_id = pip.db.get('pipeline_id', 'unknown')
859: is_valid, error_msg, error_component = pip.validate_step_input(counter_data, step.show)
871: await pip.set_step_data(pipeline_id, step_id, counter_data, steps)
874: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Histogram created from Counter data', widget=histogram_widget, steps=steps)
876: await self.message_queue.add(pip, f'{step.show} complete. Histogram created.', verbatim=True)
889: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
894: pipeline_id = pip.db.get('pipeline_id', 'unknown')
895: state = pip.read_state(pipeline_id)
896: step_data = pip.get_step_data(pipeline_id, step_id, {})
898: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
902: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {url_value}', widget=Div(P(f'URL configured: ', B(url_value)), Button('Open URL Again ▸', type='button', _onclick=f"window.open('{url_value}', '_blank')", cls='secondary')), steps=steps)
906: await self.message_queue.add(pip, 'Enter the URL you want to open:', verbatim=True)
907: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter a URL to open in your default browser.'), Form(Div(Input(type='url', name='url', placeholder='https://example.com', required=True, value=display_value, cls='contrast'), Div(Button('Open URL ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
916: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
920: pipeline_id = pip.db.get('pipeline_id', 'unknown')
928: await pip.set_step_data(pipeline_id, step_id, url, steps)
929: await self.message_queue.add(pip, f'URL set to: {url}', verbatim=True)
933: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {url}', widget=url_widget, steps=steps)
935: await self.message_queue.add(pip, f'Opening URL: {url}', verbatim=True)
993: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
998: pipeline_id = pip.db.get('pipeline_id', 'unknown')
999: state = pip.read_state(pipeline_id)
1000: step_data = pip.get_step_data(pipeline_id, step_id, {})
1002: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1015: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
1020: pip.write_state(pipeline_id, state)
1023: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1024: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter table data as JSON array of objects. Example is pre-populated.'), P('Format: [{"name": "value", "value1": number, ...}, {...}]', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the table', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
1028: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1033: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1036: is_valid, error_msg, error_component = pip.validate_step_input(table_data, step.show)
1047: await pip.set_step_data(pipeline_id, step_id, table_data, steps)
1050: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table created with {len(data)} rows', widget=table_widget, steps=steps)
1052: await self.message_queue.add(pip, f'{step.show} complete. Table created with {len(data)} rows.', verbatim=True)
1083: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1088: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1089: state = pip.read_state(pipeline_id)
1090: step_data = pip.get_step_data(pipeline_id, step_id, {})
1092: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1096: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {url_value}', widget=Div(P(f'URL configured: ', B(url_value)), Form(Input(type='hidden', name='url', value=url_value), Button('Open URL Again 🪄', type='submit', cls='secondary'), hx_post=f'/{app_name}/reopen_url', hx_target=f'#{step_id}-status'), Div(id=f'{step_id}-status')), steps=steps)
1099: await self.message_queue.add(pip, 'Enter the URL you want to open with Selenium:', verbatim=True)
1105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1110: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1117: await pip.set_step_data(pipeline_id, step_id, url, steps)
1128: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
1130: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
1133: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
1135: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
1137: await self.message_queue.add(pip, f'Opening URL with Selenium: {url}', verbatim=True)
1141: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
1143: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
1149: await self.message_queue.add(pip, error_msg, verbatim=True)
1151: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{url}')
1152: pip.append_to_history(f'[WIDGET STATE] {step.show}: Step completed')
1153: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
1154: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1165: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1170: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1171: state = pip.read_state(pipeline_id)
1172: step_data = pip.get_step_data(pipeline_id, step_id, {})
1174: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1176: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Finalized):\n{file_summary}')
1179: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Completed):\n{file_summary}')
1180: return Div(Card(H3(f'{step.show}'), P('Uploaded files:'), Pre(file_summary, style='white-space: pre-wrap; font-size: 0.9em;'), pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps)), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1182: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing upload form')
1183: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1188: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1193: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1197: await self.message_queue.add(pip, 'No files selected. Please try again.', verbatim=True)
1205: await self.message_queue.add(pip, error_msg, verbatim=True)
1223: await self.message_queue.add(pip, error_msg, verbatim=True)
1228: await pip.set_step_data(pipeline_id, step_id, file_summary, steps)
1229: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{file_summary}')
1230: pip.append_to_history(f'[WIDGET STATE] {step.show}: Files saved')
1231: await self.message_queue.add(pip, f'Successfully saved {len(uploaded_files)} files to {save_directory}', verbatim=True)
1232: return Div(Card(H3(f'{step.show}'), P('Files saved successfully:'), Pre(file_summary, style='white-space: pre-wrap; font-size: 0.9em;'), pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps)), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1236: pip, db = (self.pipulate, self.pipulate.db)
1251: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
1253: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
1256: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
1258: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
1260: await self.message_queue.add(pip, f'Reopening URL with Selenium: {url}', verbatim=True)
1264: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
1266: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
1273: await self.message_queue.add(pip, error_msg, verbatim=True)
apps/200_workflow_genesis.py
59: pip = self.pipulate
60: pip = self.pipulate
61: self.message_queue = pip.get_message_queue()
101: pip = self.pipulate
105: 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'
123: pip = self.pipulate
126: return pip.create_standard_landing_page(self)
129: pip, db = self.pipulate, self.pipulate.db
140: _, prefix_for_key_gen, _ = pip.generate_pipeline_key(self)
144: _, prefix, user_part = pip.generate_pipeline_key(self, user_input_key)
147: pip.db['pipeline_id'] = pipeline_id
148: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
151: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
152: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
154: return pip.run_all_cells(internal_app_name, self.steps)
158: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
159: pipeline_id = pip.db.get('pipeline_id', 'unknown')
162: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
170: Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'),
177: all_data_steps_complete = all(pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
192: await pip.finalize_workflow(pipeline_id)
193: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
194: return pip.run_all_cells(app_name, self.steps)
197: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
198: pipeline_id = pip.db.get('pipeline_id', 'unknown')
199: await pip.unfinalize_workflow(pipeline_id)
200: await self.message_queue.add(pip, 'Workflow creation unfinalized. You can now modify any step.', verbatim=True)
201: return pip.run_all_cells(app_name, self.steps)
204: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
207: pipeline_id = pip.db.get('pipeline_id', 'unknown')
212: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, self.steps)
213: state = pip.read_state(pipeline_id)
215: pip.write_state(pipeline_id, state)
217: message = await pip.get_state_message(pipeline_id, self.steps, self.step_messages)
218: await self.message_queue.add(pip, message, verbatim=True)
219: return pip.run_all_cells(app_name, self.steps)
496: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
502: pipeline_id = pip.db.get('pipeline_id', 'unknown')
503: state = pip.read_state(pipeline_id)
504: step_data = pip.get_step_data(pipeline_id, step_id, {})
506: finalize_sys_data = pip.get_step_data(pipeline_id, 'finalize', {})
510: pip.finalized_content(message=f"🔒 {step_obj.show}", content=P(f"Workflow: {current_value.get('display_name', 'Unknown')}", cls='text-success')),
517: pip.display_revert_widget(
528: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
574: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
580: pipeline_id = pip.db.get('pipeline_id', 'unknown')
594: await pip.set_step_data(pipeline_id, step_id, params, self.steps)
595: await self.message_queue.add(pip, self.step_messages[step_id]['complete'], verbatim=True)
598: pip.display_revert_widget(
611: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
617: pipeline_id = pip.db.get('pipeline_id', 'unknown')
618: state = pip.read_state(pipeline_id)
619: step_data = pip.get_step_data(pipeline_id, step_id, {})
621: finalize_sys_data = pip.get_step_data(pipeline_id, 'finalize', {})
626: pip.finalized_content(message=f"🔒 {step_obj.show}", content=P(f"Template: {template_info['name']}", cls='text-success')),
634: pip.display_revert_widget(
645: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
666: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
672: pipeline_id = pip.db.get('pipeline_id', 'unknown')
680: await pip.set_step_data(pipeline_id, step_id, template_choice, self.steps)
681: await self.message_queue.add(pip, self.step_messages[step_id]['complete'], verbatim=True)
684: step_01_data = pip.get_step_data(pipeline_id, 'step_01', {})
760: pip.display_revert_widget(
773: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
779: pipeline_id = pip.db.get('pipeline_id', 'unknown')
780: state = pip.read_state(pipeline_id)
781: step_data = pip.get_step_data(pipeline_id, step_id, {})
783: finalize_sys_data = pip.get_step_data(pipeline_id, 'finalize', {})
787: pip.finalized_content(message=f"🔒 {step_obj.show}", content=P("Command execution complete.", cls='text-success')),
794: pip.display_revert_widget(
805: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
808: step_01_data = pip.get_step_data(pipeline_id, 'step_01', {})
866: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
872: pipeline_id = pip.db.get('pipeline_id', 'unknown')
875: step_01_data = pip.get_step_data(pipeline_id, 'step_01', {})
876: step_02_data = pip.get_step_data(pipeline_id, 'step_02', {})
945: await self.message_queue.add(pip, "⚠️ Another critical operation is in progress. Please wait and try again.", verbatim=True)
949: await self.message_queue.add(pip, "🔄 Executing workflow creation commands...", verbatim=True)
984: await self.message_queue.add(pip, f"✅ Created {display_filename} successfully!", verbatim=True)
985: await self.message_queue.add(pip, "🔄 Server restart will be triggered after command completion...", verbatim=True)
988: await self.message_queue.add(pip, f"❌ Command execution failed with exit code {result.returncode}", verbatim=True)
1003: await self.message_queue.add(pip, "❌ Command execution timed out", verbatim=True)
1008: await self.message_queue.add(pip, f"❌ Error executing command: {str(e)}", verbatim=True)
1034: await pip.set_step_data(pipeline_id, step_id, execution_summary, self.steps)
1035: await self.message_queue.add(pip, self.step_messages[step_id]['complete'], verbatim=True)
1039: parsed_key = pip.parse_pipeline_key(pipeline_id)
1098: pip.display_revert_widget(
apps/130_gap_analysis.py
37: pip = self.pipulate
38: pip = self.pipulate
39: self.message_queue = pip.get_message_queue()
42: self.ui = pip.get_ui_constants()
83: pip = self.pipulate
86: return pip.create_standard_landing_page(self)
90: pip, db, steps, app_name = (self.pipulate, self.pipulate.db, self.steps, self.APP_NAME)
98: context = pip.get_plugin_context(self)
107: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
109: pip.db['pipeline_id'] = pipeline_id
111: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
118: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["WORKFLOW"]} Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
119: await self.message_queue.add(pip, f"{self.ui["EMOJIS"]["KEY"]} Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
126: await self.message_queue.add(pip, status_msg, verbatim=True)
128: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["INPUT_FORM"]} Please complete each step in sequence. Your progress will be saved automatically.', verbatim=True)
130: parsed = pip.parse_pipeline_key(pipeline_id)
136: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
137: return pip.run_all_cells(app_name, steps)
140: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
173: all_data_steps_complete = all(pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
202: await pip.finalize_workflow(pipeline_id)
203: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
204: return pip.run_all_cells(app_name, self.steps)
207: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
208: pipeline_id = pip.db.get('pipeline_id', 'unknown')
209: await pip.unfinalize_workflow(pipeline_id)
210: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
211: return pip.run_all_cells(app_name, self.steps)
251: pip, db, current_steps = self.pipulate, self.pipulate.db, self.steps
259: prev_data = pip.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
265: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
269: pipeline_id = pip.db.get('pipeline_id', 'unknown')
274: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
275: state = pip.read_state(pipeline_id)
277: pip.write_state(pipeline_id, state)
279: message = await pip.get_state_message(pipeline_id, current_steps_to_pass_helpers, self.step_messages)
280: await self.message_queue.add(pip, message, verbatim=True)
281: return pip.run_all_cells(app_name, current_steps_to_pass_helpers)
286: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
291: pipeline_id = pip.db.get('pipeline_id', 'unknown')
292: state = pip.read_state(pipeline_id)
293: step_data = pip.get_step_data(pipeline_id, step_id, {})
295: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
298: await self.message_queue.add(pip, locked_msg, verbatim=True)
307: await self.message_queue.add(pip, completed_msg, verbatim=True)
310: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=yaml_widget, steps=steps)
321: await self.message_queue.add(pip, form_msg, verbatim=True)
322: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
324: await self.message_queue.add(pip, explanation, verbatim=True)
325: return Div(Card(H3(f'{pip.fmt(step.id)}: Enter {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder=f'Enter competitor domains, one per line...', required=True, autofocus=True, cls='textarea-standard', data_testid='text-area-widget-textarea-input', aria_label='Multi-line text input area for competitor domains', aria_required='true', aria_labelledby=f'{step_id}-form-title', aria_describedby=f'{step_id}-form-instruction'), Div(Button('Analyze Domains ▸', type='submit', cls='primary', **{'hx-on:click': 'this.setAttribute("aria-busy", "true"); this.textContent = "Analyzing domains..."'}), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step.id}_submit', hx_target=f'#{step.id}')), Div(id=next_step_id), id=step.id)
334: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
339: pipeline_id = pip.db.get('pipeline_id', 'unknown')
342: return await pip.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
358: await self.message_queue.add(pip, f'Found existing YAML with {len(existing_yaml_domains)} domains', verbatim=True)
360: await self.message_queue.add(pip, f'⚠️ YAML parsing failed: {str(e)}. Looking for raw domains...', verbatim=True)
400: existing_data = pip.get_step_data(pipeline_id, step_id, {})
409: await self.message_queue.add(pip, f'Found {len(processed_domains)} previously analyzed domains', verbatim=True)
411: await self.message_queue.add(pip, f'Could not parse existing data, starting fresh: {str(e)}', verbatim=True)
427: await self.message_queue.add(pip, f'➕ Found {len(new_raw_domains)} new domains to analyze: {", ".join(new_raw_domains)}', verbatim=True)
429: await self.message_queue.add(pip, f'📋 Total: {len(all_existing_domains)} existing + {len(new_raw_domains)} new = {len(domains)} domains', verbatim=True)
434: await self.message_queue.add(pip, f'Processing {len(domains)} domains...', verbatim=True)
441: await self.message_queue.add(pip, error_msg, verbatim=True)
442: return pip.create_error_form(error_msg, step_id, app_name)
448: await self.message_queue.add(pip, f'🤖 Starting AI browser automation analysis...', verbatim=True)
475: await self.message_queue.add(pip, f'🔍 Analyzing {domain}...', verbatim=True)
491: await self.message_queue.add(pip, f'👁️ AI Browser Vision: Capturing {scrape_url}...', verbatim=True)
509: await self.message_queue.add(pip, f'✅ AI Vision captured: {page_title[:50]}...', verbatim=True)
519: await self.message_queue.add(pip, f'🧠 AI Brain Analysis: {target_count} automation targets, {form_count} forms detected', verbatim=True)
521: await self.message_queue.add(pip, f'⚠️ AI Browser automation failed: {browser_result.get("error", "Unknown error")}', verbatim=True)
524: await self.message_queue.add(pip, f'⚠️ AI Browser automation error: {str(browser_error)[:100]}', verbatim=True)
589: await self.message_queue.add(pip, f'🎯 AI Enhanced: {domain_info["ai_browser_analysis"]["automation_intelligence"]["total_targets"]} targets, automation_ready: {domain_info["ai_browser_analysis"]["automation_intelligence"]["automation_ready"]}', verbatim=True)
609: await self.message_queue.add(pip, f" Chain: {chain_details}", verbatim=True)
614: await self.message_queue.add(pip, redirect_summary, verbatim=True)
628: await self.message_queue.add(pip, f"❌ {domain} - Connection failed", verbatim=True)
637: await self.message_queue.add(pip, f"❌ {domain} - {str(e)[:100]}", verbatim=True)
661: await pip.set_step_data(pipeline_id, step_id, yaml_output, steps)
668: await self.message_queue.add(pip, f'✅ Analysis complete: {preserved_count} existing domains preserved + {new_domains_processed} new domains analyzed = {total_count} total ({success_count} successful)', verbatim=True)
670: await self.message_queue.add(pip, f'✅ Analysis complete: {preserved_count} existing domains preserved (no new domains added)', verbatim=True)
672: await self.message_queue.add(pip, f'✅ Analysis complete: {success_count}/{total_count} domains processed successfully', verbatim=True)
674: if pip.check_finalize_needed(step_index, steps):
676: await self.message_queue.add(pip, finalize_msg, verbatim=True)
681: content_container = pip.display_revert_widget(
705: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
712: state = pip.read_state(pipeline_id)
713: step_data = pip.get_step_data(pipeline_id, step_id, {})
715: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
718: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\n{current_value}")
725: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\n{current_value}")
727: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
732: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
733: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
752: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
762: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
764: pip.append_to_history(f"[WIDGET CONTENT] {step.show}:\n{value_to_save}")
765: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
767: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
770: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
781: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
788: state = pip.read_state(pipeline_id)
789: step_data = pip.get_step_data(pipeline_id, step_id, {})
791: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
794: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\n{current_value}")
801: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\n{current_value}")
803: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
808: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
809: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
828: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
838: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
840: pip.append_to_history(f"[WIDGET CONTENT] {step.show}:\n{value_to_save}")
841: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
843: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
846: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
apps/230_dev_assistant.py
93: pip = self.pipulate
94: pip = self.pipulate
95: self.message_queue = pip.get_message_queue()
143: pip, db = self.pipulate, self.pipulate.db
154: _, prefix_for_key_gen, _ = pip.generate_pipeline_key(self)
158: _, prefix, user_part = pip.generate_pipeline_key(self, user_input_key)
161: pip.db['pipeline_id'] = pipeline_id
162: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
165: await self.message_queue.add(pip, f'Development Assistant Session: {pipeline_id}', verbatim=True, spaces_before=0)
167: return pip.run_all_cells(internal_app_name, self.steps)
170: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
171: pipeline_id = pip.db.get('pipeline_id', 'unknown')
174: await pip.set_step_data(pipeline_id, 'finalize', {'finalized': True}, self.steps)
175: await self.message_queue.add(pip, 'Development analysis session finalized.', verbatim=True)
176: return pip.run_all_cells(app_name, self.steps)
178: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
209: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
210: pipeline_id = pip.db.get('pipeline_id', 'unknown')
211: await pip.unfinalize_workflow(pipeline_id)
212: await self.message_queue.add(pip, 'Development analysis session unlocked for editing.', verbatim=True)
213: return pip.run_all_cells(app_name, self.steps)
216: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
219: pipeline_id = pip.db.get('pipeline_id', 'unknown')
224: await pip.clear_steps_from(pipeline_id, step_id, self.steps)
226: state = pip.read_state(pipeline_id)
228: pip.write_state(pipeline_id, state)
230: await self.message_queue.add(pip, f'Reverted to {step_id} for re-analysis.', verbatim=True)
232: return pip.run_all_cells(app_name, self.steps)
786: f"finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
796: f" return pip.chain_reverter(step_id, step_index, steps, app_name, processed_val)\n"
813: f"return pip.chain_reverter(\n"
824: f" pip.display_revert_header(step_id, app_name, steps, f'{{step.show}}: {{value}}'),\n"
872: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
873: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
875: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
876: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
877: f" return pip.run_all_cells(app_name, self.steps)\n"
904: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
905: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
907: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
908: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
909: f" return pip.run_all_cells(app_name, self.steps)\n"
922: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
923: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
930: f" await pip.finalize_workflow(pipeline_id)\n"
931: f" return pip.run_all_cells(app_name, self.steps)\n"
948: f" pip, steps, app_name = (self.pipulate, self.steps, self.app_name)\n"
951: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
958: f" await pip.set_step_data(pipeline_id, step_id, user_input, steps)\n"
960: f" return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n"
1101: has_centralized_ui = 'pip.get_ui_constants()' in content or 'self.ui = ' in content
1137: f" self.ui = pip.get_ui_constants() # Access centralized UI constants\n"
1326: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1331: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1332: state = pip.read_state(pipeline_id)
1333: step_data = pip.get_step_data(pipeline_id, step_id, {{}})
1335: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})
1344: return pip.chain_reverter(step_id, step_index, steps, app_name, user_val)
1364: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1367: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1374: await pip.set_step_data(pipeline_id, step_id, user_input, steps)
1376: return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n""")
1395: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
1396: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
1399: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
1400: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
1401: f" return pip.run_all_cells(app_name, self.steps)\n"
1403: f" finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
apps/400_botify_trifecta.py
281: pip = self.pipulate
282: pip = self.pipulate
283: self.message_queue = pip.message_queue
286: self.ui = pip.get_ui_constants()
287: self.config = pip.get_config()
326: self.step_messages = {'finalize': {'ready': self.ui['MESSAGES']['ALL_STEPS_COMPLETE'], 'complete': f'Workflow finalized. Use {self.ui["BUTTON_LABELS"]["UNLOCK"]} to make changes.'}, 'step_analysis': {'input': f"❔{pip.fmt('step_analysis')}: Please select a crawl analysis for this project.", 'complete': '📊 Crawl analysis download complete. Continue to next step.'}}
329: self.step_messages[step.id] = {'input': f'❔{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'✳️ {step.show} complete. Continue to next step.'}
330: self.step_messages['step_gsc'] = {'input': f"❔{pip.fmt('step_gsc')}: Please check if the project has Search Console data.", 'complete': 'Search Console check complete. Continue to next step.'}
332: self.step_messages['step_crawler'] = {'input': f"❔{pip.fmt('step_crawler')}: Please download basic crawl attributes for node metadata.", 'complete': '📊 Basic crawl data download complete. Continue to next step.'}
333: self.step_messages['step_webogs'] = {'input': f"❔{pip.fmt('step_webogs')}: Please check if the project has web logs available.", 'complete': '📋 Web logs check complete. Continue to next step.'}
452: pip = self.pipulate
455: return pip.create_standard_landing_page(self)
459: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
467: context = pip.get_plugin_context(self)
476: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
478: pip.db['pipeline_id'] = pipeline_id
479: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
482: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
483: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
484: return pip.run_all_cells(app_name, steps)
492: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
493: pipeline_id = pip.db.get('pipeline_id', 'unknown')
495: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
500: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
502: await self.message_queue.add(pip, 'All steps are complete. You can now finalize the workflow or revert to any step to make changes.', verbatim=True)
507: await pip.finalize_workflow(pipeline_id)
508: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
509: return pip.run_all_cells(app_name, steps)
513: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
514: pipeline_id = pip.db.get('pipeline_id', 'unknown')
515: await pip.unfinalize_workflow(pipeline_id)
516: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
517: return pip.run_all_cells(app_name, steps)
521: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
529: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
535: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
538: pipeline_id = pip.db.get('pipeline_id', 'unknown')
541: await pip.clear_steps_from(pipeline_id, step_id, steps)
542: state = pip.read_state(pipeline_id)
544: pip.write_state(pipeline_id, state)
545: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
546: await self.message_queue.add(pip, message, verbatim=True)
547: await self.message_queue.add(pip, f'↩️ Reverted to {step_id}. All subsequent data has been cleared.', verbatim=True)
548: return pip.run_all_cells(app_name, steps)
556: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
561: pipeline_id = pip.db.get('pipeline_id', 'unknown')
562: state = pip.read_state(pipeline_id)
563: step_data = pip.get_step_data(pipeline_id, step_id, {})
567: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
574: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
577: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
628: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
633: pipeline_id = pip.db.get('pipeline_id', 'unknown')
640: await pip.set_step_data(pipeline_id, step_id, project_data_str, steps)
641: await self.message_queue.add(pip, f"✳️ {step.show} complete: {project_data['project_name']}", verbatim=True)
651: await self.message_queue.add(pip, "🔍 Fetching analyses data...", verbatim=True)
653: await self.message_queue.add(pip, save_message, verbatim=True)
668: await pip.log_api_call_details(
679: await self.message_queue.add(pip, f"⚠️ Could not save analyses data: {str(e)}", verbatim=True)
682: await self.message_queue.add(pip, "⚠️ No API token found - skipping analyses data save", verbatim=True)
686: return pip.chain_reverter(step_id, step_index, steps, app_name, project_url)
690: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
695: pipeline_id = pip.db.get('pipeline_id', 'unknown')
696: state = pip.read_state(pipeline_id)
697: step_data = pip.get_step_data(pipeline_id, step_id, {})
702: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
709: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
734: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_slug}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
782: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Select an analysis for {project_name}'), verbatim=True)
899: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
904: pipeline_id = pip.db.get('pipeline_id', 'unknown')
906: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
922: await self.message_queue.add(pip, "🔍 Fetching advanced export data...", verbatim=True)
924: await self.message_queue.add(pip, save_message, verbatim=True)
939: await pip.log_api_call_details(
950: await self.message_queue.add(pip, f"⚠️ Could not save advanced export data: {str(e)}", verbatim=True)
953: await self.message_queue.add(pip, "⚠️ No API token found - skipping advanced export data save", verbatim=True)
977: await pip.set_step_data(pipeline_id, step_id, step.done, analysis_result_str)
1005: pip.display_revert_widget(
1019: await self.message_queue.add(pip, f'📊 Selected analysis: {analysis_slug}. Starting crawl data download...', verbatim=True)
1037: await self.message_queue.add(pip, qualifier_config['user_message_running'], verbatim=True)
1046: await self.message_queue.add(pip, qualifier_config['user_message_found'].format(
1052: await self.message_queue.add(pip, f'Error during qualifier logic: {str(e)}', verbatim=True)
1059: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1081: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1086: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1087: state = pip.read_state(pipeline_id)
1088: step_data = pip.get_step_data(pipeline_id, step_id, {})
1092: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1123: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1125: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1134: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1194: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1199: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1207: await self.message_queue.add(pip, f"⏭️ Skipping basic crawl data download...", verbatim=True)
1220: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1221: await self.message_queue.add(pip, f"⏭️ Basic crawl data step skipped. Proceeding to next step.", verbatim=True)
1224: pip.display_revert_widget(
1237: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1247: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1267: await self.message_queue.add(pip, f"✅ Using cached basic crawl data ({file_info['size']})...", verbatim=True)
1324: await pip.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
1346: pip.display_revert_widget(
1359: await self.message_queue.add(pip, f"Cache check failed, proceeding with download: {str(e)}", verbatim=True)
1379: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1384: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1388: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1397: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1429: stored_step_data = pip.get_step_data(pipeline_id, step_id, {})
1450: await pip.set_step_data(pipeline_id, step_id, json.dumps(enhanced_data), steps)
1464: await pip.set_step_data(pipeline_id, step_id, json.dumps(check_result), steps)
1474: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1479: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1480: state = pip.read_state(pipeline_id)
1481: step_data = pip.get_step_data(pipeline_id, step_id, {})
1485: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1516: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1518: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1526: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1592: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1597: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1605: await self.message_queue.add(pip, f"⏭️ Skipping Web Logs download...", verbatim=True)
1619: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1620: await self.message_queue.add(pip, f"⏭️ Web Logs step skipped. Proceeding to next step.", verbatim=True)
1623: pip.display_revert_widget(
1636: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1644: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1656: await self.message_queue.add(pip, f"✅ Using cached Web Logs data ({file_info['size']})...", verbatim=True)
1674: await pip.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
1696: pip.display_revert_widget(
1707: await self.message_queue.add(pip, f"Cache check failed, proceeding with download: {str(e)}", verbatim=True)
1732: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1737: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1748: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1756: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1773: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1778: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1779: state = pip.read_state(pipeline_id)
1780: step_data = pip.get_step_data(pipeline_id, step_id, {})
1784: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1815: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1817: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1826: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1886: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1891: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1899: await self.message_queue.add(pip, f"⏭️ Skipping Search Console download...", verbatim=True)
1913: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1914: await self.message_queue.add(pip, f"⏭️ Search Console step skipped. Proceeding to next step.", verbatim=True)
1917: pip.display_revert_widget(
1930: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1940: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1960: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})...", verbatim=True)
1979: await pip.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
2001: pip.display_revert_widget(
2014: await self.message_queue.add(pip, f"Cache check failed, proceeding with download: {str(e)}", verbatim=True)
2034: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2039: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2041: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
2049: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
2061: await self.message_queue.add(pip, f'✅ Project has Search Console data, downloading...', verbatim=True)
2062: await self.process_search_console_data(pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
2064: await self.message_queue.add(pip, f'Project does not have Search Console data (skipping download)', verbatim=True)
2086: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
2109: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {completed_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3008: async def process_search_console_data(self, pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
3015: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})", verbatim=True)
3026: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3028: await self.message_queue.add(pip, '🔄 Initiating Search Console data export...', verbatim=True)
3057: await self.message_queue.add(pip, '✅ Export job created successfully!', verbatim=True)
3060: await self.message_queue.add(pip, f'❌ Error creating export job: {str(e)}', verbatim=True)
3062: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3066: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3068: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3071: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3073: await self.message_queue.add(pip, '🔄 Downloading Search Console data...', verbatim=True)
3100: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3106: await self.message_queue.add(pip, f'❌ Error downloading or extracting file: {str(e)}', verbatim=True)
3108: await self.message_queue.add(pip, '✅ Search Console data ready for analysis!', verbatim=True)
3110: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3115: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3116: await self.message_queue.add(pip, f'❌ Error processing Search Console data: {str(e)}', verbatim=True)
3539: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3545: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3552: step_data = pip.get_step_data(pipeline_id, step_id, {})
3573: await self.message_queue.add(pip, f"✅ Using cached crawl data ({file_info['size']})", verbatim=True)
3614: await self.message_queue.add(pip, '🔄 Initiating crawl data export...', verbatim=True)
3692: await self.message_queue.add(pip, '✅ Crawl export job created successfully!', verbatim=True)
3693: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3696: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3710: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3726: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3732: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
3745: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3748: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3760: await self.message_queue.add(pip, '🔄 Downloading crawl data...', verbatim=True)
3775: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3808: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3821: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3834: await self.message_queue.add(pip, f"✅ Crawl data downloaded: {file_info['size']}", verbatim=True)
3837: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3867: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Analysis {status_text}{download_message}', widget=widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3874: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3879: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3892: await self.message_queue.add(pip, f'{step.show} complete: Project {status_text} web logs', verbatim=True)
3911: await self.message_queue.add(pip, f"✅ Using cached web logs data ({file_info['size']})", verbatim=True)
3928: await self.message_queue.add(pip, '🔄 Initiating web logs export...', verbatim=True)
3971: await self.message_queue.add(pip, f'✅ Web logs export job created successfully! (Job ID: {job_id})', verbatim=True)
3972: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3975: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3990: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
4004: await self.message_queue.add(pip, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
4009: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
4015: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
4031: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
4034: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
4047: await self.message_queue.add(pip, '🔄 Downloading web logs data...', verbatim=True)
4078: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
4079: await self.message_queue.add(pip, f"✅ Web logs data downloaded: {file_info['size']}", verbatim=True)
4093: await self.message_queue.add(pip, f'❌ Error downloading file: {str(e)}', verbatim=True)
4106: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
4137: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text} web logs{download_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
4154: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
4160: pipeline_id = pip.db.get('pipeline_id', 'unknown')
4161: step_data = pip.get_step_data(pipeline_id, step_id, {})
4165: state = pip.read_state(pipeline_id)
4210: pip.write_state(pipeline_id, state)
4215: pip.write_state(pipeline_id, state)
apps/120_link_graph.py
326: await self.message_queue.add(pip, f'📊 Loaded new data: {len(new_df):,} rows', verbatim=True)
336: await self.message_queue.add(pip, f'⚠️ Warning: Could not load new data: {str(e)}', verbatim=True)
338: await self.message_queue.add(pip, '⚠️ No new data found, continuing without additional metrics', verbatim=True)
436: await self.message_queue.add(pip, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
437: return P('Error: Link graph data not found. Please complete Step 2 first.', style=pip.get_style('error'))
449: await self.message_queue.add(pip, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
459: await self.message_queue.add(pip, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
461: await self.message_queue.add(pip, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
473: await self.message_queue.add(pip, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
483: await self.message_queue.add(pip, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
485: await self.message_queue.add(pip, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
687: pip = self.pipulate
688: self.message_queue = pip.message_queue
691: self.ui = pip.get_ui_constants()
692: self.config = pip.get_config()
725: self.step_messages = {'finalize': {'ready': self.ui['MESSAGES']['ALL_STEPS_COMPLETE'], 'complete': f'Workflow finalized. Use {self.ui["BUTTON_LABELS"]["UNLOCK"]} to make changes.'}, 'step_02': {'input': f"❔{pip.fmt('step_02')}: Please select a crawl analysis for this project.", 'complete': '📊 Crawl analysis download complete. Continue to next step.'}}
728: self.step_messages[step.id] = {'input': f'❔{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'✳️ {step.show} complete. Continue to next step.'}
729: self.step_messages['step_04'] = {'input': f"❔{pip.fmt('step_04')}: Please check if the project has Search Console data.", 'complete': 'Search Console check complete. Continue to next step.'}
730: self.step_messages['step_03'] = {'input': f"❔{pip.fmt('step_03')}: Please check if the project has web logs available.", 'complete': '📋 Web logs check complete. Continue to next step.'}
731: self.step_messages['step_05'] = {'input': f"❔{pip.fmt('step_05')}: All data downloaded. Ready to prepare visualization.", 'complete': 'Visualization ready. Graph link generated.'}
732: self.step_messages['step_02b'] = {'input': f"❔{pip.fmt('step_02b')}: Download node attributes (page type, compliance, etc.)", 'complete': 'Node attributes download complete. Continue to next step.'}
775: pip = self.pipulate
778: return pip.create_standard_landing_page(self)
782: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
790: context = pip.get_plugin_context(self)
799: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
801: pip.db['pipeline_id'] = pipeline_id
802: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
805: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
806: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
807: return pip.run_all_cells(app_name, steps)
815: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
816: pipeline_id = pip.db.get('pipeline_id', 'unknown')
818: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
823: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
825: await self.message_queue.add(pip, 'All steps are complete. You can now finalize the workflow or revert to any step to make changes.', verbatim=True)
830: await pip.finalize_workflow(pipeline_id)
831: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
832: return pip.run_all_cells(app_name, steps)
836: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
837: pipeline_id = pip.db.get('pipeline_id', 'unknown')
838: await pip.unfinalize_workflow(pipeline_id)
839: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
840: return pip.run_all_cells(app_name, steps)
844: pip, db, steps = (self.pipulate, self.pip.db, self.steps)
852: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
858: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
861: pipeline_id = pip.db.get('pipeline_id', 'unknown')
864: await pip.clear_steps_from(pipeline_id, step_id, steps)
865: state = pip.read_state(pipeline_id)
867: pip.write_state(pipeline_id, state)
868: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
869: await self.message_queue.add(pip, message, verbatim=True)
870: await self.message_queue.add(pip, f'↩️ Reverted to {step_id}. All subsequent data has been cleared.', verbatim=True)
871: return pip.run_all_cells(app_name, steps)
879: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
884: pipeline_id = pip.db.get('pipeline_id', 'unknown')
885: state = pip.read_state(pipeline_id)
886: step_data = pip.get_step_data(pipeline_id, step_id, {})
890: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
897: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
900: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
945: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
950: pipeline_id = pip.db.get('pipeline_id', 'unknown')
957: await pip.set_step_data(pipeline_id, step_id, project_data_str, steps)
958: await self.message_queue.add(pip, f"✳️ {step.show} complete: {project_data['project_name']}", verbatim=True)
962: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
966: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
971: pipeline_id = pip.db.get('pipeline_id', 'unknown')
972: state = pip.read_state(pipeline_id)
973: step_data = pip.get_step_data(pipeline_id, step_id, {})
978: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
985: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1010: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_slug}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1064: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Select an analysis for {project_name}'), verbatim=True)
1127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1132: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1134: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1145: await self.message_queue.add(pip, f'📊 Selected analysis: {analysis_slug}. Starting crawl data download...', verbatim=True)
1168: await self.message_queue.add(pip, qualifier_config['user_message_running'], verbatim=True)
1177: await self.message_queue.add(pip, qualifier_config['user_message_found'].format(
1183: await self.message_queue.add(pip, f'Error during qualifier logic: {str(e)}', verbatim=True)
1190: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1212: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1217: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1218: state = pip.read_state(pipeline_id)
1219: step_data = pip.get_step_data(pipeline_id, step_id, {})
1225: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1235: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1269: pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {analysis_slug}', widget=widget, steps=steps),
1289: await self.message_queue.add(pip, f'📄 Ready to download node attributes for analysis {analysis_slug}', verbatim=True)
1313: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1318: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1326: await self.message_queue.add(pip, f"⏭️ Skipping Node Attributes download...", verbatim=True)
1339: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1340: await self.message_queue.add(pip, f"⏭️ Node Attributes step skipped. Proceeding to next step.", verbatim=True)
1343: pip.display_revert_widget(
1357: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1370: await self.message_queue.add(pip, f'📊 Starting node attributes download for {analysis_slug}...', verbatim=True)
1384: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1407: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1412: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1421: step_data = pip.get_step_data(pipeline_id, step_id, {})
1434: await self.message_queue.add(pip, f"✅ Using cached node attributes ({file_info['size']})", verbatim=True)
1474: await self.message_queue.add(pip, '🔄 Initiating node attributes export...', verbatim=True)
1525: await self.message_queue.add(pip, '✅ Node attributes export job created successfully!', verbatim=True)
1526: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
1530: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1544: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1560: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
1565: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
1577: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
1580: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
1591: await self.message_queue.add(pip, '🔄 Downloading node attributes...', verbatim=True)
1608: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
1619: await self.message_queue.add(pip, f'✅ Node attributes data saved with {len(df):,} rows', verbatim=True)
1635: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1648: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1653: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1664: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1687: pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {analysis_slug}', widget=widget, steps=steps),
1694: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1699: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1700: state = pip.read_state(pipeline_id)
1701: step_data = pip.get_step_data(pipeline_id, step_id, {})
1705: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1734: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1736: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1744: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1807: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1812: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1820: await self.message_queue.add(pip, f"⏭️ Skipping Web Logs download...", verbatim=True)
1834: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1835: await self.message_queue.add(pip, f"⏭️ Web Logs step skipped. Proceeding to next step.", verbatim=True)
1838: pip.display_revert_widget(
1851: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1859: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1865: await self.message_queue.add(pip, f"📥 Downloading Web Logs for '{project_name}'...", verbatim=True)
1887: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1892: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1893: state = pip.read_state(pipeline_id)
1894: step_data = pip.get_step_data(pipeline_id, step_id, {})
1898: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1927: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1929: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1938: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1995: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2000: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2008: await self.message_queue.add(pip, f"⏭️ Skipping Search Console download...", verbatim=True)
2022: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
2023: await self.message_queue.add(pip, f"⏭️ Search Console step skipped. Proceeding to next step.", verbatim=True)
2026: pip.display_revert_widget(
2039: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
2063: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2068: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2071: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
2081: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
2097: await self.message_queue.add(pip, f'✅ Project has Search Console data, downloading...', verbatim=True)
2098: await self.process_search_console_data(pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
2100: await self.message_queue.add(pip, f'Project does not have Search Console data (skipping download)', verbatim=True)
2104: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
2126: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {completed_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
2133: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
2139: state = pip.read_state(pipeline_id)
2140: step_data = pip.get_step_data(pipeline_id, step_id, {})
2147: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
2162: pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"{step.show}: Link generated!", widget=widget, steps=steps),
2168: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
2184: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
2207: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2216: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
2217: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
2223: await self.message_queue.add(pip, '❌ Error: Could not load project or analysis data', verbatim=True)
2231: await self.message_queue.add(pip, '❌ Error: Missing required project information', verbatim=True)
2235: await self.message_queue.add(pip, '🔄 Processing data for visualization...', verbatim=True)
2247: await self.message_queue.add(pip, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
2250: await self.message_queue.add(pip, '📊 Loading link graph data...', verbatim=True)
2255: await self.message_queue.add(pip, f'✅ Loaded {len(link_graph_df):,} link graph rows', verbatim=True)
2279: await self.message_queue.add(pip, f'❌ Error: Could not identify source/target columns in link graph. Found: {columns}', verbatim=True)
2282: await self.message_queue.add(pip, f'✅ Using columns: {source_col} → {target_col}', verbatim=True)
2285: await self.message_queue.add(pip, f'❌ Error loading link graph data: {str(e)}', verbatim=True)
2288: await self.message_queue.add(pip, '🔗 Creating edge list for Cosmograph...', verbatim=True)
2298: await self.message_queue.add(pip, f'✅ Created {len(edges_df):,} edges (removed self-loops)', verbatim=True)
2304: await self.message_queue.add(pip, '🎯 Creating node list and merging performance data...', verbatim=True)
2310: await self.message_queue.add(pip, f'✅ Created {len(nodes_df):,} unique nodes', verbatim=True)
2316: await self.message_queue.add(pip, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
2326: await self.message_queue.add(pip, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
2328: await self.message_queue.add(pip, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
2335: await self.message_queue.add(pip, f'📄 Loaded crawl attributes: {len(crawl_df):,} rows', verbatim=True)
2349: await self.message_queue.add(pip, f'✅ Merged crawl attributes (HTTP status, page titles, compliance, etc.)', verbatim=True)
2352: await self.message_queue.add(pip, f'⚠️ Warning: Could not load crawl attributes: {str(e)}', verbatim=True)
2354: await self.message_queue.add(pip, '⚠️ No crawl attributes found from step_02b', verbatim=True)
2360: await self.message_queue.add(pip, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
2370: await self.message_queue.add(pip, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
2372: await self.message_queue.add(pip, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
2374: await self.message_queue.add(pip, '📋 Preparing non-destructive metadata for Cosmograph...', verbatim=True)
2406: await self.message_queue.add(pip, f'✅ Saved visualization files: {len(edges_df):,} edges, {len(cosmo_nodes_df):,} nodes', verbatim=True)
2408: await self.message_queue.add(pip, '🚀 Generating Cosmograph visualization URL...', verbatim=True)
2436: await pip.set_step_data(pipeline_id, step_id, json.dumps(visualization_result), self.steps)
2438: await self.message_queue.add(pip, f'🎉 Visualization ready! {len(edges_df):,} edges and {len(cosmo_nodes_df):,} nodes processed.', verbatim=True)
2452: pip.display_revert_widget(
2465: await self.message_queue.add(pip, f'❌ Error generating visualization: {str(e)}', verbatim=True)
2688: pip = self.pipulate
2735: await self.message_queue.add(pip, f"API error during qualifier check at {iter_param_name}={current_iter_val}: Status {response.status_code}", verbatim=True)
2753: await self.message_queue.add(pip, f"Could not extract metric from response at {iter_param_name}={current_iter_val}", verbatim=True)
2756: await self.message_queue.add(pip, f"🔍 Qualifier '{iter_param_name}' at {current_iter_val}: {metric_value:,} items.", verbatim=True)
2767: await self.message_queue.add(pip, qualifier_config['user_message_threshold_exceeded'].format(metric_value=metric_value), verbatim=True)
2774: await self.message_queue.add(pip, f"Error during qualifier check at {iter_param_name}={current_iter_val}: {str(e)}", verbatim=True)
3107: async def process_search_console_data(self, pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
3121: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})", verbatim=True)
3124: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3126: await self.message_queue.add(pip, '🔄 Initiating Search Console data export...', verbatim=True)
3155: await self.message_queue.add(pip, '✅ Export job created successfully!', verbatim=True)
3158: await self.message_queue.add(pip, f'❌ Error creating export job: {str(e)}', verbatim=True)
3160: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3164: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3166: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3169: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3171: await self.message_queue.add(pip, '🔄 Downloading Search Console data...', verbatim=True)
3198: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3204: await self.message_queue.add(pip, f'❌ Error downloading or extracting file: {str(e)}', verbatim=True)
3206: await self.message_queue.add(pip, '✅ Search Console data ready for analysis!', verbatim=True)
3208: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3213: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3214: await self.message_queue.add(pip, f'❌ Error processing Search Console data: {str(e)}', verbatim=True)
3614: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3619: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3626: step_data = pip.get_step_data(pipeline_id, step_id, {})
3643: await self.message_queue.add(pip, f"✅ Using cached crawl data ({file_info['size']})", verbatim=True)
3681: await self.message_queue.add(pip, '🔄 Initiating crawl data export...', verbatim=True)
3756: await self.message_queue.add(pip, '✅ Crawl export job created successfully!', verbatim=True)
3757: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3760: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3774: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3790: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3796: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
3809: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3812: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3824: await self.message_queue.add(pip, '🔄 Downloading crawl data...', verbatim=True)
3839: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3872: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3885: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3898: await self.message_queue.add(pip, f"✅ Crawl data downloaded: {file_info['size']}", verbatim=True)
3901: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3931: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Analysis {status_text}{download_message}', widget=widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3938: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3943: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3956: await self.message_queue.add(pip, f'{step.show} complete: Project {status_text} web logs', verbatim=True)
3961: await self.message_queue.add(pip, f"✅ Using cached web logs data ({file_info['size']})", verbatim=True)
3978: await self.message_queue.add(pip, '🔄 Initiating web logs export...', verbatim=True)
4021: await self.message_queue.add(pip, f'✅ Web logs export job created successfully! (Job ID: {job_id})', verbatim=True)
4022: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
4025: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
4040: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
4054: await self.message_queue.add(pip, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
4059: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
4065: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
4081: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
4084: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
4097: await self.message_queue.add(pip, '🔄 Downloading web logs data...', verbatim=True)
4128: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
4129: await self.message_queue.add(pip, f"✅ Web logs data downloaded: {file_info['size']}", verbatim=True)
4143: await self.message_queue.add(pip, f'❌ Error downloading file: {str(e)}', verbatim=True)
4156: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
4185: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text} web logs{download_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
4200: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
4206: pipeline_id = pip.db.get('pipeline_id', 'unknown')
4207: step_data = pip.get_step_data(pipeline_id, step_id, {})
4211: state = pip.read_state(pipeline_id)
4241: pip.write_state(pipeline_id, state)
4246: pip.write_state(pipeline_id, state)
5157: pip, db, steps, app_name = self.pipulate, self.pip.db, self.steps, self.app_name
5164: state = pip.read_state(pipeline_id)
5165: step_data = pip.get_step_data(pipeline_id, step_id, {})
5167: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
5170: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\n{current_value}")
5177: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\n{current_value}")
5179: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
5184: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
5185: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
5204: pip, db, steps, app_name = self.pipulate, self.pip.db, self.steps, self.app_name
5214: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
5216: pip.append_to_history(f"[WIDGET CONTENT] {step.show}:\n{value_to_save}")
5217: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
5219: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
5222: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
5248: pip = self.pipulate
5249: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
5250: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
apps/110_parameter_buster.py
224: pip = self.pipulate
225: pip = self.pipulate
226: self.message_queue = pip.message_queue
228: self.ui = pip.get_ui_constants()
229: self.config = pip.get_config()
262: self.step_messages = {'finalize': {'ready': self.ui['MESSAGES']['ALL_STEPS_COMPLETE'], 'complete': f'Workflow finalized. Use {self.ui["BUTTON_LABELS"]["UNLOCK"]} to make changes.'}, 'step_02': {'input': f"❔{pip.fmt('step_02')}: Please select a crawl analysis for this project.", 'complete': '📊 Crawl analysis download complete. Continue to next step.'}}
265: self.step_messages[step.id] = {'input': f'❔{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'✳️ {step.show} complete. Continue to next step.'}
266: self.step_messages['step_04'] = {'input': f"❔{pip.fmt('step_04')}: Please check if the project has Search Console data.", 'complete': 'Search Console check complete. Continue to next step.'}
267: self.step_messages['step_03'] = {'input': f"❔{pip.fmt('step_03')}: Please check if the project has web logs available.", 'complete': '📋 Web logs check complete. Continue to next step.'}
268: self.step_messages['step_05'] = {'input': f"❔{pip.fmt('step_05')}: Ready to count parameters from downloaded data.", 'complete': 'Parameter counting is complete.'}
269: self.step_messages['step_06'] = {'input': f"❔{pip.fmt('step_06')}: Ready to configure parameter optimization.", 'complete': 'Parameter optimization configured.'}
270: self.step_messages['step_07'] = {'input': f"❔{pip.fmt('step_07')}: Ready to generate instructions and robots.txt.", 'complete': 'Instructions generated.'}
310: pip = self.pipulate
312: return pip.create_standard_landing_page(self)
316: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
323: context = pip.get_plugin_context(self)
332: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
334: pip.db['pipeline_id'] = pipeline_id
335: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
338: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
339: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
340: return pip.run_all_cells(app_name, steps)
347: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
348: pipeline_id = pip.db.get('pipeline_id', 'unknown')
350: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
355: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
357: await self.message_queue.add(pip, 'All steps are complete. You can now finalize the workflow or revert to any step to make changes.', verbatim=True)
362: await pip.finalize_workflow(pipeline_id)
363: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
364: return pip.run_all_cells(app_name, steps)
368: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
369: pipeline_id = pip.db.get('pipeline_id', 'unknown')
370: await pip.unfinalize_workflow(pipeline_id)
371: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
372: return pip.run_all_cells(app_name, steps)
376: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
384: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
390: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
393: pipeline_id = pip.db.get('pipeline_id', 'unknown')
396: await pip.clear_steps_from(pipeline_id, step_id, steps)
397: state = pip.read_state(pipeline_id)
399: pip.write_state(pipeline_id, state)
400: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
401: await self.message_queue.add(pip, message, verbatim=True)
402: await self.message_queue.add(pip, f'↩️ Reverted to {step_id}. All subsequent data has been cleared.', verbatim=True)
403: return pip.run_all_cells(app_name, steps)
410: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
415: pipeline_id = pip.db.get('pipeline_id', 'unknown')
416: state = pip.read_state(pipeline_id)
417: step_data = pip.get_step_data(pipeline_id, step_id, {})
421: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
428: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
431: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
475: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
480: pipeline_id = pip.db.get('pipeline_id', 'unknown')
487: await pip.set_step_data(pipeline_id, step_id, project_data_str, steps)
488: await self.message_queue.add(pip, f"✳️ {step.show} complete: {project_data['project_name']}", verbatim=True)
492: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
496: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
501: pipeline_id = pip.db.get('pipeline_id', 'unknown')
502: state = pip.read_state(pipeline_id)
503: step_data = pip.get_step_data(pipeline_id, step_id, {})
508: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
515: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
539: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_slug}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
579: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Select an analysis for {project_name}'), verbatim=True)
636: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
641: pipeline_id = pip.db.get('pipeline_id', 'unknown')
643: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
654: await self.message_queue.add(pip, f'📊 Selected analysis: {analysis_slug}. Starting crawl data download...', verbatim=True)
673: await self.message_queue.add(pip, qualifier_config['user_message_running'], verbatim=True)
680: await self.message_queue.add(pip, qualifier_config['user_message_found'].format(
685: await self.message_queue.add(pip, f'Error during qualifier logic: {str(e)}', verbatim=True)
691: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
713: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
718: pipeline_id = pip.db.get('pipeline_id', 'unknown')
719: state = pip.read_state(pipeline_id)
720: step_data = pip.get_step_data(pipeline_id, step_id, {})
724: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
731: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
753: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
775: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
777: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
784: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
839: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
844: pipeline_id = pip.db.get('pipeline_id', 'unknown')
850: await self.message_queue.add(pip, f"⏭️ Skipping Web Logs download...", verbatim=True)
862: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
863: await self.message_queue.add(pip, f"⏭️ Web Logs step skipped. Proceeding to next step.", verbatim=True)
865: pip.display_revert_widget(
877: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
885: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
891: await self.message_queue.add(pip, f"📥 Downloading Web Logs for '{project_name}'...", verbatim=True)
913: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
918: pipeline_id = pip.db.get('pipeline_id', 'unknown')
919: state = pip.read_state(pipeline_id)
920: step_data = pip.get_step_data(pipeline_id, step_id, {})
924: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
931: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
953: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
975: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
977: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
985: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1035: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1040: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1046: await self.message_queue.add(pip, f"⏭️ Skipping Search Console download...", verbatim=True)
1058: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1059: await self.message_queue.add(pip, f"⏭️ Search Console step skipped. Proceeding to next step.", verbatim=True)
1061: pip.display_revert_widget(
1073: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1097: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1102: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1105: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1115: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1141: await self.message_queue.add(pip, f'✅ Project has Search Console data, downloading...', verbatim=True)
1142: await self.process_search_console_data(pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
1144: await self.message_queue.add(pip, f'Project does not have Search Console data (skipping download)', verbatim=True)
1148: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
1175: pip.display_revert_widget(
1196: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1201: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1202: state = pip.read_state(pipeline_id)
1203: step_data = pip.get_step_data(pipeline_id, step_id, {})
1212: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
1213: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
1224: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1235: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"{step.show}: {json.loads(optimization_result).get('total_unique_parameters', 0):,} unique parameters found", widget=visualization_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1238: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Parameter analysis complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1239: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1250: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1255: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1262: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1270: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
1271: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
1283: await self.message_queue.add(pip, 'Counting parameters...', verbatim=True)
1309: await pip.set_step_data(pipeline_id, step_id, summary_str, steps)
1310: await self.message_queue.add(pip, f"✓ Parameter analysis complete! Found {len(total_unique_params):,} unique parameters across {len(parameter_summary['data_sources'])} sources with {total_occurrences:,} total occurrences.", verbatim=True)
1312: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {len(total_unique_params):,} unique parameters found', widget=visualization_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1319: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1324: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1325: state = pip.read_state(pipeline_id)
1326: step_data = pip.get_step_data(pipeline_id, step_id, {})
1328: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1373: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Parameter Optimization with {(len(selected_params) if selected_params else 0)} parameters', widget=prism_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1379: pip.write_state(pipeline_id, state)
1380: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1428: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1431: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1489: return Div(Card(H3(f'{pip.fmt(step_id)}: {step.show}'), P('Set thresholds for parameter optimization:'), Form(Div(Div(Small('Lower GSC Threshold to lower risk (generally keep set to 0)', style='color: #888; font-style: italic;'), Div(Label(NotStr('GSC Threshold:'), For='gsc_threshold', style='min-width: 180px; color: #888;'), Input(type='range', name='gsc_threshold_slider', id='gsc_threshold_slider', value=gsc_threshold, min='0', max='100', step='1', style='width: 60%; margin: 0 10px;', _oninput="document.getElementById('gsc_threshold').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='input changed delay:300ms, load', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), Input(type='number', name='gsc_threshold', id='gsc_threshold', value=gsc_threshold, min='0', max='100', style='width: 150px;', _oninput="document.getElementById('gsc_threshold_slider').value = this.value; triggerParameterPreview();", _onchange="document.getElementById('gsc_threshold_slider').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='none', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), style='display: flex; align-items: center; gap: 5px;')), Div(Small('Higher Minimum Frequency to reduce to only the biggest offenders', style='color: #888; font-style: italic;'), Div(Label(NotStr('<strong>Minimum Frequency:</strong>'), For='min_frequency', style='min-width: 180px;'), Input(type='range', name='min_frequency_slider', id='min_frequency_slider', value=min_frequency, min='0', max=str(max_frequency), step='1', style='flex-grow: 1; margin: 0 10px;', _oninput="document.getElementById('min_frequency').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='input changed delay:300ms', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), Input(type='number', name='min_frequency', id='min_frequency', value=min_frequency, min='0', max=str(max_frequency), step='1', style='width: 150px;', _oninput="document.getElementById('min_frequency_slider').value = this.value; triggerParameterPreview();", _onchange="document.getElementById('min_frequency_slider').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='none', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), style='display: flex; align-items: center; gap: 5px;'), style='margin-bottom: 15px;'), NotStr(breakpoints_html) if breakpoints_html else None, Div(H4('Parameters That Would Be Optimized:'), Div(P('Adjust thresholds above to see which parameters would be optimized.', style='color: #888; font-style: italic;'), id='parameter-preview', style='max-height: 300px; overflow-y: auto; background: #111; border-radius: 5px; padding: 10px; margin-bottom: 15px;'), style='margin-bottom: 20px;'), Div(Button('Create Optimization ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
1493: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1497: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1502: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1539: pip.append_to_history(f'[OPTIMIZATION CODE] Generated PageWorkers optimization for {len(selected_params)} parameters:\n{js_code}')
1542: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
1545: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Parameter Optimization with {(len(selected_params) if selected_params else 0)} parameters', widget=prism_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1554: pip, db, app_name = (self.pipulate, self.pipulate.db, self.app_name)
1555: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1559: pip.append_to_history(f'[PARAMETER PREVIEW] Previewing parameters with GSC threshold={gsc_threshold} and min_frequency={min_frequency}')
1560: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1636: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1641: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1642: state = pip.read_state(pipeline_id)
1643: step_data = pip.get_step_data(pipeline_id, step_id, {})
1644: step_06_data = pip.get_step_data(pipeline_id, 'step_06', {})
1672: state = pip.read_state(pipeline_id)
1678: pip.write_state(pipeline_id, state)
1680: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1688: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown Documentation', widget=markdown_widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1693: await pip.clear_steps_from(pipeline_id, 'finalize', self.steps)
1694: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1699: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1704: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1708: step_data = pip.get_step_data(pipeline_id, step_id, {})
1716: step_06_data = pip.get_step_data(pipeline_id, 'step_06', {})
1726: state = pip.read_state(pipeline_id)
1730: await pip.clear_steps_from(pipeline_id, step_id, self.steps)
1733: pip.write_state(pipeline_id, state)
1734: await self.message_queue.add(pip, f'{step.show}: Markdown content updated', verbatim=True)
1737: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown Documentation', widget=markdown_widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1903: pip = self.pipulate
1943: await self.message_queue.add(pip, f"API error during qualifier check at {iter_param_name}={current_iter_val}: Status {response.status_code}", verbatim=True)
1957: await self.message_queue.add(pip, f"Could not extract metric from response at {iter_param_name}={current_iter_val}", verbatim=True)
1959: await self.message_queue.add(pip, f"🔍 Qualifier '{iter_param_name}' at {current_iter_val}: {metric_value:,} items.", verbatim=True)
1969: await self.message_queue.add(pip, qualifier_config['user_message_threshold_exceeded'].format(metric_value=metric_value), verbatim=True)
1975: await self.message_queue.add(pip, f"Error during qualifier check at {iter_param_name}={current_iter_val}: {str(e)}", verbatim=True)
2270: async def process_search_console_data(self, pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
2277: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})", verbatim=True)
2280: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2282: await self.message_queue.add(pip, '🔄 Initiating Search Console data export...', verbatim=True)
2310: await self.message_queue.add(pip, '✅ Export job created successfully!', verbatim=True)
2313: await self.message_queue.add(pip, f'❌ Error creating export job: {str(e)}', verbatim=True)
2315: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
2319: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
2321: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
2324: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
2326: await self.message_queue.add(pip, '🔄 Downloading Search Console data...', verbatim=True)
2353: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
2359: await self.message_queue.add(pip, f'❌ Error downloading or extracting file: {str(e)}', verbatim=True)
2361: await self.message_queue.add(pip, '✅ Search Console data ready for analysis!', verbatim=True)
2363: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2368: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2369: await self.message_queue.add(pip, f'❌ Error processing Search Console data: {str(e)}', verbatim=True)
2741: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2746: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2753: step_data = pip.get_step_data(pipeline_id, step_id, {})
2768: await self.message_queue.add(pip, f"✅ Using cached crawl data ({file_info['size']})", verbatim=True)
2804: await self.message_queue.add(pip, '🔄 Initiating crawl data export...', verbatim=True)
2875: await self.message_queue.add(pip, '✅ Crawl export job created successfully!', verbatim=True)
2876: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
2879: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
2893: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
2909: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
2914: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
2926: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
2929: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
2941: await self.message_queue.add(pip, '🔄 Downloading crawl data...', verbatim=True)
2956: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
2986: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
2999: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3012: await self.message_queue.add(pip, f"✅ Crawl data downloaded: {file_info['size']}", verbatim=True)
3014: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3041: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Analysis {status_text}{download_message}', widget=widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3048: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3053: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3066: await self.message_queue.add(pip, f'{step.show} complete: Project {status_text} web logs', verbatim=True)
3071: await self.message_queue.add(pip, f"✅ Using cached web logs data ({file_info['size']})", verbatim=True)
3087: await self.message_queue.add(pip, '🔄 Initiating web logs export...', verbatim=True)
3128: await self.message_queue.add(pip, f'✅ Web logs export job created successfully! (Job ID: {job_id})', verbatim=True)
3129: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3132: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3147: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3161: await self.message_queue.add(pip, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
3166: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3171: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
3186: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3189: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3202: await self.message_queue.add(pip, '🔄 Downloading web logs data...', verbatim=True)
3233: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3234: await self.message_queue.add(pip, f"✅ Web logs data downloaded: {file_info['size']}", verbatim=True)
3247: await self.message_queue.add(pip, f'❌ Error downloading file: {str(e)}', verbatim=True)
3260: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
3286: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text} web logs{download_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3293: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3299: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3302: state = pip.read_state(pipeline_id)
3306: pip.write_state(pipeline_id, state)
3309: pip.write_state(pipeline_id, state)
3317: step_data = pip.get_step_data(pipeline_id, step_id, {})
3333: state = pip.read_state(pipeline_id)
3351: pip.write_state(pipeline_id, state)
3355: pip.write_state(pipeline_id, state)
apps/540_checkboxes.py
51: pip = self.pipulate
52: pip = self.pipulate
53: self.message_queue = pip.message_queue
64: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
66: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
72: pip = self.pipulate
75: return pip.create_standard_landing_page(self)
79: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: context = pip.get_plugin_context(self)
96: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
98: pip.db['pipeline_id'] = pipeline_id
99: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
102: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
103: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
104: return pip.run_all_cells(app_name, steps)
108: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
109: pipeline_id = pip.db.get('pipeline_id', 'unknown')
111: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
114: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='checkbox-widget-unlock-button', aria_label='Unlock checkbox workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
116: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
122: await pip.finalize_workflow(pipeline_id)
123: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
124: return pip.run_all_cells(app_name, steps)
128: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
129: pipeline_id = pip.db.get('pipeline_id', 'unknown')
130: await pip.unfinalize_workflow(pipeline_id)
131: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
132: return pip.run_all_cells(app_name, steps)
136: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
144: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
150: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
153: pipeline_id = pip.db.get('pipeline_id', 'unknown')
156: await pip.clear_steps_from(pipeline_id, step_id, steps)
157: state = pip.read_state(pipeline_id)
159: pip.write_state(pipeline_id, state)
160: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
161: await self.message_queue.add(pip, message, verbatim=True)
162: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
182: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
186: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: {', '.join(selected_values)}", steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
187: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
216: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
221: pipeline_id = pip.db.get('pipeline_id', 'unknown')
apps/010_introduction.py
8:- Global Voice Toggle (persisted in pip.db as '1'/'0')
39: self.pip = pipulate
67: current_state = self.pip.db.get('voice_enabled', '0') == '1'
71: self.pip.db['voice_enabled'] = '1' if new_state else '0'
106: voice_enabled = self.pip.db.get('voice_enabled', '0') == '1'
apps/580_upload.py
64: pip = self.pipulate
65: pip = self.pipulate
66: self.message_queue = pip.message_queue
80: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please complete each step to explore the file upload widget.', 'step_01': {'input': 'Please select files to upload.', 'complete': 'Files uploaded successfully.'}}
86: pip = self.pipulate
89: return pip.create_standard_landing_page(self)
93: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
101: context = pip.get_plugin_context(self)
110: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
112: pip.db['pipeline_id'] = pipeline_id
114: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
119: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
120: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
122: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
123: await self.message_queue.add(pip, status_msg, verbatim=True)
125: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
126: parsed = pip.parse_pipeline_key(pipeline_id)
132: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
133: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
138: pipeline_id = pip.db.get('pipeline_id', 'unknown')
140: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
143: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
145: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
151: await pip.finalize_workflow(pipeline_id)
152: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
153: return pip.run_all_cells(app_name, steps)
157: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
158: pipeline_id = pip.db.get('pipeline_id', 'unknown')
159: await pip.unfinalize_workflow(pipeline_id)
160: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
161: return pip.run_all_cells(app_name, steps)
171: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
174: pipeline_id = pip.db.get('pipeline_id', 'unknown')
177: await pip.clear_steps_from(pipeline_id, step_id, steps)
178: state = pip.read_state(pipeline_id)
180: pip.write_state(pipeline_id, state)
181: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
182: await self.message_queue.add(pip, message, verbatim=True)
183: return pip.run_all_cells(app_name, steps)
187: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
192: pipeline_id = pip.db.get('pipeline_id', 'unknown')
193: state = pip.read_state(pipeline_id)
194: step_data = pip.get_step_data(pipeline_id, step_id, {})
196: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
236: file_summary = pip.get_step_data(pipeline_id, step_id)
304: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Files previously uploaded', widget=Div(
310: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
311: await self.message_queue.add(pip, explanation, verbatim=True)
312: return Div(Card(H3(f'{pip.fmt(step_id)}: {step.show}'), P(explanation, cls='text-secondary'), Form(Input(type='file', name='uploaded_files', multiple='true', required='true', cls='contrast', data_testid=self.UI_CONSTANTS["AUTOMATION"]["FILE_INPUT_TESTID"], aria_label='Select files to upload to the server'), Button('Upload Files ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION"]["UPLOAD_BUTTON_TESTID"], aria_label='Upload selected files to server'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', enctype='multipart/form-data')), Div(id=next_step_id), id=step_id)
316: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
321: pipeline_id = pip.db.get('pipeline_id', 'unknown')
325: await self.message_queue.add(pip, 'No files selected. Please try again.', verbatim=True)
327: return Div(Card(H3(f'{pip.fmt(step_id)}: {step.show}'), P('No files were selected. Please try again.', cls='text-invalid'), P(explanation, cls='text-secondary'), Form(Input(type='file', name='uploaded_files', multiple='true', required='true', cls='contrast', data_testid=self.UI_CONSTANTS["AUTOMATION"]["FILE_INPUT_TESTID"], aria_label='Select files to upload to the server'), Button('Upload Files ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION"]["UPLOAD_BUTTON_TESTID"], aria_label='Upload selected files to server'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', enctype='multipart/form-data')), Div(id=next_step_id), id=step_id)
336: await self.message_queue.add(pip, error_msg, verbatim=True)
369: await self.message_queue.add(pip, error_msg, verbatim=True)
377: await pip.set_step_data(pipeline_id, step_id, file_summary, steps)
378: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{file_summary}')
379: pip.append_to_history(f'[WIDGET STATE] {step.show}: Files saved')
380: await self.message_queue.add(pip, f'Successfully saved {len(actual_saved_file_details)} files to {save_directory}', verbatim=True)
440: content_container = pip.display_revert_widget(
apps/440_browser_automation.py
67: pip = self.pipulate
68: pip = self.pipulate
69: self.message_queue = pip.message_queue
82: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
84: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
90: pip = self.pipulate
93: return pip.create_standard_landing_page(self)
97: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
105: context = pip.get_plugin_context(self)
114: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
116: pip.db['pipeline_id'] = pipeline_id
117: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
120: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
121: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
122: return pip.run_all_cells(app_name, steps)
126: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
127: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
132: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{finalize_step.id}'), id=finalize_step.id)
134: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
140: state = pip.read_state(pipeline_id)
142: step_data = pip.get_step_data(pipeline_id, step.id, {})
147: pip.write_state(pipeline_id, state)
148: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
149: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{finalize_step.id}'), id=finalize_step.id)
153: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
154: pipeline_id = pip.db.get('pipeline_id', 'unknown')
155: state = pip.read_state(pipeline_id)
161: pip.write_state(pipeline_id, state)
162: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
167: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
175: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
181: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
184: pipeline_id = pip.db.get('pipeline_id', 'unknown')
187: await pip.clear_steps_from(pipeline_id, step_id, steps)
188: state = pip.read_state(pipeline_id)
200: pip.write_state(pipeline_id, state)
201: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
202: await self.message_queue.add(pip, message, verbatim=True)
203: return pip.run_all_cells(app_name, steps)
207: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
212: pipeline_id = pip.db.get('pipeline_id', 'unknown')
213: state = pip.read_state(pipeline_id)
214: step_data = pip.get_step_data(pipeline_id, step_id, {})
216: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
220: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Open URL: {url_value}', widget=Div(P(f'URL opened (and closed): ', B(url_value)), Div(id=f'{step_id}-status')), steps=steps)
223: await self.message_queue.add(pip, 'Enter the URL you want to open with Selenium:', verbatim=True)
229: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
234: pipeline_id = pip.db.get('pipeline_id', 'unknown')
241: await pip.set_step_data(pipeline_id, step_id, url, steps)
252: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
254: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
257: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
259: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
261: await self.message_queue.add(pip, f'Opening URL with Selenium: {url}', verbatim=True)
265: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
267: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
274: await self.message_queue.add(pip, safe_error_msg, verbatim=True)
277: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Open URL: {url}', widget=url_widget, steps=steps)
282: pip, db = (self.pipulate, self.pipulate.db)
297: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
299: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
302: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
304: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
306: await self.message_queue.add(pip, f'Reopening URL with Selenium: {url}', verbatim=True)
310: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
312: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
319: await self.message_queue.add(pip, error_msg, verbatim=True)
324: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
329: pipeline_id = pip.db.get('pipeline_id', 'unknown')
330: state = pip.read_state(pipeline_id)
331: step_data = pip.get_step_data(pipeline_id, step_id, {})
333: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
337: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"Crawl URL: {url_value.get('url', '')}", widget=Div(P(f'URL crawled and saved: ', B(url_value.get('url', ''))), P(f"Title: {url_value.get('title', '')}"), P(f"Status: {url_value.get('status', '')}"), P(f"Saved to: {url_value.get('save_path', '')}"), P(f"Reconstructed URL: {url_value.get('reconstructed_url', '')}", cls='text-secondary'), Div(id=f'{step_id}-status')), steps=steps)
340: await self.message_queue.add(pip, 'Enter the URL you want to crawl:', verbatim=True)
353: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
358: pipeline_id = pip.db.get('pipeline_id', 'unknown')
375: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
377: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
380: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
382: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
384: await self.message_queue.add(pip, f'Crawling URL with Selenium: {url}', verbatim=True)
408: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
413: await pip.set_step_data(pipeline_id, step_id, crawl_data, steps)
414: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
416: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"Crawl URL: {crawl_data['url']}", widget=url_widget, steps=steps)
422: await self.message_queue.add(pip, safe_error_msg, verbatim=True)
514: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
519: pipeline_id = pip.db.get('pipeline_id', 'unknown')
520: state = pip.read_state(pipeline_id)
525: pip.write_state(pipeline_id, state)
526: await self.message_queue.add(pip, 'Ephemeral login test confirmed!', verbatim=True)
527: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message='Ephemeral Login Test', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
596: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
601: pipeline_id = pip.db.get('pipeline_id', 'unknown')
602: state = pip.read_state(pipeline_id)
607: pip.write_state(pipeline_id, state)
608: await self.message_queue.add(pip, 'Persistent login test confirmed!', verbatim=True)
609: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message='Persistent Login Test', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
613: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
618: pipeline_id = pip.db.get('pipeline_id', 'unknown')
619: state = pip.read_state(pipeline_id)
620: step_data = pip.get_step_data(pipeline_id, step_id, {})
622: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
624: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Finalized):\n{placeholder_value}')
627: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Completed):\n{placeholder_value}')
628: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
630: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing input form')
631: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
636: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
641: pipeline_id = pip.db.get('pipeline_id', 'unknown')
643: await pip.set_step_data(pipeline_id, step_id, placeholder_value, steps)
644: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{placeholder_value}')
645: pip.append_to_history(f'[WIDGET STATE] {step.show}: Step completed')
646: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
647: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/520_text_area.py
45: pip = self.pipulate
46: pip = self.pipulate
47: self.message_queue = pip.message_queue
58: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
60: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please enter {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
66: pip = self.pipulate
69: return pip.create_standard_landing_page(self)
72: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
80: context = pip.get_plugin_context(self)
89: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
91: pip.db['pipeline_id'] = pipeline_id
92: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
95: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
96: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
97: return pip.run_all_cells(app_name, steps)
100: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
101: pipeline_id = pip.db.get('pipeline_id', 'unknown')
103: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
106: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='text-area-widget-unlock-button', aria_label='Unlock text area for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
108: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
114: await pip.finalize_workflow(pipeline_id)
115: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
116: return pip.run_all_cells(app_name, steps)
119: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
120: pipeline_id = pip.db.get('pipeline_id', 'unknown')
121: await pip.unfinalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
123: return pip.run_all_cells(app_name, steps)
126: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
134: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
150: await self.message_queue.add(pip, message, verbatim=True)
151: return pip.run_all_cells(app_name, steps)
156: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
161: pipeline_id = pip.db.get('pipeline_id', 'unknown')
162: state = pip.read_state(pipeline_id)
163: step_data = pip.get_step_data(pipeline_id, step_id, {})
165: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
168: await self.message_queue.add(pip, locked_msg, verbatim=True)
172: await self.message_queue.add(pip, completed_msg, verbatim=True)
174: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=text_widget, steps=steps)
182: await self.message_queue.add(pip, form_msg, verbatim=True)
183: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
185: await self.message_queue.add(pip, explanation, verbatim=True)
186: return Div(Card(H3(f'{pip.fmt(step.id)}: Enter {step.show}'), P(explanation, cls='text-secondary'), Form(pip.wrap_with_inline_button(Textarea(display_value, name=step.done, placeholder=f'Enter {step.show}', required=True, autofocus=True, cls='textarea-standard', data_testid='text-area-widget-textarea-input', aria_label='Multi-line text input area', aria_required='true', aria_labelledby=f'{step_id}-form-title', aria_describedby=f'{step_id}-form-instruction'), button_label='Next ▸'), hx_post=f'/{app_name}/{step.id}_submit', hx_target=f'#{step.id}')), Div(id=next_step_id), id=step.id)
190: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
195: pipeline_id = pip.db.get('pipeline_id', 'unknown')
197: return await pip.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
201: await self.message_queue.add(pip, submit_msg, verbatim=True)
202: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
205: await self.message_queue.add(pip, error_msg, verbatim=True)
208: await pip.set_step_data(pipeline_id, step_id, processed_val, steps)
210: await self.message_queue.add(pip, confirm_msg, verbatim=True)
211: if pip.check_finalize_needed(step_index, steps):
213: await self.message_queue.add(pip, finalize_msg, verbatim=True)
215: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=text_widget, steps=steps)
apps/510_text_field.py
30: pip = self.pipulate
31: pip = self.pipulate
32: self.message_queue = pip.message_queue
43: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
45: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please enter {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
51: pip = self.pipulate
54: return pip.create_standard_landing_page(self)
57: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
65: context = pip.get_plugin_context(self)
74: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
76: pip.db['pipeline_id'] = pipeline_id
77: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
80: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
81: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
82: return pip.run_all_cells(app_name, steps)
85: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: pipeline_id = pip.db.get('pipeline_id', 'unknown')
88: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
91: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='text-field-widget-unlock-button', aria_label='Unlock workflow'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
93: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
99: await pip.finalize_workflow(pipeline_id)
100: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
101: return pip.run_all_cells(app_name, steps)
104: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
105: pipeline_id = pip.db.get('pipeline_id', 'unknown')
106: await pip.unfinalize_workflow(pipeline_id)
107: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
108: return pip.run_all_cells(app_name, steps)
111: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
119: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
124: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
127: pipeline_id = pip.db.get('pipeline_id', 'unknown')
130: await pip.clear_steps_from(pipeline_id, step_id, steps)
131: state = pip.read_state(pipeline_id)
133: pip.write_state(pipeline_id, state)
134: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
135: await self.message_queue.add(pip, message, verbatim=True)
136: return pip.run_all_cells(app_name, steps)
140: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
145: pipeline_id = pip.db.get('pipeline_id', 'unknown')
146: state = pip.read_state(pipeline_id)
147: step_data = pip.get_step_data(pipeline_id, step_id, {})
149: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
152: await self.message_queue.add(pip, locked_msg, verbatim=True)
156: await self.message_queue.add(pip, completed_msg, verbatim=True)
157: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {user_val}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'))
161: await self.message_queue.add(pip, form_msg, verbatim=True)
162: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
164: await self.message_queue.add(pip, explanation, verbatim=True)
165: return Div(Card(H3(f'{pip.fmt(step.id)}: Enter {step.show}'), P(explanation, cls='text-secondary'), Form(pip.wrap_with_inline_button(Input(type='text', name=step.done, value=display_value, placeholder=f'Enter {step.show}', required=True, autofocus=True, _onfocus='this.setSelectionRange(this.value.length, this.value.length)', cls='textfield-standard', aria_required='true', aria_labelledby=f'{step_id}-form-title', aria_describedby=f'{step_id}-form-instruction', data_testid='text-field-widget-text-input', aria_label='Text input field'), button_label='Next ▸', button_data_testid='text-field-widget-next-button', button_aria_label='Continue to next step'), hx_post=f'/{app_name}/{step.id}_submit', hx_target=f'#{step.id}')), Div(id=next_step_id), id=step.id)
169: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
174: pipeline_id = pip.db.get('pipeline_id', 'unknown')
176: return await pip.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
180: await self.message_queue.add(pip, submit_msg, verbatim=True)
181: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
184: await self.message_queue.add(pip, error_msg, verbatim=True)
187: await pip.set_step_data(pipeline_id, step_id, processed_val, steps)
189: await self.message_queue.add(pip, confirm_msg, verbatim=True)
190: if pip.check_finalize_needed(step_index, steps):
192: await self.message_queue.add(pip, finalize_msg, verbatim=True)
193: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {processed_val}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/810_webbrowser.py
34: pip = self.pipulate
35: pip = self.pipulate
36: self.message_queue = pip.message_queue
47: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter the URL to open.', 'complete': 'URL processed.'}}
53: pip = self.pipulate
56: return pip.create_standard_landing_page(self)
59: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
67: context = pip.get_plugin_context(self)
76: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
78: pip.db['pipeline_id'] = pipeline_id
79: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
82: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
83: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
84: return pip.run_all_cells(app_name, steps)
87: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
88: pipeline_id = pip.db.get('pipeline_id', 'unknown')
90: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
93: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
95: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
101: await pip.finalize_workflow(pipeline_id)
102: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
103: return pip.run_all_cells(app_name, steps)
106: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
107: pipeline_id = pip.db.get('pipeline_id', 'unknown')
108: await pip.unfinalize_workflow(pipeline_id)
109: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
110: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = pip.db.get('pipeline_id', 'unknown')
124: await pip.clear_steps_from(pipeline_id, step_id, steps)
125: state = pip.read_state(pipeline_id)
127: pip.write_state(pipeline_id, state)
128: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
129: await self.message_queue.add(pip, message, verbatim=True)
130: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
143: state = pip.read_state(pipeline_id)
144: step_data = pip.get_step_data(pipeline_id, step_id, {})
146: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
152: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {user_val}', widget=url_widget_display, steps=steps)
156: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
158: await self.message_queue.add(pip, explanation, verbatim=True)
159: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Input(type='url', name=step.done, placeholder='https://example.com', required=True, value=display_value, cls='contrast'), Div(Button('Open URL ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
162: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
167: pipeline_id = pip.db.get('pipeline_id', 'unknown')
174: await pip.set_step_data(pipeline_id, step_id, url_to_open, steps)
177: await self.message_queue.add(pip, f'Attempting to open URL: {url_to_open}', verbatim=True)
181: await self.message_queue.add(pip, error_msg, verbatim=True)
184: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Error Opening URL', widget=Div(error_widget, url_widget_display_on_error), steps=steps)
186: pip.append_to_history(f'[WIDGET ACTION] {step.show}: Opened URL {url_to_open}')
188: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: URL opened - {url_to_open}', widget=url_widget_display, steps=steps)
190: await self.message_queue.add(pip, f'{step.show} complete. URL opened.', verbatim=True)
191: if pip.check_finalize_needed(step_index, steps):
192: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/050_documentation.py
83: pip = self.pipulate
apps/720_rich.py
33: pip = self.pipulate
34: pip = self.pipulate
35: self.message_queue = pip.message_queue
46: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter JSON data for the Rich Table.', 'complete': 'JSON data processed and Rich Table rendered.'}}
52: pip = self.pipulate
55: return pip.create_standard_landing_page(self)
58: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
66: context = pip.get_plugin_context(self)
75: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
77: pip.db['pipeline_id'] = pipeline_id
78: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
81: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
82: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
83: return pip.run_all_cells(app_name, steps)
86: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: pipeline_id = pip.db.get('pipeline_id', 'unknown')
89: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
92: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
94: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
100: await pip.finalize_workflow(pipeline_id)
101: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
102: return pip.run_all_cells(app_name, steps)
105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = pip.db.get('pipeline_id', 'unknown')
107: await pip.unfinalize_workflow(pipeline_id)
108: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
109: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = pip.db.get('pipeline_id', 'unknown')
124: await pip.clear_steps_from(pipeline_id, step_id, steps)
125: state = pip.read_state(pipeline_id)
127: pip.write_state(pipeline_id, state)
128: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
129: await self.message_queue.add(pip, message, verbatim=True)
130: return pip.run_all_cells(app_name, steps)
166: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
171: pipeline_id = pip.db.get('pipeline_id', 'unknown')
172: state = pip.read_state(pipeline_id)
173: step_data = pip.get_step_data(pipeline_id, step_id, {})
175: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
188: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
193: pip.write_state(pipeline_id, state)
195: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
196: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter table data as JSON array of objects. Example is pre-populated.'), P('Format: [{"name": "value", "value1": number, ...}, {...}]', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the table', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
200: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
205: pipeline_id = pip.db.get('pipeline_id', 'unknown')
208: is_valid, error_msg, error_component = pip.validate_step_input(table_data, step.show)
219: await pip.set_step_data(pipeline_id, step_id, table_data, steps)
222: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table created with {len(data)} rows', widget=table_widget, steps=steps)
224: await self.message_queue.add(pip, f'{step.show} complete. Table created with {len(data)} rows.', verbatim=True)
apps/100_connect_with_botify.py
43: pip = self.pipulate
44: pip = self.pipulate
47: self.message_queue = pip.message_queue
78: "complete": f"✅ Botify API token saved. Use {pip.UNLOCK_BUTTON_LABEL} to change token."
85: "input": f"{pip.fmt(step.id)}: Please enter {step.show}.",
125: pip, pipeline, steps, app_name = self.pipulate, self.pipeline, self.steps, self.app_name
151: pip.wrap_with_inline_button(
202: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
208: state, error = pip.initialize_if_missing(pipeline_id, {"app_name": app_name})
212: state = pip.read_state(pipeline_id)
216: pip.write_state(pipeline_id, state)
225: f"🟢 Botify API token validated for user: {pip.fmt(username)}. Ready to finalize.",
235: return pip.run_all_cells(app_name, steps)
259: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
262: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
272: P(f"Botify API token is saved. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.",
276: pip.UNLOCK_BUTTON_LABEL,
283: title=f"Click to {pip.UNLOCK_BUTTON_LABEL.lower()} and modify token"
333: state = pip.read_state(pipeline_id)
337: pip.write_state(pipeline_id, state)
340: return pip.run_all_cells(app_name, steps)
343: state = pip.read_state(pipeline_id)
346: pip.write_state(pipeline_id, state)
354: await self.safe_stream(f"✅ Botify API token saved to botify_token.txt for user: {pip.fmt(username)}", verbatim=True, spaces_after=1)
366: return pip.run_all_cells(app_name, steps)
403: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
407: state = pip.read_state(pipeline_id)
410: pip.write_state(pipeline_id, state)
427: return pip.run_all_cells(app_name, steps)
430: pip, db, steps = self.pipulate, self.pipulate.db, self.steps
440: prev_data = pip.get_step_data(db["pipeline_id"], prev_step_id, {})
445: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
451: await pip.clear_steps_from(pipeline_id, step_id, steps)
452: state = pip.read_state(pipeline_id)
454: pip.write_state(pipeline_id, state)
456: return pip.run_all_cells(app_name, steps)
476: pip = self.pipulate
482: pip,
493: pip,
508: pip,
apps/730_matplotlib.py
43: pip = self.pipulate
44: pip = self.pipulate
45: self.message_queue = pip.message_queue
56: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please enter JSON counter data to create a histogram visualization.', 'step_01': {'input': 'Please enter JSON counter data for the histogram.', 'complete': 'Counter data processed and histogram rendered.'}}
62: pip = self.pipulate
65: return pip.create_standard_landing_page(self)
69: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
77: context = pip.get_plugin_context(self)
86: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
88: pip.db['pipeline_id'] = pipeline_id
90: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
95: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
96: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
98: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
99: await self.message_queue.add(pip, status_msg, verbatim=True)
101: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
102: parsed = pip.parse_pipeline_key(pipeline_id)
108: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
109: return pip.run_all_cells(app_name, steps)
113: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
114: pipeline_id = pip.db.get('pipeline_id', 'unknown')
116: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
119: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
121: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
127: await pip.finalize_workflow(pipeline_id)
128: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
129: return pip.run_all_cells(app_name, steps)
133: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
134: pipeline_id = pip.db.get('pipeline_id', 'unknown')
135: await pip.unfinalize_workflow(pipeline_id)
136: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
137: return pip.run_all_cells(app_name, steps)
147: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
150: pipeline_id = pip.db.get('pipeline_id', 'unknown')
153: await pip.clear_steps_from(pipeline_id, step_id, steps)
154: state = pip.read_state(pipeline_id)
156: pip.write_state(pipeline_id, state)
157: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
158: await self.message_queue.add(pip, message, verbatim=True)
159: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
176: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
187: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=histogram_widget, steps=steps)
192: pip.write_state(pipeline_id, state)
194: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
195: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter counter data as JSON object (keys and values):'), P('Format: {"category1": count1, "category2": count2, ...}', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON object for Counter data', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Histogram ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
203: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
207: pipeline_id = pip.db.get('pipeline_id', 'unknown')
211: is_valid, error_msg, error_component = pip.validate_step_input(counter_data, step.show)
222: await pip.set_step_data(pipeline_id, step_id, counter_data, steps)
225: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Histogram created from Counter data', widget=histogram_widget, steps=steps)
227: await self.message_queue.add(pip, f'{step.show} complete. Histogram created.', verbatim=True)
(nix)
[mike@nixos:~/repos/pipulate]$
And:
(nix)
[mike@nixos:~/repos/pipulate]$ rg '\b(self\.)?pip\b' apps/ -t py --stats
apps/300_blank_placeholder.py
36: pip = self.pipulate
37: pip = self.pipulate
38: self.message_queue = pip.get_message_queue()
41: self.ui = pip.get_ui_constants()
70: pip = self.pipulate
73: return pip.create_standard_landing_page(self)
77: pip, db, steps, app_name = (self.pipulate, self.pipulate.db, self.steps, self.APP_NAME)
85: context = pip.get_plugin_context(self)
94: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
96: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
105: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["WORKFLOW"]} Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
106: await self.message_queue.add(pip, f"{self.ui["EMOJIS"]["KEY"]} Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
113: await self.message_queue.add(pip, status_msg, verbatim=True)
115: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["INPUT_FORM"]} Please complete each step in sequence. Your progress will be saved automatically.', verbatim=True)
117: parsed = pip.parse_pipeline_key(pipeline_id)
123: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
124: return pip.run_all_cells(app_name, steps)
127: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
129: pipeline_id = pip.db.get('pipeline_id', 'unknown')
132: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
160: all_data_steps_complete = all(pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
189: await pip.finalize_workflow(pipeline_id)
190: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
191: return pip.run_all_cells(app_name, self.steps)
194: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
195: pipeline_id = pip.db.get('pipeline_id', 'unknown')
196: await pip.unfinalize_workflow(pipeline_id)
197: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
198: return pip.run_all_cells(app_name, self.steps)
201: pip, db, current_steps = self.pipulate, self.pipulate.db, self.steps
209: prev_data = pip.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
215: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
219: pipeline_id = pip.db.get('pipeline_id', 'unknown')
224: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
225: state = pip.read_state(pipeline_id)
227: pip.write_state(pipeline_id, state)
229: message = await pip.get_state_message(pipeline_id, current_steps_to_pass_helpers, self.step_messages)
230: await self.message_queue.add(pip, message, verbatim=True)
231: return pip.run_all_cells(app_name, current_steps_to_pass_helpers)
236: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.APP_NAME
242: pipeline_id = pip.db.get('pipeline_id', 'unknown')
243: state = pip.read_state(pipeline_id)
244: step_data = pip.get_step_data(pipeline_id, step_id, {})
246: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
249: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\\n{current_value}")
256: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\\n{current_value}")
258: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
263: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
264: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
301: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.APP_NAME
306: pipeline_id = pip.db.get('pipeline_id', 'unknown')
311: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
313: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
315: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
318: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
apps/820_selenium.py
41: pip = self.pipulate
42: pip = self.pipulate
43: self.message_queue = pip.message_queue
54: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter the URL to open with Selenium.', 'complete': 'URL processed for Selenium.'}}
60: pip = self.pipulate
63: return pip.create_standard_landing_page(self)
66: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
74: context = pip.get_plugin_context(self)
83: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
85: pip.db['pipeline_id'] = pipeline_id
86: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
89: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
90: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
91: return pip.run_all_cells(app_name, steps)
94: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
95: pipeline_id = pip.db.get('pipeline_id', 'unknown')
97: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
100: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
102: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
108: await pip.finalize_workflow(pipeline_id)
109: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
110: return pip.run_all_cells(app_name, steps)
113: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
114: pipeline_id = pip.db.get('pipeline_id', 'unknown')
115: await pip.unfinalize_workflow(pipeline_id)
116: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
117: return pip.run_all_cells(app_name, steps)
125: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
131: await pip.clear_steps_from(pipeline_id, step_id, steps)
132: state = pip.read_state(pipeline_id)
134: pip.write_state(pipeline_id, state)
135: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
136: await self.message_queue.add(pip, message, verbatim=True)
137: return pip.run_all_cells(app_name, steps)
145: pip = self.pipulate
155: await self.message_queue.add(pip, f'Effective OS for Selenium: {effective_os}', verbatim=True)
157: await self.message_queue.add(pip, 'Using webdriver-manager for macOS.', verbatim=True)
160: await self.message_queue.add(pip, "Attempting to use system ChromeDriver (ensure it's in PATH).", verbatim=True)
162: await self.message_queue.add(pip, 'Initializing Chrome driver with Selenium...', verbatim=True)
164: await self.message_queue.add(pip, f'Selenium opening URL: {url_to_open}', verbatim=True)
168: await self.message_queue.add(pip, f'Selenium page loaded. Title: {page_title}', verbatim=True)
171: await self.message_queue.add(pip, 'Selenium browser closed.', verbatim=True)
177: await self.message_queue.add(pip, error_msg, verbatim=True)
183: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
188: pipeline_id = pip.db.get('pipeline_id', 'unknown')
189: state = pip.read_state(pipeline_id)
190: step_data = pip.get_step_data(pipeline_id, step_id, {})
192: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
198: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {user_val}', widget=url_widget_display, steps=steps)
202: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
204: await self.message_queue.add(pip, explanation, verbatim=True)
205: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Input(type='url', name=step.done, placeholder='https://www.google.com', required=True, value=display_value, cls='contrast'), Div(Button('Open with Selenium ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
208: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
213: pipeline_id = pip.db.get('pipeline_id', 'unknown')
220: await pip.set_step_data(pipeline_id, step_id, url_to_open, steps)
222: pip.append_to_history(f'[WIDGET ACTION] {step.show}: Attempted to open URL {url_to_open}. Success: {success}. Message: {message}')
226: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: URL processed - {url_to_open}', widget=combined_widget, steps=steps)
228: await self.message_queue.add(pip, f'{step.show} complete. {message}', verbatim=True)
229: if pip.check_finalize_needed(step_index, steps):
230: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
235: pip = self.pipulate
apps/015_backup_introduction.py
27: pip = self.pipulate
apps/200_workflow_genesis.py
59: pip = self.pipulate
60: pip = self.pipulate
61: self.message_queue = pip.get_message_queue()
101: pip = self.pipulate
105: 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'
123: pip = self.pipulate
126: return pip.create_standard_landing_page(self)
129: pip, db = self.pipulate, self.pipulate.db
140: _, prefix_for_key_gen, _ = pip.generate_pipeline_key(self)
144: _, prefix, user_part = pip.generate_pipeline_key(self, user_input_key)
147: pip.db['pipeline_id'] = pipeline_id
148: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
151: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
152: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
154: return pip.run_all_cells(internal_app_name, self.steps)
158: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
159: pipeline_id = pip.db.get('pipeline_id', 'unknown')
162: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
170: Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'),
177: all_data_steps_complete = all(pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
192: await pip.finalize_workflow(pipeline_id)
193: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
194: return pip.run_all_cells(app_name, self.steps)
197: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
198: pipeline_id = pip.db.get('pipeline_id', 'unknown')
199: await pip.unfinalize_workflow(pipeline_id)
200: await self.message_queue.add(pip, 'Workflow creation unfinalized. You can now modify any step.', verbatim=True)
201: return pip.run_all_cells(app_name, self.steps)
204: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
207: pipeline_id = pip.db.get('pipeline_id', 'unknown')
212: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, self.steps)
213: state = pip.read_state(pipeline_id)
215: pip.write_state(pipeline_id, state)
217: message = await pip.get_state_message(pipeline_id, self.steps, self.step_messages)
218: await self.message_queue.add(pip, message, verbatim=True)
219: return pip.run_all_cells(app_name, self.steps)
496: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
502: pipeline_id = pip.db.get('pipeline_id', 'unknown')
503: state = pip.read_state(pipeline_id)
504: step_data = pip.get_step_data(pipeline_id, step_id, {})
506: finalize_sys_data = pip.get_step_data(pipeline_id, 'finalize', {})
510: pip.finalized_content(message=f"🔒 {step_obj.show}", content=P(f"Workflow: {current_value.get('display_name', 'Unknown')}", cls='text-success')),
517: pip.display_revert_widget(
528: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
574: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
580: pipeline_id = pip.db.get('pipeline_id', 'unknown')
594: await pip.set_step_data(pipeline_id, step_id, params, self.steps)
595: await self.message_queue.add(pip, self.step_messages[step_id]['complete'], verbatim=True)
598: pip.display_revert_widget(
611: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
617: pipeline_id = pip.db.get('pipeline_id', 'unknown')
618: state = pip.read_state(pipeline_id)
619: step_data = pip.get_step_data(pipeline_id, step_id, {})
621: finalize_sys_data = pip.get_step_data(pipeline_id, 'finalize', {})
626: pip.finalized_content(message=f"🔒 {step_obj.show}", content=P(f"Template: {template_info['name']}", cls='text-success')),
634: pip.display_revert_widget(
645: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
666: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
672: pipeline_id = pip.db.get('pipeline_id', 'unknown')
680: await pip.set_step_data(pipeline_id, step_id, template_choice, self.steps)
681: await self.message_queue.add(pip, self.step_messages[step_id]['complete'], verbatim=True)
684: step_01_data = pip.get_step_data(pipeline_id, 'step_01', {})
760: pip.display_revert_widget(
773: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
779: pipeline_id = pip.db.get('pipeline_id', 'unknown')
780: state = pip.read_state(pipeline_id)
781: step_data = pip.get_step_data(pipeline_id, step_id, {})
783: finalize_sys_data = pip.get_step_data(pipeline_id, 'finalize', {})
787: pip.finalized_content(message=f"🔒 {step_obj.show}", content=P("Command execution complete.", cls='text-success')),
794: pip.display_revert_widget(
805: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
808: step_01_data = pip.get_step_data(pipeline_id, 'step_01', {})
866: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
872: pipeline_id = pip.db.get('pipeline_id', 'unknown')
875: step_01_data = pip.get_step_data(pipeline_id, 'step_01', {})
876: step_02_data = pip.get_step_data(pipeline_id, 'step_02', {})
945: await self.message_queue.add(pip, "⚠️ Another critical operation is in progress. Please wait and try again.", verbatim=True)
949: await self.message_queue.add(pip, "🔄 Executing workflow creation commands...", verbatim=True)
984: await self.message_queue.add(pip, f"✅ Created {display_filename} successfully!", verbatim=True)
985: await self.message_queue.add(pip, "🔄 Server restart will be triggered after command completion...", verbatim=True)
988: await self.message_queue.add(pip, f"❌ Command execution failed with exit code {result.returncode}", verbatim=True)
1003: await self.message_queue.add(pip, "❌ Command execution timed out", verbatim=True)
1008: await self.message_queue.add(pip, f"❌ Error executing command: {str(e)}", verbatim=True)
1034: await pip.set_step_data(pipeline_id, step_id, execution_summary, self.steps)
1035: await self.message_queue.add(pip, self.step_messages[step_id]['complete'], verbatim=True)
1039: parsed_key = pip.parse_pipeline_key(pipeline_id)
1098: pip.display_revert_widget(
apps/540_checkboxes.py
51: pip = self.pipulate
52: pip = self.pipulate
53: self.message_queue = pip.message_queue
64: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
66: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
72: pip = self.pipulate
75: return pip.create_standard_landing_page(self)
79: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: context = pip.get_plugin_context(self)
96: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
98: pip.db['pipeline_id'] = pipeline_id
99: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
102: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
103: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
104: return pip.run_all_cells(app_name, steps)
108: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
109: pipeline_id = pip.db.get('pipeline_id', 'unknown')
111: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
114: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='checkbox-widget-unlock-button', aria_label='Unlock checkbox workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
116: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
122: await pip.finalize_workflow(pipeline_id)
123: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
124: return pip.run_all_cells(app_name, steps)
128: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
129: pipeline_id = pip.db.get('pipeline_id', 'unknown')
130: await pip.unfinalize_workflow(pipeline_id)
131: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
132: return pip.run_all_cells(app_name, steps)
136: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
144: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
150: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
153: pipeline_id = pip.db.get('pipeline_id', 'unknown')
156: await pip.clear_steps_from(pipeline_id, step_id, steps)
157: state = pip.read_state(pipeline_id)
159: pip.write_state(pipeline_id, state)
160: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
161: await self.message_queue.add(pip, message, verbatim=True)
162: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
182: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
186: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: {', '.join(selected_values)}", steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
187: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
216: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
221: pipeline_id = pip.db.get('pipeline_id', 'unknown')
apps/070_history.py
73: pip = self.pipulate
apps/620_mermaid.py
65: pip = self.pipulate
66: pip = self.pipulate
67: self.message_queue = pip.message_queue
78: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter Mermaid diagram syntax.', 'complete': 'Mermaid diagram syntax processed.'}}
84: pip = self.pipulate
87: return pip.create_standard_landing_page(self)
90: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
98: context = pip.get_plugin_context(self)
107: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
109: pip.db['pipeline_id'] = pipeline_id
110: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
113: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
114: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
115: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
119: pipeline_id = pip.db.get('pipeline_id', 'unknown')
121: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
124: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["UNLOCK_BUTTON"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["UNLOCK_BUTTON"]), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
126: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
132: await pip.finalize_workflow(pipeline_id)
133: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
134: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
138: pipeline_id = pip.db.get('pipeline_id', 'unknown')
139: await pip.unfinalize_workflow(pipeline_id)
140: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
141: return pip.run_all_cells(app_name, steps)
149: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
152: pipeline_id = pip.db.get('pipeline_id', 'unknown')
155: await pip.clear_steps_from(pipeline_id, step_id, steps)
156: state = pip.read_state(pipeline_id)
158: pip.write_state(pipeline_id, state)
159: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
160: await self.message_queue.add(pip, message, verbatim=True)
161: return pip.run_all_cells(app_name, steps)
188: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
193: pipeline_id = pip.db.get('pipeline_id', 'unknown')
194: state = pip.read_state(pipeline_id)
195: step_data = pip.get_step_data(pipeline_id, step_id, {})
197: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
207: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=mermaid_widget, steps=steps)
213: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
215: await self.message_queue.add(pip, explanation, verbatim=True)
216: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter Mermaid diagram syntax', required=True, rows=15, style='width: 100%; font-family: monospace;', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["MERMAID_TEXTAREA"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["MERMAID_INPUT"], aria_describedby="mermaid-help-text"), Div(Button('Create Diagram ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["RENDER_BUTTON"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["RENDER_BUTTON"]), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', role="form", aria_label="Configure Mermaid diagram syntax"), P("Supports flowcharts, sequence diagrams, class diagrams, and more", id="mermaid-help-text", cls='text-secondary')), Div(id=next_step_id), id=step_id)
219: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
224: pipeline_id = pip.db.get('pipeline_id', 'unknown')
227: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
230: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
231: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{user_val}')
234: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Client-side Mermaid diagram rendering', widget=mermaid_widget, steps=steps)
238: await self.message_queue.add(pip, f'{step.show} complete. Mermaid diagram rendered.', verbatim=True)
239: if pip.check_finalize_needed(step_index, steps):
240: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/720_rich.py
33: pip = self.pipulate
34: pip = self.pipulate
35: self.message_queue = pip.message_queue
46: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter JSON data for the Rich Table.', 'complete': 'JSON data processed and Rich Table rendered.'}}
52: pip = self.pipulate
55: return pip.create_standard_landing_page(self)
58: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
66: context = pip.get_plugin_context(self)
75: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
77: pip.db['pipeline_id'] = pipeline_id
78: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
81: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
82: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
83: return pip.run_all_cells(app_name, steps)
86: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: pipeline_id = pip.db.get('pipeline_id', 'unknown')
89: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
92: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
94: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
100: await pip.finalize_workflow(pipeline_id)
101: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
102: return pip.run_all_cells(app_name, steps)
105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = pip.db.get('pipeline_id', 'unknown')
107: await pip.unfinalize_workflow(pipeline_id)
108: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
109: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = pip.db.get('pipeline_id', 'unknown')
124: await pip.clear_steps_from(pipeline_id, step_id, steps)
125: state = pip.read_state(pipeline_id)
127: pip.write_state(pipeline_id, state)
128: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
129: await self.message_queue.add(pip, message, verbatim=True)
130: return pip.run_all_cells(app_name, steps)
166: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
171: pipeline_id = pip.db.get('pipeline_id', 'unknown')
172: state = pip.read_state(pipeline_id)
173: step_data = pip.get_step_data(pipeline_id, step_id, {})
175: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
188: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
193: pip.write_state(pipeline_id, state)
195: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
196: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter table data as JSON array of objects. Example is pre-populated.'), P('Format: [{"name": "value", "value1": number, ...}, {...}]', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the table', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
200: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
205: pipeline_id = pip.db.get('pipeline_id', 'unknown')
208: is_valid, error_msg, error_component = pip.validate_step_input(table_data, step.show)
219: await pip.set_step_data(pipeline_id, step_id, table_data, steps)
222: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table created with {len(data)} rows', widget=table_widget, steps=steps)
224: await self.message_queue.add(pip, f'{step.show} complete. Table created with {len(data)} rows.', verbatim=True)
apps/630_prism.py
33: pip = self.pipulate
34: pip = self.pipulate
35: self.message_queue = pip.message_queue
46: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter code content for syntax highlighting.', 'complete': 'Code content processed and highlighted.'}}
52: pip = self.pipulate
55: return pip.create_standard_landing_page(self)
58: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
66: context = pip.get_plugin_context(self)
75: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
77: pip.db['pipeline_id'] = pipeline_id
78: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
81: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
82: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
83: return pip.run_all_cells(app_name, steps)
86: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: pipeline_id = pip.db.get('pipeline_id', 'unknown')
89: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
92: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
94: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
100: await pip.finalize_workflow(pipeline_id)
101: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
102: return pip.run_all_cells(app_name, steps)
105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = pip.db.get('pipeline_id', 'unknown')
107: await pip.unfinalize_workflow(pipeline_id)
108: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
109: return pip.run_all_cells(app_name, steps)
117: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
120: pipeline_id = pip.db.get('pipeline_id', 'unknown')
123: await pip.clear_steps_from(pipeline_id, step_id, steps)
124: state = pip.read_state(pipeline_id)
126: pip.write_state(pipeline_id, state)
127: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
128: await self.message_queue.add(pip, message, verbatim=True)
129: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
176: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
203: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
209: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
211: await self.message_queue.add(pip, explanation, verbatim=True)
212: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter code for syntax highlighting', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Highlight Code ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
216: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
221: pipeline_id = pip.db.get('pipeline_id', 'unknown')
242: is_valid, error_msg, error_component = pip.validate_step_input(code_to_highlight, step.show)
245: await pip.set_step_data(pipeline_id, step_id, user_val_raw, steps)
246: pip.append_to_history(f'[WIDGET CONTENT] {step.show} ({language}):\n{code_to_highlight}')
249: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
253: await self.message_queue.add(pip, f'{step.show} complete. Code syntax highlighted with {language}.', verbatim=True)
254: if pip.check_finalize_needed(step_index, steps):
255: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/210_widget_examples.py
38: - Widgets use pip.widget_container for consistent styling and DOM structure
142:* Emphasize the `pip.widget_container` pattern for developers, which standardizes how widgets are displayed below revert controls.
160: pip = self.pipulate
161: pip = self.pipulate
162: self.message_queue = pip.message_queue
173: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please complete each step to explore different widget types.', 'step_08': {'input': f"{pip.fmt('step_08')}: Please complete New Placeholder Step.", 'complete': f'New Placeholder Step complete. Continue to next step.'}, 'step_09': {'input': f"{pip.fmt('step_09')}: Configure Rich Table Widget.", 'complete': f'Rich Table Widget complete. Continue to next step.'}, 'step_07': {'input': f"{pip.fmt('step_07')}: Enter counter data for Matplotlib Histogram.", 'complete': f'Matplotlib Histogram complete. Continue to next step.'}}
175: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Enter content for {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
181: pip, pipeline, steps, app_name = (self.pipulate, self.pipeline, self.steps, self.app_name)
182: context = pip.get_plugin_context(self)
184: full_key, prefix, user_part = pip.generate_pipeline_key(self)
189: return Container(Card(H2(title), P(self.ENDPOINT_MESSAGE, cls='text-secondary'), Form(pip.wrap_with_inline_button(Input(placeholder='Existing or new 🗝 here (Enter for auto)', name='pipeline_id', list='pipeline-ids', type='search', required=False, autofocus=True, value=default_value, _onfocus='this.setSelectionRange(this.value.length, this.value.length)', cls='contrast'), button_label=f'Enter 🔑', button_class='secondary'), pip.update_datalist('pipeline-ids', options=datalist_options if datalist_options else None), hx_post=f'/{app_name}/init', hx_target=f'#{app_name}-container')), Div(id=f'{app_name}-container'))
193: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
201: context = pip.get_plugin_context(self)
210: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
212: pip.db['pipeline_id'] = pipeline_id
214: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
219: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
220: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
222: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
223: await self.message_queue.add(pip, status_msg, verbatim=True)
225: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
226: parsed = pip.parse_pipeline_key(pipeline_id)
232: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
233: return pip.run_all_cells(app_name, steps)
237: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
238: pipeline_id = pip.db.get('pipeline_id', 'unknown')
240: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
243: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
245: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
251: await pip.finalize_workflow(pipeline_id)
252: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
253: return pip.run_all_cells(app_name, steps)
257: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
258: pipeline_id = pip.db.get('pipeline_id', 'unknown')
259: await pip.unfinalize_workflow(pipeline_id)
260: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
261: return pip.run_all_cells(app_name, steps)
265: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
271: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
274: pipeline_id = pip.db.get('pipeline_id', 'unknown')
277: await pip.clear_steps_from(pipeline_id, step_id, steps)
278: state = pip.read_state(pipeline_id)
280: pip.write_state(pipeline_id, state)
281: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
282: await self.message_queue.add(pip, message, verbatim=True)
283: return pip.run_all_cells(app_name, steps)
297: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
302: pipeline_id = pip.db.get('pipeline_id', 'unknown')
303: state = pip.read_state(pipeline_id)
304: step_data = pip.get_step_data(pipeline_id, step_id, {})
306: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
312: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=simple_widget, steps=steps)
316: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
317: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter text content for the simple widget. Example is pre-populated.'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter text content for the widget', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Record Text ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
332: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
336: pipeline_id = pip.db.get('pipeline_id', 'unknown')
340: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
343: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
344: pip.append_to_history(f'[WIDGET CONTENT] Simple Text Widget:\n{user_val}')
346: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Simple text content provided', widget=simple_widget, steps=steps)
348: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
358: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
363: pipeline_id = pip.db.get('pipeline_id', 'unknown')
364: state = pip.read_state(pipeline_id)
365: step_data = pip.get_step_data(pipeline_id, step_id, {})
367: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
382: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=marked_widget, steps=steps)
389: pip.write_state(pipeline_id, state)
391: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
392: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter markdown content to be rendered. Example is pre-populated.'), P('The markdown will be rendered with support for headings, lists, bold/italic text, and code blocks.', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter markdown content', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Render Markdown ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
401: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
405: pipeline_id = pip.db.get('pipeline_id', 'unknown')
409: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
412: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
413: pip.append_to_history(f'[WIDGET CONTENT] Markdown Widget:\n{user_val}')
416: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown rendered with Marked.js', widget=marked_widget, steps=steps)
420: await self.message_queue.add(pip, f'{step.show} complete. Markdown rendered successfully.', verbatim=True)
425: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
430: pipeline_id = pip.db.get('pipeline_id', 'unknown')
431: state = pip.read_state(pipeline_id)
432: step_data = pip.get_step_data(pipeline_id, step_id, {})
434: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
449: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=mermaid_widget, steps=steps)
456: pip.write_state(pipeline_id, state)
458: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
459: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter Mermaid diagram syntax for the widget. Example is pre-populated.'), P('Supports flowcharts, sequence diagrams, class diagrams, etc.', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter Mermaid diagram syntax', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Create Diagram ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
478: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
482: pipeline_id = pip.db.get('pipeline_id', 'unknown')
486: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
489: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
490: pip.append_to_history(f'[WIDGET CONTENT] Mermaid Diagram:\n{user_val}')
493: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Client-side Mermaid diagram rendering', widget=mermaid_widget, steps=steps)
497: await self.message_queue.add(pip, f'{step.show} complete. Mermaid diagram rendered.', verbatim=True)
508: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
513: pipeline_id = pip.db.get('pipeline_id', 'unknown')
514: state = pip.read_state(pipeline_id)
515: step_data = pip.get_step_data(pipeline_id, step_id, {})
517: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
528: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
533: pip.write_state(pipeline_id, state)
535: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
536: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter table data as JSON array of objects. Example is pre-populated.'), P('Format: [{"name": "value", "value1": number, ...}, {...}]', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the DataFrame', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Draw Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
551: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
555: pipeline_id = pip.db.get('pipeline_id', 'unknown')
558: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
569: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
570: pip.append_to_history(f'[WIDGET CONTENT] Pandas Table Data:\n{user_val}')
576: await self.message_queue.add(pip, f'{step.show} complete. Table rendered successfully.', verbatim=True)
577: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table rendered from pandas DataFrame', widget=table_container, steps=steps), Div(id=f'{steps[step_index + 1].id}', hx_get=f'/{app_name}/{steps[step_index + 1].id}', hx_trigger='load'), id=step_id)))
585: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
590: pipeline_id = pip.db.get('pipeline_id', 'unknown')
591: state = pip.read_state(pipeline_id)
592: step_data = pip.get_step_data(pipeline_id, step_id, {})
594: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
631: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
638: pip.write_state(pipeline_id, state)
640: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
641: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter code to be highlighted with syntax coloring. JavaScript example is pre-populated.'), P('The code will be displayed with syntax highlighting and a copy button.', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter code for syntax highlighting', required=True, rows=15, style='width: 100%; font-family: monospace;'), Div(Button('Highlight Code ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
645: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
649: pipeline_id = pip.db.get('pipeline_id', 'unknown')
663: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
666: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
667: pip.append_to_history(f'[WIDGET CONTENT] Code Syntax Highlighting ({language}):\n{user_val}')
670: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Syntax highlighting with Prism.js ({language})', widget=prism_widget, steps=steps)
674: await self.message_queue.add(pip, f'{step.show} complete. Code syntax highlighted with {language}.', verbatim=True)
679: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
684: pipeline_id = pip.db.get('pipeline_id', 'unknown')
685: state = pip.read_state(pipeline_id)
686: step_data = pip.get_step_data(pipeline_id, step_id, {})
688: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
705: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=js_widget, steps=steps)
712: pip.write_state(pipeline_id, state)
714: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
715: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter JavaScript code for the widget. Example is pre-populated.'), P("Use the 'widget' variable to access the container element.", cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JavaScript code', required=True, rows=12, style='width: 100%; font-family: monospace;'), Div(Button('Run JavaScript ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
719: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
723: pipeline_id = pip.db.get('pipeline_id', 'unknown')
727: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
730: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
731: pip.append_to_history(f'[WIDGET CONTENT] JavaScript Widget Code:\n{user_val}')
735: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Interactive JavaScript example', widget=js_widget, steps=steps)
739: await self.message_queue.add(pip, f'{step.show} complete. JavaScript executed.', verbatim=True)
815: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
820: pipeline_id = pip.db.get('pipeline_id', 'unknown')
821: state = pip.read_state(pipeline_id)
822: step_data = pip.get_step_data(pipeline_id, step_id, {})
824: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
835: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=histogram_widget, steps=steps)
840: pip.write_state(pipeline_id, state)
842: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
843: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter counter data as JSON object (keys and values):'), P('Format: {"category1": count1, "category2": count2, ...}', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON object for Counter data', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Histogram ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
851: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
855: pipeline_id = pip.db.get('pipeline_id', 'unknown')
859: is_valid, error_msg, error_component = pip.validate_step_input(counter_data, step.show)
871: await pip.set_step_data(pipeline_id, step_id, counter_data, steps)
874: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Histogram created from Counter data', widget=histogram_widget, steps=steps)
876: await self.message_queue.add(pip, f'{step.show} complete. Histogram created.', verbatim=True)
889: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
894: pipeline_id = pip.db.get('pipeline_id', 'unknown')
895: state = pip.read_state(pipeline_id)
896: step_data = pip.get_step_data(pipeline_id, step_id, {})
898: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
902: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {url_value}', widget=Div(P(f'URL configured: ', B(url_value)), Button('Open URL Again ▸', type='button', _onclick=f"window.open('{url_value}', '_blank')", cls='secondary')), steps=steps)
906: await self.message_queue.add(pip, 'Enter the URL you want to open:', verbatim=True)
907: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter a URL to open in your default browser.'), Form(Div(Input(type='url', name='url', placeholder='https://example.com', required=True, value=display_value, cls='contrast'), Div(Button('Open URL ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
916: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
920: pipeline_id = pip.db.get('pipeline_id', 'unknown')
928: await pip.set_step_data(pipeline_id, step_id, url, steps)
929: await self.message_queue.add(pip, f'URL set to: {url}', verbatim=True)
933: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {url}', widget=url_widget, steps=steps)
935: await self.message_queue.add(pip, f'Opening URL: {url}', verbatim=True)
993: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
998: pipeline_id = pip.db.get('pipeline_id', 'unknown')
999: state = pip.read_state(pipeline_id)
1000: step_data = pip.get_step_data(pipeline_id, step_id, {})
1002: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1015: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
1020: pip.write_state(pipeline_id, state)
1023: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1024: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter table data as JSON array of objects. Example is pre-populated.'), P('Format: [{"name": "value", "value1": number, ...}, {...}]', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the table', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
1028: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1033: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1036: is_valid, error_msg, error_component = pip.validate_step_input(table_data, step.show)
1047: await pip.set_step_data(pipeline_id, step_id, table_data, steps)
1050: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table created with {len(data)} rows', widget=table_widget, steps=steps)
1052: await self.message_queue.add(pip, f'{step.show} complete. Table created with {len(data)} rows.', verbatim=True)
1083: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1088: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1089: state = pip.read_state(pipeline_id)
1090: step_data = pip.get_step_data(pipeline_id, step_id, {})
1092: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1096: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {url_value}', widget=Div(P(f'URL configured: ', B(url_value)), Form(Input(type='hidden', name='url', value=url_value), Button('Open URL Again 🪄', type='submit', cls='secondary'), hx_post=f'/{app_name}/reopen_url', hx_target=f'#{step_id}-status'), Div(id=f'{step_id}-status')), steps=steps)
1099: await self.message_queue.add(pip, 'Enter the URL you want to open with Selenium:', verbatim=True)
1105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1110: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1117: await pip.set_step_data(pipeline_id, step_id, url, steps)
1128: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
1130: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
1133: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
1135: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
1137: await self.message_queue.add(pip, f'Opening URL with Selenium: {url}', verbatim=True)
1141: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
1143: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
1149: await self.message_queue.add(pip, error_msg, verbatim=True)
1151: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{url}')
1152: pip.append_to_history(f'[WIDGET STATE] {step.show}: Step completed')
1153: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
1154: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1165: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1170: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1171: state = pip.read_state(pipeline_id)
1172: step_data = pip.get_step_data(pipeline_id, step_id, {})
1174: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1176: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Finalized):\n{file_summary}')
1179: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Completed):\n{file_summary}')
1180: return Div(Card(H3(f'{step.show}'), P('Uploaded files:'), Pre(file_summary, style='white-space: pre-wrap; font-size: 0.9em;'), pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps)), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1182: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing upload form')
1183: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1188: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1193: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1197: await self.message_queue.add(pip, 'No files selected. Please try again.', verbatim=True)
1205: await self.message_queue.add(pip, error_msg, verbatim=True)
1223: await self.message_queue.add(pip, error_msg, verbatim=True)
1228: await pip.set_step_data(pipeline_id, step_id, file_summary, steps)
1229: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{file_summary}')
1230: pip.append_to_history(f'[WIDGET STATE] {step.show}: Files saved')
1231: await self.message_queue.add(pip, f'Successfully saved {len(uploaded_files)} files to {save_directory}', verbatim=True)
1232: return Div(Card(H3(f'{step.show}'), P('Files saved successfully:'), Pre(file_summary, style='white-space: pre-wrap; font-size: 0.9em;'), pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps)), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1236: pip, db = (self.pipulate, self.pipulate.db)
1251: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
1253: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
1256: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
1258: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
1260: await self.message_queue.add(pip, f'Reopening URL with Selenium: {url}', verbatim=True)
1264: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
1266: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
1273: await self.message_queue.add(pip, error_msg, verbatim=True)
apps/010_introduction.py
8:- Global Voice Toggle (persisted in pip.db as '1'/'0')
39: self.pip = pipulate
67: current_state = self.pip.db.get('voice_enabled', '0') == '1'
71: self.pip.db['voice_enabled'] = '1' if new_state else '0'
106: voice_enabled = self.pip.db.get('voice_enabled', '0') == '1'
apps/640_javascript.py
39: pip = self.pipulate
40: pip = self.pipulate
41: self.message_queue = pip.message_queue
52: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please complete each step to explore the JavaScript execution widget.', 'step_01': {'input': 'Please enter JavaScript code to execute.', 'complete': 'JavaScript code processed.'}}
58: pip = self.pipulate
61: return pip.create_standard_landing_page(self)
65: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
73: context = pip.get_plugin_context(self)
82: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
84: pip.db['pipeline_id'] = pipeline_id
86: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
91: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
92: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
94: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
95: await self.message_queue.add(pip, status_msg, verbatim=True)
97: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
98: parsed = pip.parse_pipeline_key(pipeline_id)
104: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
105: return pip.run_all_cells(app_name, steps)
109: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
110: pipeline_id = pip.db.get('pipeline_id', 'unknown')
112: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
115: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
117: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
123: await pip.finalize_workflow(pipeline_id)
124: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
125: return pip.run_all_cells(app_name, steps)
129: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
130: pipeline_id = pip.db.get('pipeline_id', 'unknown')
131: await pip.unfinalize_workflow(pipeline_id)
132: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
133: return pip.run_all_cells(app_name, steps)
143: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
146: pipeline_id = pip.db.get('pipeline_id', 'unknown')
149: await pip.clear_steps_from(pipeline_id, step_id, steps)
150: state = pip.read_state(pipeline_id)
152: pip.write_state(pipeline_id, state)
153: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
154: await self.message_queue.add(pip, message, verbatim=True)
155: return pip.run_all_cells(app_name, steps)
163: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
168: pipeline_id = pip.db.get('pipeline_id', 'unknown')
169: state = pip.read_state(pipeline_id)
170: step_data = pip.get_step_data(pipeline_id, step_id, {})
172: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
189: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=js_widget, steps=steps)
196: pip.write_state(pipeline_id, state)
198: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
199: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter JavaScript code for the widget. Example is pre-populated.'), P("Use the 'widget' variable to access the container element.", cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JavaScript code', required=True, rows=12, style='width: 100%; font-family: monospace;'), Div(Button('Run JavaScript ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
203: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
207: pipeline_id = pip.db.get('pipeline_id', 'unknown')
211: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
214: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
215: pip.append_to_history(f'[WIDGET CONTENT] JavaScript Widget Code:\n{user_val}')
219: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Interactive JavaScript example', widget=js_widget, steps=steps)
223: await self.message_queue.add(pip, f'{step.show} complete. JavaScript executed.', verbatim=True)
apps/810_webbrowser.py
34: pip = self.pipulate
35: pip = self.pipulate
36: self.message_queue = pip.message_queue
47: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter the URL to open.', 'complete': 'URL processed.'}}
53: pip = self.pipulate
56: return pip.create_standard_landing_page(self)
59: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
67: context = pip.get_plugin_context(self)
76: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
78: pip.db['pipeline_id'] = pipeline_id
79: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
82: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
83: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
84: return pip.run_all_cells(app_name, steps)
87: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
88: pipeline_id = pip.db.get('pipeline_id', 'unknown')
90: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
93: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
95: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
101: await pip.finalize_workflow(pipeline_id)
102: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
103: return pip.run_all_cells(app_name, steps)
106: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
107: pipeline_id = pip.db.get('pipeline_id', 'unknown')
108: await pip.unfinalize_workflow(pipeline_id)
109: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
110: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = pip.db.get('pipeline_id', 'unknown')
124: await pip.clear_steps_from(pipeline_id, step_id, steps)
125: state = pip.read_state(pipeline_id)
127: pip.write_state(pipeline_id, state)
128: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
129: await self.message_queue.add(pip, message, verbatim=True)
130: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
143: state = pip.read_state(pipeline_id)
144: step_data = pip.get_step_data(pipeline_id, step_id, {})
146: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
152: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {user_val}', widget=url_widget_display, steps=steps)
156: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
158: await self.message_queue.add(pip, explanation, verbatim=True)
159: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Input(type='url', name=step.done, placeholder='https://example.com', required=True, value=display_value, cls='contrast'), Div(Button('Open URL ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
162: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
167: pipeline_id = pip.db.get('pipeline_id', 'unknown')
174: await pip.set_step_data(pipeline_id, step_id, url_to_open, steps)
177: await self.message_queue.add(pip, f'Attempting to open URL: {url_to_open}', verbatim=True)
181: await self.message_queue.add(pip, error_msg, verbatim=True)
184: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Error Opening URL', widget=Div(error_widget, url_widget_display_on_error), steps=steps)
186: pip.append_to_history(f'[WIDGET ACTION] {step.show}: Opened URL {url_to_open}')
188: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: URL opened - {url_to_open}', widget=url_widget_display, steps=steps)
190: await self.message_queue.add(pip, f'{step.show} complete. URL opened.', verbatim=True)
191: if pip.check_finalize_needed(step_index, steps):
192: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/120_link_graph.py
326: await self.message_queue.add(pip, f'📊 Loaded new data: {len(new_df):,} rows', verbatim=True)
336: await self.message_queue.add(pip, f'⚠️ Warning: Could not load new data: {str(e)}', verbatim=True)
338: await self.message_queue.add(pip, '⚠️ No new data found, continuing without additional metrics', verbatim=True)
436: await self.message_queue.add(pip, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
437: return P('Error: Link graph data not found. Please complete Step 2 first.', style=pip.get_style('error'))
449: await self.message_queue.add(pip, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
459: await self.message_queue.add(pip, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
461: await self.message_queue.add(pip, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
473: await self.message_queue.add(pip, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
483: await self.message_queue.add(pip, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
485: await self.message_queue.add(pip, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
687: pip = self.pipulate
688: self.message_queue = pip.message_queue
691: self.ui = pip.get_ui_constants()
692: self.config = pip.get_config()
725: self.step_messages = {'finalize': {'ready': self.ui['MESSAGES']['ALL_STEPS_COMPLETE'], 'complete': f'Workflow finalized. Use {self.ui["BUTTON_LABELS"]["UNLOCK"]} to make changes.'}, 'step_02': {'input': f"❔{pip.fmt('step_02')}: Please select a crawl analysis for this project.", 'complete': '📊 Crawl analysis download complete. Continue to next step.'}}
728: self.step_messages[step.id] = {'input': f'❔{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'✳️ {step.show} complete. Continue to next step.'}
729: self.step_messages['step_04'] = {'input': f"❔{pip.fmt('step_04')}: Please check if the project has Search Console data.", 'complete': 'Search Console check complete. Continue to next step.'}
730: self.step_messages['step_03'] = {'input': f"❔{pip.fmt('step_03')}: Please check if the project has web logs available.", 'complete': '📋 Web logs check complete. Continue to next step.'}
731: self.step_messages['step_05'] = {'input': f"❔{pip.fmt('step_05')}: All data downloaded. Ready to prepare visualization.", 'complete': 'Visualization ready. Graph link generated.'}
732: self.step_messages['step_02b'] = {'input': f"❔{pip.fmt('step_02b')}: Download node attributes (page type, compliance, etc.)", 'complete': 'Node attributes download complete. Continue to next step.'}
775: pip = self.pipulate
778: return pip.create_standard_landing_page(self)
782: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
790: context = pip.get_plugin_context(self)
799: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
801: pip.db['pipeline_id'] = pipeline_id
802: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
805: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
806: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
807: return pip.run_all_cells(app_name, steps)
815: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
816: pipeline_id = pip.db.get('pipeline_id', 'unknown')
818: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
823: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
825: await self.message_queue.add(pip, 'All steps are complete. You can now finalize the workflow or revert to any step to make changes.', verbatim=True)
830: await pip.finalize_workflow(pipeline_id)
831: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
832: return pip.run_all_cells(app_name, steps)
836: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
837: pipeline_id = pip.db.get('pipeline_id', 'unknown')
838: await pip.unfinalize_workflow(pipeline_id)
839: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
840: return pip.run_all_cells(app_name, steps)
844: pip, db, steps = (self.pipulate, self.pip.db, self.steps)
852: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
858: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
861: pipeline_id = pip.db.get('pipeline_id', 'unknown')
864: await pip.clear_steps_from(pipeline_id, step_id, steps)
865: state = pip.read_state(pipeline_id)
867: pip.write_state(pipeline_id, state)
868: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
869: await self.message_queue.add(pip, message, verbatim=True)
870: await self.message_queue.add(pip, f'↩️ Reverted to {step_id}. All subsequent data has been cleared.', verbatim=True)
871: return pip.run_all_cells(app_name, steps)
879: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
884: pipeline_id = pip.db.get('pipeline_id', 'unknown')
885: state = pip.read_state(pipeline_id)
886: step_data = pip.get_step_data(pipeline_id, step_id, {})
890: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
897: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
900: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
945: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
950: pipeline_id = pip.db.get('pipeline_id', 'unknown')
957: await pip.set_step_data(pipeline_id, step_id, project_data_str, steps)
958: await self.message_queue.add(pip, f"✳️ {step.show} complete: {project_data['project_name']}", verbatim=True)
962: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
966: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
971: pipeline_id = pip.db.get('pipeline_id', 'unknown')
972: state = pip.read_state(pipeline_id)
973: step_data = pip.get_step_data(pipeline_id, step_id, {})
978: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
985: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1010: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_slug}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1064: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Select an analysis for {project_name}'), verbatim=True)
1127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1132: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1134: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1145: await self.message_queue.add(pip, f'📊 Selected analysis: {analysis_slug}. Starting crawl data download...', verbatim=True)
1168: await self.message_queue.add(pip, qualifier_config['user_message_running'], verbatim=True)
1177: await self.message_queue.add(pip, qualifier_config['user_message_found'].format(
1183: await self.message_queue.add(pip, f'Error during qualifier logic: {str(e)}', verbatim=True)
1190: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1212: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1217: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1218: state = pip.read_state(pipeline_id)
1219: step_data = pip.get_step_data(pipeline_id, step_id, {})
1225: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1235: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1269: pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {analysis_slug}', widget=widget, steps=steps),
1289: await self.message_queue.add(pip, f'📄 Ready to download node attributes for analysis {analysis_slug}', verbatim=True)
1313: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1318: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1326: await self.message_queue.add(pip, f"⏭️ Skipping Node Attributes download...", verbatim=True)
1339: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1340: await self.message_queue.add(pip, f"⏭️ Node Attributes step skipped. Proceeding to next step.", verbatim=True)
1343: pip.display_revert_widget(
1357: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1370: await self.message_queue.add(pip, f'📊 Starting node attributes download for {analysis_slug}...', verbatim=True)
1384: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1407: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1412: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1421: step_data = pip.get_step_data(pipeline_id, step_id, {})
1434: await self.message_queue.add(pip, f"✅ Using cached node attributes ({file_info['size']})", verbatim=True)
1474: await self.message_queue.add(pip, '🔄 Initiating node attributes export...', verbatim=True)
1525: await self.message_queue.add(pip, '✅ Node attributes export job created successfully!', verbatim=True)
1526: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
1530: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1544: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1560: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
1565: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
1577: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
1580: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
1591: await self.message_queue.add(pip, '🔄 Downloading node attributes...', verbatim=True)
1608: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
1619: await self.message_queue.add(pip, f'✅ Node attributes data saved with {len(df):,} rows', verbatim=True)
1635: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1648: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1653: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
1664: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1687: pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {analysis_slug}', widget=widget, steps=steps),
1694: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1699: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1700: state = pip.read_state(pipeline_id)
1701: step_data = pip.get_step_data(pipeline_id, step_id, {})
1705: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1734: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1736: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1744: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1807: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1812: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1820: await self.message_queue.add(pip, f"⏭️ Skipping Web Logs download...", verbatim=True)
1834: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1835: await self.message_queue.add(pip, f"⏭️ Web Logs step skipped. Proceeding to next step.", verbatim=True)
1838: pip.display_revert_widget(
1851: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1859: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1865: await self.message_queue.add(pip, f"📥 Downloading Web Logs for '{project_name}'...", verbatim=True)
1887: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1892: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1893: state = pip.read_state(pipeline_id)
1894: step_data = pip.get_step_data(pipeline_id, step_id, {})
1898: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1927: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1929: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1938: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1995: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2000: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2008: await self.message_queue.add(pip, f"⏭️ Skipping Search Console download...", verbatim=True)
2022: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
2023: await self.message_queue.add(pip, f"⏭️ Search Console step skipped. Proceeding to next step.", verbatim=True)
2026: pip.display_revert_widget(
2039: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
2063: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2068: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2071: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
2081: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
2097: await self.message_queue.add(pip, f'✅ Project has Search Console data, downloading...', verbatim=True)
2098: await self.process_search_console_data(pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
2100: await self.message_queue.add(pip, f'Project does not have Search Console data (skipping download)', verbatim=True)
2104: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
2126: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {completed_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
2133: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
2139: state = pip.read_state(pipeline_id)
2140: step_data = pip.get_step_data(pipeline_id, step_id, {})
2147: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
2162: pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"{step.show}: Link generated!", widget=widget, steps=steps),
2168: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
2184: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
2207: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2216: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
2217: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
2223: await self.message_queue.add(pip, '❌ Error: Could not load project or analysis data', verbatim=True)
2231: await self.message_queue.add(pip, '❌ Error: Missing required project information', verbatim=True)
2235: await self.message_queue.add(pip, '🔄 Processing data for visualization...', verbatim=True)
2247: await self.message_queue.add(pip, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
2250: await self.message_queue.add(pip, '📊 Loading link graph data...', verbatim=True)
2255: await self.message_queue.add(pip, f'✅ Loaded {len(link_graph_df):,} link graph rows', verbatim=True)
2279: await self.message_queue.add(pip, f'❌ Error: Could not identify source/target columns in link graph. Found: {columns}', verbatim=True)
2282: await self.message_queue.add(pip, f'✅ Using columns: {source_col} → {target_col}', verbatim=True)
2285: await self.message_queue.add(pip, f'❌ Error loading link graph data: {str(e)}', verbatim=True)
2288: await self.message_queue.add(pip, '🔗 Creating edge list for Cosmograph...', verbatim=True)
2298: await self.message_queue.add(pip, f'✅ Created {len(edges_df):,} edges (removed self-loops)', verbatim=True)
2304: await self.message_queue.add(pip, '🎯 Creating node list and merging performance data...', verbatim=True)
2310: await self.message_queue.add(pip, f'✅ Created {len(nodes_df):,} unique nodes', verbatim=True)
2316: await self.message_queue.add(pip, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
2326: await self.message_queue.add(pip, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
2328: await self.message_queue.add(pip, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
2335: await self.message_queue.add(pip, f'📄 Loaded crawl attributes: {len(crawl_df):,} rows', verbatim=True)
2349: await self.message_queue.add(pip, f'✅ Merged crawl attributes (HTTP status, page titles, compliance, etc.)', verbatim=True)
2352: await self.message_queue.add(pip, f'⚠️ Warning: Could not load crawl attributes: {str(e)}', verbatim=True)
2354: await self.message_queue.add(pip, '⚠️ No crawl attributes found from step_02b', verbatim=True)
2360: await self.message_queue.add(pip, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
2370: await self.message_queue.add(pip, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
2372: await self.message_queue.add(pip, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
2374: await self.message_queue.add(pip, '📋 Preparing non-destructive metadata for Cosmograph...', verbatim=True)
2406: await self.message_queue.add(pip, f'✅ Saved visualization files: {len(edges_df):,} edges, {len(cosmo_nodes_df):,} nodes', verbatim=True)
2408: await self.message_queue.add(pip, '🚀 Generating Cosmograph visualization URL...', verbatim=True)
2436: await pip.set_step_data(pipeline_id, step_id, json.dumps(visualization_result), self.steps)
2438: await self.message_queue.add(pip, f'🎉 Visualization ready! {len(edges_df):,} edges and {len(cosmo_nodes_df):,} nodes processed.', verbatim=True)
2452: pip.display_revert_widget(
2465: await self.message_queue.add(pip, f'❌ Error generating visualization: {str(e)}', verbatim=True)
2688: pip = self.pipulate
2735: await self.message_queue.add(pip, f"API error during qualifier check at {iter_param_name}={current_iter_val}: Status {response.status_code}", verbatim=True)
2753: await self.message_queue.add(pip, f"Could not extract metric from response at {iter_param_name}={current_iter_val}", verbatim=True)
2756: await self.message_queue.add(pip, f"🔍 Qualifier '{iter_param_name}' at {current_iter_val}: {metric_value:,} items.", verbatim=True)
2767: await self.message_queue.add(pip, qualifier_config['user_message_threshold_exceeded'].format(metric_value=metric_value), verbatim=True)
2774: await self.message_queue.add(pip, f"Error during qualifier check at {iter_param_name}={current_iter_val}: {str(e)}", verbatim=True)
3107: async def process_search_console_data(self, pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
3121: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})", verbatim=True)
3124: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3126: await self.message_queue.add(pip, '🔄 Initiating Search Console data export...', verbatim=True)
3155: await self.message_queue.add(pip, '✅ Export job created successfully!', verbatim=True)
3158: await self.message_queue.add(pip, f'❌ Error creating export job: {str(e)}', verbatim=True)
3160: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3164: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3166: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3169: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3171: await self.message_queue.add(pip, '🔄 Downloading Search Console data...', verbatim=True)
3198: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3204: await self.message_queue.add(pip, f'❌ Error downloading or extracting file: {str(e)}', verbatim=True)
3206: await self.message_queue.add(pip, '✅ Search Console data ready for analysis!', verbatim=True)
3208: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3213: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3214: await self.message_queue.add(pip, f'❌ Error processing Search Console data: {str(e)}', verbatim=True)
3614: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3619: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3626: step_data = pip.get_step_data(pipeline_id, step_id, {})
3643: await self.message_queue.add(pip, f"✅ Using cached crawl data ({file_info['size']})", verbatim=True)
3681: await self.message_queue.add(pip, '🔄 Initiating crawl data export...', verbatim=True)
3756: await self.message_queue.add(pip, '✅ Crawl export job created successfully!', verbatim=True)
3757: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3760: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3774: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3790: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3796: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
3809: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3812: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3824: await self.message_queue.add(pip, '🔄 Downloading crawl data...', verbatim=True)
3839: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3872: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3885: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3898: await self.message_queue.add(pip, f"✅ Crawl data downloaded: {file_info['size']}", verbatim=True)
3901: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3931: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Analysis {status_text}{download_message}', widget=widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3938: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3943: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3956: await self.message_queue.add(pip, f'{step.show} complete: Project {status_text} web logs', verbatim=True)
3961: await self.message_queue.add(pip, f"✅ Using cached web logs data ({file_info['size']})", verbatim=True)
3978: await self.message_queue.add(pip, '🔄 Initiating web logs export...', verbatim=True)
4021: await self.message_queue.add(pip, f'✅ Web logs export job created successfully! (Job ID: {job_id})', verbatim=True)
4022: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
4025: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
4040: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
4054: await self.message_queue.add(pip, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
4059: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
4065: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
4081: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
4084: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
4097: await self.message_queue.add(pip, '🔄 Downloading web logs data...', verbatim=True)
4128: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
4129: await self.message_queue.add(pip, f"✅ Web logs data downloaded: {file_info['size']}", verbatim=True)
4143: await self.message_queue.add(pip, f'❌ Error downloading file: {str(e)}', verbatim=True)
4156: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
4185: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text} web logs{download_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
4200: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
4206: pipeline_id = pip.db.get('pipeline_id', 'unknown')
4207: step_data = pip.get_step_data(pipeline_id, step_id, {})
4211: state = pip.read_state(pipeline_id)
4241: pip.write_state(pipeline_id, state)
4246: pip.write_state(pipeline_id, state)
5157: pip, db, steps, app_name = self.pipulate, self.pip.db, self.steps, self.app_name
5164: state = pip.read_state(pipeline_id)
5165: step_data = pip.get_step_data(pipeline_id, step_id, {})
5167: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
5170: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\n{current_value}")
5177: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\n{current_value}")
5179: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
5184: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
5185: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
5204: pip, db, steps, app_name = self.pipulate, self.pip.db, self.steps, self.app_name
5214: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
5216: pip.append_to_history(f"[WIDGET CONTENT] {step.show}:\n{value_to_save}")
5217: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
5219: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
5222: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
5248: pip = self.pipulate
5249: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
5250: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
apps/440_browser_automation.py
67: pip = self.pipulate
68: pip = self.pipulate
69: self.message_queue = pip.message_queue
82: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
84: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
90: pip = self.pipulate
93: return pip.create_standard_landing_page(self)
97: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
105: context = pip.get_plugin_context(self)
114: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
116: pip.db['pipeline_id'] = pipeline_id
117: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
120: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
121: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
122: return pip.run_all_cells(app_name, steps)
126: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
127: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
132: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{finalize_step.id}'), id=finalize_step.id)
134: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
140: state = pip.read_state(pipeline_id)
142: step_data = pip.get_step_data(pipeline_id, step.id, {})
147: pip.write_state(pipeline_id, state)
148: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
149: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{finalize_step.id}'), id=finalize_step.id)
153: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
154: pipeline_id = pip.db.get('pipeline_id', 'unknown')
155: state = pip.read_state(pipeline_id)
161: pip.write_state(pipeline_id, state)
162: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
167: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
175: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
181: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
184: pipeline_id = pip.db.get('pipeline_id', 'unknown')
187: await pip.clear_steps_from(pipeline_id, step_id, steps)
188: state = pip.read_state(pipeline_id)
200: pip.write_state(pipeline_id, state)
201: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
202: await self.message_queue.add(pip, message, verbatim=True)
203: return pip.run_all_cells(app_name, steps)
207: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
212: pipeline_id = pip.db.get('pipeline_id', 'unknown')
213: state = pip.read_state(pipeline_id)
214: step_data = pip.get_step_data(pipeline_id, step_id, {})
216: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
220: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Open URL: {url_value}', widget=Div(P(f'URL opened (and closed): ', B(url_value)), Div(id=f'{step_id}-status')), steps=steps)
223: await self.message_queue.add(pip, 'Enter the URL you want to open with Selenium:', verbatim=True)
229: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
234: pipeline_id = pip.db.get('pipeline_id', 'unknown')
241: await pip.set_step_data(pipeline_id, step_id, url, steps)
252: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
254: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
257: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
259: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
261: await self.message_queue.add(pip, f'Opening URL with Selenium: {url}', verbatim=True)
265: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
267: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
274: await self.message_queue.add(pip, safe_error_msg, verbatim=True)
277: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Open URL: {url}', widget=url_widget, steps=steps)
282: pip, db = (self.pipulate, self.pipulate.db)
297: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
299: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
302: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
304: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
306: await self.message_queue.add(pip, f'Reopening URL with Selenium: {url}', verbatim=True)
310: await self.message_queue.add(pip, f'Page loaded successfully. Title: {title}', verbatim=True)
312: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
319: await self.message_queue.add(pip, error_msg, verbatim=True)
324: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
329: pipeline_id = pip.db.get('pipeline_id', 'unknown')
330: state = pip.read_state(pipeline_id)
331: step_data = pip.get_step_data(pipeline_id, step_id, {})
333: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
337: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"Crawl URL: {url_value.get('url', '')}", widget=Div(P(f'URL crawled and saved: ', B(url_value.get('url', ''))), P(f"Title: {url_value.get('title', '')}"), P(f"Status: {url_value.get('status', '')}"), P(f"Saved to: {url_value.get('save_path', '')}"), P(f"Reconstructed URL: {url_value.get('reconstructed_url', '')}", cls='text-secondary'), Div(id=f'{step_id}-status')), steps=steps)
340: await self.message_queue.add(pip, 'Enter the URL you want to crawl:', verbatim=True)
353: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
358: pipeline_id = pip.db.get('pipeline_id', 'unknown')
375: await self.message_queue.add(pip, f'Current OS: {effective_os}', verbatim=True)
377: await self.message_queue.add(pip, 'Using webdriver-manager for macOS', verbatim=True)
380: await self.message_queue.add(pip, 'Using system Chrome for Linux', verbatim=True)
382: await self.message_queue.add(pip, 'Initializing Chrome driver...', verbatim=True)
384: await self.message_queue.add(pip, f'Crawling URL with Selenium: {url}', verbatim=True)
408: await self.message_queue.add(pip, 'Browser closed successfully', verbatim=True)
413: await pip.set_step_data(pipeline_id, step_id, crawl_data, steps)
414: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
416: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"Crawl URL: {crawl_data['url']}", widget=url_widget, steps=steps)
422: await self.message_queue.add(pip, safe_error_msg, verbatim=True)
514: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
519: pipeline_id = pip.db.get('pipeline_id', 'unknown')
520: state = pip.read_state(pipeline_id)
525: pip.write_state(pipeline_id, state)
526: await self.message_queue.add(pip, 'Ephemeral login test confirmed!', verbatim=True)
527: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message='Ephemeral Login Test', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
596: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
601: pipeline_id = pip.db.get('pipeline_id', 'unknown')
602: state = pip.read_state(pipeline_id)
607: pip.write_state(pipeline_id, state)
608: await self.message_queue.add(pip, 'Persistent login test confirmed!', verbatim=True)
609: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message='Persistent Login Test', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
613: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
618: pipeline_id = pip.db.get('pipeline_id', 'unknown')
619: state = pip.read_state(pipeline_id)
620: step_data = pip.get_step_data(pipeline_id, step_id, {})
622: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
624: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Finalized):\n{placeholder_value}')
627: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Completed):\n{placeholder_value}')
628: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
630: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing input form')
631: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
636: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
641: pipeline_id = pip.db.get('pipeline_id', 'unknown')
643: await pip.set_step_data(pipeline_id, step_id, placeholder_value, steps)
644: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{placeholder_value}')
645: pip.append_to_history(f'[WIDGET STATE] {step.show}: Step completed')
646: await self.message_queue.add(pip, f'{step.show} complete.', verbatim=True)
647: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/130_gap_analysis.py
37: pip = self.pipulate
38: pip = self.pipulate
39: self.message_queue = pip.get_message_queue()
42: self.ui = pip.get_ui_constants()
83: pip = self.pipulate
86: return pip.create_standard_landing_page(self)
90: pip, db, steps, app_name = (self.pipulate, self.pipulate.db, self.steps, self.APP_NAME)
98: context = pip.get_plugin_context(self)
107: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
109: pip.db['pipeline_id'] = pipeline_id
111: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
118: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["WORKFLOW"]} Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
119: await self.message_queue.add(pip, f"{self.ui["EMOJIS"]["KEY"]} Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
126: await self.message_queue.add(pip, status_msg, verbatim=True)
128: await self.message_queue.add(pip, f'{self.ui["EMOJIS"]["INPUT_FORM"]} Please complete each step in sequence. Your progress will be saved automatically.', verbatim=True)
130: parsed = pip.parse_pipeline_key(pipeline_id)
136: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
137: return pip.run_all_cells(app_name, steps)
140: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
173: all_data_steps_complete = all(pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
202: await pip.finalize_workflow(pipeline_id)
203: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
204: return pip.run_all_cells(app_name, self.steps)
207: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
208: pipeline_id = pip.db.get('pipeline_id', 'unknown')
209: await pip.unfinalize_workflow(pipeline_id)
210: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
211: return pip.run_all_cells(app_name, self.steps)
251: pip, db, current_steps = self.pipulate, self.pipulate.db, self.steps
259: prev_data = pip.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
265: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
269: pipeline_id = pip.db.get('pipeline_id', 'unknown')
274: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
275: state = pip.read_state(pipeline_id)
277: pip.write_state(pipeline_id, state)
279: message = await pip.get_state_message(pipeline_id, current_steps_to_pass_helpers, self.step_messages)
280: await self.message_queue.add(pip, message, verbatim=True)
281: return pip.run_all_cells(app_name, current_steps_to_pass_helpers)
286: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
291: pipeline_id = pip.db.get('pipeline_id', 'unknown')
292: state = pip.read_state(pipeline_id)
293: step_data = pip.get_step_data(pipeline_id, step_id, {})
295: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
298: await self.message_queue.add(pip, locked_msg, verbatim=True)
307: await self.message_queue.add(pip, completed_msg, verbatim=True)
310: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=yaml_widget, steps=steps)
321: await self.message_queue.add(pip, form_msg, verbatim=True)
322: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
324: await self.message_queue.add(pip, explanation, verbatim=True)
325: return Div(Card(H3(f'{pip.fmt(step.id)}: Enter {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder=f'Enter competitor domains, one per line...', required=True, autofocus=True, cls='textarea-standard', data_testid='text-area-widget-textarea-input', aria_label='Multi-line text input area for competitor domains', aria_required='true', aria_labelledby=f'{step_id}-form-title', aria_describedby=f'{step_id}-form-instruction'), Div(Button('Analyze Domains ▸', type='submit', cls='primary', **{'hx-on:click': 'this.setAttribute("aria-busy", "true"); this.textContent = "Analyzing domains..."'}), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step.id}_submit', hx_target=f'#{step.id}')), Div(id=next_step_id), id=step.id)
334: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
339: pipeline_id = pip.db.get('pipeline_id', 'unknown')
342: return await pip.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
358: await self.message_queue.add(pip, f'Found existing YAML with {len(existing_yaml_domains)} domains', verbatim=True)
360: await self.message_queue.add(pip, f'⚠️ YAML parsing failed: {str(e)}. Looking for raw domains...', verbatim=True)
400: existing_data = pip.get_step_data(pipeline_id, step_id, {})
409: await self.message_queue.add(pip, f'Found {len(processed_domains)} previously analyzed domains', verbatim=True)
411: await self.message_queue.add(pip, f'Could not parse existing data, starting fresh: {str(e)}', verbatim=True)
427: await self.message_queue.add(pip, f'➕ Found {len(new_raw_domains)} new domains to analyze: {", ".join(new_raw_domains)}', verbatim=True)
429: await self.message_queue.add(pip, f'📋 Total: {len(all_existing_domains)} existing + {len(new_raw_domains)} new = {len(domains)} domains', verbatim=True)
434: await self.message_queue.add(pip, f'Processing {len(domains)} domains...', verbatim=True)
441: await self.message_queue.add(pip, error_msg, verbatim=True)
442: return pip.create_error_form(error_msg, step_id, app_name)
448: await self.message_queue.add(pip, f'🤖 Starting AI browser automation analysis...', verbatim=True)
475: await self.message_queue.add(pip, f'🔍 Analyzing {domain}...', verbatim=True)
491: await self.message_queue.add(pip, f'👁️ AI Browser Vision: Capturing {scrape_url}...', verbatim=True)
509: await self.message_queue.add(pip, f'✅ AI Vision captured: {page_title[:50]}...', verbatim=True)
519: await self.message_queue.add(pip, f'🧠 AI Brain Analysis: {target_count} automation targets, {form_count} forms detected', verbatim=True)
521: await self.message_queue.add(pip, f'⚠️ AI Browser automation failed: {browser_result.get("error", "Unknown error")}', verbatim=True)
524: await self.message_queue.add(pip, f'⚠️ AI Browser automation error: {str(browser_error)[:100]}', verbatim=True)
589: await self.message_queue.add(pip, f'🎯 AI Enhanced: {domain_info["ai_browser_analysis"]["automation_intelligence"]["total_targets"]} targets, automation_ready: {domain_info["ai_browser_analysis"]["automation_intelligence"]["automation_ready"]}', verbatim=True)
609: await self.message_queue.add(pip, f" Chain: {chain_details}", verbatim=True)
614: await self.message_queue.add(pip, redirect_summary, verbatim=True)
628: await self.message_queue.add(pip, f"❌ {domain} - Connection failed", verbatim=True)
637: await self.message_queue.add(pip, f"❌ {domain} - {str(e)[:100]}", verbatim=True)
661: await pip.set_step_data(pipeline_id, step_id, yaml_output, steps)
668: await self.message_queue.add(pip, f'✅ Analysis complete: {preserved_count} existing domains preserved + {new_domains_processed} new domains analyzed = {total_count} total ({success_count} successful)', verbatim=True)
670: await self.message_queue.add(pip, f'✅ Analysis complete: {preserved_count} existing domains preserved (no new domains added)', verbatim=True)
672: await self.message_queue.add(pip, f'✅ Analysis complete: {success_count}/{total_count} domains processed successfully', verbatim=True)
674: if pip.check_finalize_needed(step_index, steps):
676: await self.message_queue.add(pip, finalize_msg, verbatim=True)
681: content_container = pip.display_revert_widget(
705: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
712: state = pip.read_state(pipeline_id)
713: step_data = pip.get_step_data(pipeline_id, step_id, {})
715: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
718: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\n{current_value}")
725: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\n{current_value}")
727: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
732: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
733: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
752: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
762: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
764: pip.append_to_history(f"[WIDGET CONTENT] {step.show}:\n{value_to_save}")
765: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
767: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
770: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
781: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
788: state = pip.read_state(pipeline_id)
789: step_data = pip.get_step_data(pipeline_id, step_id, {})
791: finalize_data = pip.get_step_data(pipeline_id, "finalize", {})
794: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Finalized):\n{current_value}")
801: pip.append_to_history(f"[WIDGET CONTENT] {step.show} (Completed):\n{current_value}")
803: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
808: pip.append_to_history(f"[WIDGET STATE] {step.show}: Showing input form")
809: await self.message_queue.add(pip, self.step_messages[step_id]["input"], verbatim=True)
828: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
838: await pip.set_step_data(pipeline_id, step_id, value_to_save, steps)
840: pip.append_to_history(f"[WIDGET CONTENT] {step.show}:\n{value_to_save}")
841: pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
843: await self.message_queue.add(pip, f"{step.show} complete.", verbatim=True)
846: pip.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: Complete", steps=steps),
apps/580_upload.py
64: pip = self.pipulate
65: pip = self.pipulate
66: self.message_queue = pip.message_queue
80: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please complete each step to explore the file upload widget.', 'step_01': {'input': 'Please select files to upload.', 'complete': 'Files uploaded successfully.'}}
86: pip = self.pipulate
89: return pip.create_standard_landing_page(self)
93: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
101: context = pip.get_plugin_context(self)
110: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
112: pip.db['pipeline_id'] = pipeline_id
114: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
119: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
120: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
122: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
123: await self.message_queue.add(pip, status_msg, verbatim=True)
125: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
126: parsed = pip.parse_pipeline_key(pipeline_id)
132: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
133: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
138: pipeline_id = pip.db.get('pipeline_id', 'unknown')
140: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
143: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
145: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
151: await pip.finalize_workflow(pipeline_id)
152: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
153: return pip.run_all_cells(app_name, steps)
157: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
158: pipeline_id = pip.db.get('pipeline_id', 'unknown')
159: await pip.unfinalize_workflow(pipeline_id)
160: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
161: return pip.run_all_cells(app_name, steps)
171: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
174: pipeline_id = pip.db.get('pipeline_id', 'unknown')
177: await pip.clear_steps_from(pipeline_id, step_id, steps)
178: state = pip.read_state(pipeline_id)
180: pip.write_state(pipeline_id, state)
181: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
182: await self.message_queue.add(pip, message, verbatim=True)
183: return pip.run_all_cells(app_name, steps)
187: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
192: pipeline_id = pip.db.get('pipeline_id', 'unknown')
193: state = pip.read_state(pipeline_id)
194: step_data = pip.get_step_data(pipeline_id, step_id, {})
196: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
236: file_summary = pip.get_step_data(pipeline_id, step_id)
304: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Files previously uploaded', widget=Div(
310: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
311: await self.message_queue.add(pip, explanation, verbatim=True)
312: return Div(Card(H3(f'{pip.fmt(step_id)}: {step.show}'), P(explanation, cls='text-secondary'), Form(Input(type='file', name='uploaded_files', multiple='true', required='true', cls='contrast', data_testid=self.UI_CONSTANTS["AUTOMATION"]["FILE_INPUT_TESTID"], aria_label='Select files to upload to the server'), Button('Upload Files ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION"]["UPLOAD_BUTTON_TESTID"], aria_label='Upload selected files to server'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', enctype='multipart/form-data')), Div(id=next_step_id), id=step_id)
316: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
321: pipeline_id = pip.db.get('pipeline_id', 'unknown')
325: await self.message_queue.add(pip, 'No files selected. Please try again.', verbatim=True)
327: return Div(Card(H3(f'{pip.fmt(step_id)}: {step.show}'), P('No files were selected. Please try again.', cls='text-invalid'), P(explanation, cls='text-secondary'), Form(Input(type='file', name='uploaded_files', multiple='true', required='true', cls='contrast', data_testid=self.UI_CONSTANTS["AUTOMATION"]["FILE_INPUT_TESTID"], aria_label='Select files to upload to the server'), Button('Upload Files ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION"]["UPLOAD_BUTTON_TESTID"], aria_label='Upload selected files to server'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', enctype='multipart/form-data')), Div(id=next_step_id), id=step_id)
336: await self.message_queue.add(pip, error_msg, verbatim=True)
369: await self.message_queue.add(pip, error_msg, verbatim=True)
377: await pip.set_step_data(pipeline_id, step_id, file_summary, steps)
378: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{file_summary}')
379: pip.append_to_history(f'[WIDGET STATE] {step.show}: Files saved')
380: await self.message_queue.add(pip, f'Successfully saved {len(actual_saved_file_details)} files to {save_directory}', verbatim=True)
440: content_container = pip.display_revert_widget(
apps/730_matplotlib.py
43: pip = self.pipulate
44: pip = self.pipulate
45: self.message_queue = pip.message_queue
56: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'new': 'Please enter JSON counter data to create a histogram visualization.', 'step_01': {'input': 'Please enter JSON counter data for the histogram.', 'complete': 'Counter data processed and histogram rendered.'}}
62: pip = self.pipulate
65: return pip.create_standard_landing_page(self)
69: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
77: context = pip.get_plugin_context(self)
86: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
88: pip.db['pipeline_id'] = pipeline_id
90: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
95: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
96: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
98: status_msg = f'Workflow is complete and finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.' if is_finalized else 'Workflow is complete but not finalized. Press Finalize to lock your data.'
99: await self.message_queue.add(pip, status_msg, verbatim=True)
101: await self.message_queue.add(pip, self.step_messages['new'], verbatim=True)
102: parsed = pip.parse_pipeline_key(pipeline_id)
108: updated_datalist = pip.update_datalist('pipeline-ids', options=matching_records)
109: return pip.run_all_cells(app_name, steps)
113: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
114: pipeline_id = pip.db.get('pipeline_id', 'unknown')
116: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
119: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
121: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
127: await pip.finalize_workflow(pipeline_id)
128: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
129: return pip.run_all_cells(app_name, steps)
133: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
134: pipeline_id = pip.db.get('pipeline_id', 'unknown')
135: await pip.unfinalize_workflow(pipeline_id)
136: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
137: return pip.run_all_cells(app_name, steps)
147: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
150: pipeline_id = pip.db.get('pipeline_id', 'unknown')
153: await pip.clear_steps_from(pipeline_id, step_id, steps)
154: state = pip.read_state(pipeline_id)
156: pip.write_state(pipeline_id, state)
157: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
158: await self.message_queue.add(pip, message, verbatim=True)
159: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
176: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
187: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=histogram_widget, steps=steps)
192: pip.write_state(pipeline_id, state)
194: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
195: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Enter counter data as JSON object (keys and values):'), P('Format: {"category1": count1, "category2": count2, ...}', cls='text-note'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON object for Counter data', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Create Histogram ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
203: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
207: pipeline_id = pip.db.get('pipeline_id', 'unknown')
211: is_valid, error_msg, error_component = pip.validate_step_input(counter_data, step.show)
222: await pip.set_step_data(pipeline_id, step_id, counter_data, steps)
225: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Histogram created from Counter data', widget=histogram_widget, steps=steps)
227: await self.message_queue.add(pip, f'{step.show} complete. Histogram created.', verbatim=True)
apps/710_pandas.py
34: pip = self.pipulate
35: pip = self.pipulate
36: self.message_queue = pip.message_queue
47: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter JSON data for the table.', 'complete': 'JSON data processed and table rendered.'}}
53: pip = self.pipulate
56: return pip.create_standard_landing_page(self)
59: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
67: context = pip.get_plugin_context(self)
76: _, temp_prefix, user_provided_id_part = pip.generate_pipeline_key(self, user_input)
78: pip.db['pipeline_id'] = pipeline_id
79: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
82: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
83: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
84: return pip.run_all_cells(app_name, steps)
87: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
88: pipeline_id = pip.db.get('pipeline_id', 'unknown')
90: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
93: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
95: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
101: await pip.finalize_workflow(pipeline_id)
102: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
103: return pip.run_all_cells(app_name, steps)
106: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
107: pipeline_id = pip.db.get('pipeline_id', 'unknown')
108: await pip.unfinalize_workflow(pipeline_id)
109: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
110: return pip.run_all_cells(app_name, steps)
118: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = pip.db.get('pipeline_id', 'unknown')
124: await pip.clear_steps_from(pipeline_id, step_id, steps)
125: state = pip.read_state(pipeline_id)
127: pip.write_state(pipeline_id, state)
128: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
129: await self.message_queue.add(pip, message, verbatim=True)
130: return pip.run_all_cells(app_name, steps)
167: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = pip.db.get('pipeline_id', 'unknown')
173: state = pip.read_state(pipeline_id)
174: step_data = pip.get_step_data(pipeline_id, step_id, {})
176: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
187: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=table_widget, steps=steps)
192: pip.write_state(pipeline_id, state)
194: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
196: await self.message_queue.add(pip, explanation, verbatim=True)
197: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter JSON array of objects for the DataFrame', required=True, rows=10, style='width: 100%; font-family: monospace;'), Div(Button('Draw Table ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
200: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
205: pipeline_id = pip.db.get('pipeline_id', 'unknown')
208: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
219: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
220: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (JSON Data):\n{user_val}')
226: await self.message_queue.add(pip, f'{step.show} complete. Table rendered successfully.', verbatim=True)
227: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Table rendered from pandas DataFrame', widget=table_container, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
apps/400_botify_trifecta.py
281: pip = self.pipulate
282: pip = self.pipulate
283: self.message_queue = pip.message_queue
286: self.ui = pip.get_ui_constants()
287: self.config = pip.get_config()
326: self.step_messages = {'finalize': {'ready': self.ui['MESSAGES']['ALL_STEPS_COMPLETE'], 'complete': f'Workflow finalized. Use {self.ui["BUTTON_LABELS"]["UNLOCK"]} to make changes.'}, 'step_analysis': {'input': f"❔{pip.fmt('step_analysis')}: Please select a crawl analysis for this project.", 'complete': '📊 Crawl analysis download complete. Continue to next step.'}}
329: self.step_messages[step.id] = {'input': f'❔{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'✳️ {step.show} complete. Continue to next step.'}
330: self.step_messages['step_gsc'] = {'input': f"❔{pip.fmt('step_gsc')}: Please check if the project has Search Console data.", 'complete': 'Search Console check complete. Continue to next step.'}
332: self.step_messages['step_crawler'] = {'input': f"❔{pip.fmt('step_crawler')}: Please download basic crawl attributes for node metadata.", 'complete': '📊 Basic crawl data download complete. Continue to next step.'}
333: self.step_messages['step_webogs'] = {'input': f"❔{pip.fmt('step_webogs')}: Please check if the project has web logs available.", 'complete': '📋 Web logs check complete. Continue to next step.'}
452: pip = self.pipulate
455: return pip.create_standard_landing_page(self)
459: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
467: context = pip.get_plugin_context(self)
476: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
478: pip.db['pipeline_id'] = pipeline_id
479: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
482: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
483: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
484: return pip.run_all_cells(app_name, steps)
492: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
493: pipeline_id = pip.db.get('pipeline_id', 'unknown')
495: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
500: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
502: await self.message_queue.add(pip, 'All steps are complete. You can now finalize the workflow or revert to any step to make changes.', verbatim=True)
507: await pip.finalize_workflow(pipeline_id)
508: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
509: return pip.run_all_cells(app_name, steps)
513: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
514: pipeline_id = pip.db.get('pipeline_id', 'unknown')
515: await pip.unfinalize_workflow(pipeline_id)
516: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
517: return pip.run_all_cells(app_name, steps)
521: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
529: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
535: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
538: pipeline_id = pip.db.get('pipeline_id', 'unknown')
541: await pip.clear_steps_from(pipeline_id, step_id, steps)
542: state = pip.read_state(pipeline_id)
544: pip.write_state(pipeline_id, state)
545: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
546: await self.message_queue.add(pip, message, verbatim=True)
547: await self.message_queue.add(pip, f'↩️ Reverted to {step_id}. All subsequent data has been cleared.', verbatim=True)
548: return pip.run_all_cells(app_name, steps)
556: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
561: pipeline_id = pip.db.get('pipeline_id', 'unknown')
562: state = pip.read_state(pipeline_id)
563: step_data = pip.get_step_data(pipeline_id, step_id, {})
567: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
574: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
577: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
628: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
633: pipeline_id = pip.db.get('pipeline_id', 'unknown')
640: await pip.set_step_data(pipeline_id, step_id, project_data_str, steps)
641: await self.message_queue.add(pip, f"✳️ {step.show} complete: {project_data['project_name']}", verbatim=True)
651: await self.message_queue.add(pip, "🔍 Fetching analyses data...", verbatim=True)
653: await self.message_queue.add(pip, save_message, verbatim=True)
668: await pip.log_api_call_details(
679: await self.message_queue.add(pip, f"⚠️ Could not save analyses data: {str(e)}", verbatim=True)
682: await self.message_queue.add(pip, "⚠️ No API token found - skipping analyses data save", verbatim=True)
686: return pip.chain_reverter(step_id, step_index, steps, app_name, project_url)
690: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
695: pipeline_id = pip.db.get('pipeline_id', 'unknown')
696: state = pip.read_state(pipeline_id)
697: step_data = pip.get_step_data(pipeline_id, step_id, {})
702: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
709: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
734: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_slug}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
782: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Select an analysis for {project_name}'), verbatim=True)
899: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
904: pipeline_id = pip.db.get('pipeline_id', 'unknown')
906: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
922: await self.message_queue.add(pip, "🔍 Fetching advanced export data...", verbatim=True)
924: await self.message_queue.add(pip, save_message, verbatim=True)
939: await pip.log_api_call_details(
950: await self.message_queue.add(pip, f"⚠️ Could not save advanced export data: {str(e)}", verbatim=True)
953: await self.message_queue.add(pip, "⚠️ No API token found - skipping advanced export data save", verbatim=True)
977: await pip.set_step_data(pipeline_id, step_id, step.done, analysis_result_str)
1005: pip.display_revert_widget(
1019: await self.message_queue.add(pip, f'📊 Selected analysis: {analysis_slug}. Starting crawl data download...', verbatim=True)
1037: await self.message_queue.add(pip, qualifier_config['user_message_running'], verbatim=True)
1046: await self.message_queue.add(pip, qualifier_config['user_message_found'].format(
1052: await self.message_queue.add(pip, f'Error during qualifier logic: {str(e)}', verbatim=True)
1059: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1081: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1086: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1087: state = pip.read_state(pipeline_id)
1088: step_data = pip.get_step_data(pipeline_id, step_id, {})
1092: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1123: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1125: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1134: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1194: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1199: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1207: await self.message_queue.add(pip, f"⏭️ Skipping basic crawl data download...", verbatim=True)
1220: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1221: await self.message_queue.add(pip, f"⏭️ Basic crawl data step skipped. Proceeding to next step.", verbatim=True)
1224: pip.display_revert_widget(
1237: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1247: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1267: await self.message_queue.add(pip, f"✅ Using cached basic crawl data ({file_info['size']})...", verbatim=True)
1324: await pip.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
1346: pip.display_revert_widget(
1359: await self.message_queue.add(pip, f"Cache check failed, proceeding with download: {str(e)}", verbatim=True)
1379: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1384: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1388: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1397: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1429: stored_step_data = pip.get_step_data(pipeline_id, step_id, {})
1450: await pip.set_step_data(pipeline_id, step_id, json.dumps(enhanced_data), steps)
1464: await pip.set_step_data(pipeline_id, step_id, json.dumps(check_result), steps)
1474: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1479: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1480: state = pip.read_state(pipeline_id)
1481: step_data = pip.get_step_data(pipeline_id, step_id, {})
1485: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1516: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1518: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1526: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1592: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1597: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1605: await self.message_queue.add(pip, f"⏭️ Skipping Web Logs download...", verbatim=True)
1619: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1620: await self.message_queue.add(pip, f"⏭️ Web Logs step skipped. Proceeding to next step.", verbatim=True)
1623: pip.display_revert_widget(
1636: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1644: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1656: await self.message_queue.add(pip, f"✅ Using cached Web Logs data ({file_info['size']})...", verbatim=True)
1674: await pip.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
1696: pip.display_revert_widget(
1707: await self.message_queue.add(pip, f"Cache check failed, proceeding with download: {str(e)}", verbatim=True)
1732: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1737: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1748: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1756: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1773: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1778: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1779: state = pip.read_state(pipeline_id)
1780: step_data = pip.get_step_data(pipeline_id, step_id, {})
1784: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1815: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1817: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1826: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1886: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1891: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1899: await self.message_queue.add(pip, f"⏭️ Skipping Search Console download...", verbatim=True)
1913: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1914: await self.message_queue.add(pip, f"⏭️ Search Console step skipped. Proceeding to next step.", verbatim=True)
1917: pip.display_revert_widget(
1930: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1940: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1960: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})...", verbatim=True)
1979: await pip.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
2001: pip.display_revert_widget(
2014: await self.message_queue.add(pip, f"Cache check failed, proceeding with download: {str(e)}", verbatim=True)
2034: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2039: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2041: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
2049: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
2061: await self.message_queue.add(pip, f'✅ Project has Search Console data, downloading...', verbatim=True)
2062: await self.process_search_console_data(pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
2064: await self.message_queue.add(pip, f'Project does not have Search Console data (skipping download)', verbatim=True)
2086: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
2109: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {completed_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3008: async def process_search_console_data(self, pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
3015: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})", verbatim=True)
3026: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3028: await self.message_queue.add(pip, '🔄 Initiating Search Console data export...', verbatim=True)
3057: await self.message_queue.add(pip, '✅ Export job created successfully!', verbatim=True)
3060: await self.message_queue.add(pip, f'❌ Error creating export job: {str(e)}', verbatim=True)
3062: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3066: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3068: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3071: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3073: await self.message_queue.add(pip, '🔄 Downloading Search Console data...', verbatim=True)
3100: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3106: await self.message_queue.add(pip, f'❌ Error downloading or extracting file: {str(e)}', verbatim=True)
3108: await self.message_queue.add(pip, '✅ Search Console data ready for analysis!', verbatim=True)
3110: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3115: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3116: await self.message_queue.add(pip, f'❌ Error processing Search Console data: {str(e)}', verbatim=True)
3539: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3545: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3552: step_data = pip.get_step_data(pipeline_id, step_id, {})
3573: await self.message_queue.add(pip, f"✅ Using cached crawl data ({file_info['size']})", verbatim=True)
3614: await self.message_queue.add(pip, '🔄 Initiating crawl data export...', verbatim=True)
3692: await self.message_queue.add(pip, '✅ Crawl export job created successfully!', verbatim=True)
3693: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3696: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3710: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3726: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3732: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
3745: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3748: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3760: await self.message_queue.add(pip, '🔄 Downloading crawl data...', verbatim=True)
3775: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3808: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3821: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3834: await self.message_queue.add(pip, f"✅ Crawl data downloaded: {file_info['size']}", verbatim=True)
3837: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3867: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Analysis {status_text}{download_message}', widget=widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3874: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3879: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3892: await self.message_queue.add(pip, f'{step.show} complete: Project {status_text} web logs', verbatim=True)
3911: await self.message_queue.add(pip, f"✅ Using cached web logs data ({file_info['size']})", verbatim=True)
3928: await self.message_queue.add(pip, '🔄 Initiating web logs export...', verbatim=True)
3971: await self.message_queue.add(pip, f'✅ Web logs export job created successfully! (Job ID: {job_id})', verbatim=True)
3972: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3975: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3990: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
4004: await self.message_queue.add(pip, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
4009: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
4015: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
4031: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
4034: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
4047: await self.message_queue.add(pip, '🔄 Downloading web logs data...', verbatim=True)
4078: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
4079: await self.message_queue.add(pip, f"✅ Web logs data downloaded: {file_info['size']}", verbatim=True)
4093: await self.message_queue.add(pip, f'❌ Error downloading file: {str(e)}', verbatim=True)
4106: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
4137: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text} web logs{download_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
4154: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
4160: pipeline_id = pip.db.get('pipeline_id', 'unknown')
4161: step_data = pip.get_step_data(pipeline_id, step_id, {})
4165: state = pip.read_state(pipeline_id)
4210: pip.write_state(pipeline_id, state)
4215: pip.write_state(pipeline_id, state)
apps/110_parameter_buster.py
224: pip = self.pipulate
225: pip = self.pipulate
226: self.message_queue = pip.message_queue
228: self.ui = pip.get_ui_constants()
229: self.config = pip.get_config()
262: self.step_messages = {'finalize': {'ready': self.ui['MESSAGES']['ALL_STEPS_COMPLETE'], 'complete': f'Workflow finalized. Use {self.ui["BUTTON_LABELS"]["UNLOCK"]} to make changes.'}, 'step_02': {'input': f"❔{pip.fmt('step_02')}: Please select a crawl analysis for this project.", 'complete': '📊 Crawl analysis download complete. Continue to next step.'}}
265: self.step_messages[step.id] = {'input': f'❔{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'✳️ {step.show} complete. Continue to next step.'}
266: self.step_messages['step_04'] = {'input': f"❔{pip.fmt('step_04')}: Please check if the project has Search Console data.", 'complete': 'Search Console check complete. Continue to next step.'}
267: self.step_messages['step_03'] = {'input': f"❔{pip.fmt('step_03')}: Please check if the project has web logs available.", 'complete': '📋 Web logs check complete. Continue to next step.'}
268: self.step_messages['step_05'] = {'input': f"❔{pip.fmt('step_05')}: Ready to count parameters from downloaded data.", 'complete': 'Parameter counting is complete.'}
269: self.step_messages['step_06'] = {'input': f"❔{pip.fmt('step_06')}: Ready to configure parameter optimization.", 'complete': 'Parameter optimization configured.'}
270: self.step_messages['step_07'] = {'input': f"❔{pip.fmt('step_07')}: Ready to generate instructions and robots.txt.", 'complete': 'Instructions generated.'}
310: pip = self.pipulate
312: return pip.create_standard_landing_page(self)
316: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
323: context = pip.get_plugin_context(self)
332: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
334: pip.db['pipeline_id'] = pipeline_id
335: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
338: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
339: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
340: return pip.run_all_cells(app_name, steps)
347: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
348: pipeline_id = pip.db.get('pipeline_id', 'unknown')
350: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
355: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
357: await self.message_queue.add(pip, 'All steps are complete. You can now finalize the workflow or revert to any step to make changes.', verbatim=True)
362: await pip.finalize_workflow(pipeline_id)
363: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
364: return pip.run_all_cells(app_name, steps)
368: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
369: pipeline_id = pip.db.get('pipeline_id', 'unknown')
370: await pip.unfinalize_workflow(pipeline_id)
371: await self.message_queue.add(pip, self.ui['MESSAGES']['WORKFLOW_UNLOCKED'], verbatim=True)
372: return pip.run_all_cells(app_name, steps)
376: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
384: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
390: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
393: pipeline_id = pip.db.get('pipeline_id', 'unknown')
396: await pip.clear_steps_from(pipeline_id, step_id, steps)
397: state = pip.read_state(pipeline_id)
399: pip.write_state(pipeline_id, state)
400: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
401: await self.message_queue.add(pip, message, verbatim=True)
402: await self.message_queue.add(pip, f'↩️ Reverted to {step_id}. All subsequent data has been cleared.', verbatim=True)
403: return pip.run_all_cells(app_name, steps)
410: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
415: pipeline_id = pip.db.get('pipeline_id', 'unknown')
416: state = pip.read_state(pipeline_id)
417: step_data = pip.get_step_data(pipeline_id, step_id, {})
421: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
428: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
431: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
475: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
480: pipeline_id = pip.db.get('pipeline_id', 'unknown')
487: await pip.set_step_data(pipeline_id, step_id, project_data_str, steps)
488: await self.message_queue.add(pip, f"✳️ {step.show} complete: {project_data['project_name']}", verbatim=True)
492: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {project_url}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
496: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
501: pipeline_id = pip.db.get('pipeline_id', 'unknown')
502: state = pip.read_state(pipeline_id)
503: step_data = pip.get_step_data(pipeline_id, step_id, {})
508: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
515: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
539: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_slug}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
579: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Select an analysis for {project_name}'), verbatim=True)
636: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
641: pipeline_id = pip.db.get('pipeline_id', 'unknown')
643: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
654: await self.message_queue.add(pip, f'📊 Selected analysis: {analysis_slug}. Starting crawl data download...', verbatim=True)
673: await self.message_queue.add(pip, qualifier_config['user_message_running'], verbatim=True)
680: await self.message_queue.add(pip, qualifier_config['user_message_found'].format(
685: await self.message_queue.add(pip, f'Error during qualifier logic: {str(e)}', verbatim=True)
691: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
713: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
718: pipeline_id = pip.db.get('pipeline_id', 'unknown')
719: state = pip.read_state(pipeline_id)
720: step_data = pip.get_step_data(pipeline_id, step_id, {})
724: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
731: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
753: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
775: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
777: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
784: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
839: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
844: pipeline_id = pip.db.get('pipeline_id', 'unknown')
850: await self.message_queue.add(pip, f"⏭️ Skipping Web Logs download...", verbatim=True)
862: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
863: await self.message_queue.add(pip, f"⏭️ Web Logs step skipped. Proceeding to next step.", verbatim=True)
865: pip.display_revert_widget(
877: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
885: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
891: await self.message_queue.add(pip, f"📥 Downloading Web Logs for '{project_name}'...", verbatim=True)
913: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
918: pipeline_id = pip.db.get('pipeline_id', 'unknown')
919: state = pip.read_state(pipeline_id)
920: step_data = pip.get_step_data(pipeline_id, step_id, {})
924: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
931: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
953: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
975: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
977: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
985: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1035: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1040: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1046: await self.message_queue.add(pip, f"⏭️ Skipping Search Console download...", verbatim=True)
1058: await pip.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1059: await self.message_queue.add(pip, f"⏭️ Search Console step skipped. Proceeding to next step.", verbatim=True)
1061: pip.display_revert_widget(
1073: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1097: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1102: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1105: prev_step_data = pip.get_step_data(pipeline_id, prev_step_id, {})
1115: analysis_step_data = pip.get_step_data(pipeline_id, analysis_step_id, {})
1141: await self.message_queue.add(pip, f'✅ Project has Search Console data, downloading...', verbatim=True)
1142: await self.process_search_console_data(pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
1144: await self.message_queue.add(pip, f'Project does not have Search Console data (skipping download)', verbatim=True)
1148: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
1175: pip.display_revert_widget(
1196: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1201: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1202: state = pip.read_state(pipeline_id)
1203: step_data = pip.get_step_data(pipeline_id, step_id, {})
1212: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
1213: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
1224: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1235: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f"{step.show}: {json.loads(optimization_result).get('total_unique_parameters', 0):,} unique parameters found", widget=visualization_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1238: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Parameter analysis complete', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1239: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1250: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1255: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1262: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1270: project_data = pip.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
1271: analysis_data = pip.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
1283: await self.message_queue.add(pip, 'Counting parameters...', verbatim=True)
1309: await pip.set_step_data(pipeline_id, step_id, summary_str, steps)
1310: await self.message_queue.add(pip, f"✓ Parameter analysis complete! Found {len(total_unique_params):,} unique parameters across {len(parameter_summary['data_sources'])} sources with {total_occurrences:,} total occurrences.", verbatim=True)
1312: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: {len(total_unique_params):,} unique parameters found', widget=visualization_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
1319: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1324: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1325: state = pip.read_state(pipeline_id)
1326: step_data = pip.get_step_data(pipeline_id, step_id, {})
1328: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1373: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Parameter Optimization with {(len(selected_params) if selected_params else 0)} parameters', widget=prism_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1379: pip.write_state(pipeline_id, state)
1380: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1428: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1431: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1489: return Div(Card(H3(f'{pip.fmt(step_id)}: {step.show}'), P('Set thresholds for parameter optimization:'), Form(Div(Div(Small('Lower GSC Threshold to lower risk (generally keep set to 0)', style='color: #888; font-style: italic;'), Div(Label(NotStr('GSC Threshold:'), For='gsc_threshold', style='min-width: 180px; color: #888;'), Input(type='range', name='gsc_threshold_slider', id='gsc_threshold_slider', value=gsc_threshold, min='0', max='100', step='1', style='width: 60%; margin: 0 10px;', _oninput="document.getElementById('gsc_threshold').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='input changed delay:300ms, load', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), Input(type='number', name='gsc_threshold', id='gsc_threshold', value=gsc_threshold, min='0', max='100', style='width: 150px;', _oninput="document.getElementById('gsc_threshold_slider').value = this.value; triggerParameterPreview();", _onchange="document.getElementById('gsc_threshold_slider').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='none', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), style='display: flex; align-items: center; gap: 5px;')), Div(Small('Higher Minimum Frequency to reduce to only the biggest offenders', style='color: #888; font-style: italic;'), Div(Label(NotStr('<strong>Minimum Frequency:</strong>'), For='min_frequency', style='min-width: 180px;'), Input(type='range', name='min_frequency_slider', id='min_frequency_slider', value=min_frequency, min='0', max=str(max_frequency), step='1', style='flex-grow: 1; margin: 0 10px;', _oninput="document.getElementById('min_frequency').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='input changed delay:300ms', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), Input(type='number', name='min_frequency', id='min_frequency', value=min_frequency, min='0', max=str(max_frequency), step='1', style='width: 150px;', _oninput="document.getElementById('min_frequency_slider').value = this.value; triggerParameterPreview();", _onchange="document.getElementById('min_frequency_slider').value = this.value; triggerParameterPreview();", hx_post=f'/{app_name}/parameter_preview', hx_trigger='none', hx_target='#parameter-preview', hx_include='#gsc_threshold, #min_frequency'), style='display: flex; align-items: center; gap: 5px;'), style='margin-bottom: 15px;'), NotStr(breakpoints_html) if breakpoints_html else None, Div(H4('Parameters That Would Be Optimized:'), Div(P('Adjust thresholds above to see which parameters would be optimized.', style='color: #888; font-style: italic;'), id='parameter-preview', style='max-height: 300px; overflow-y: auto; background: #111; border-radius: 5px; padding: 10px; margin-bottom: 15px;'), style='margin-bottom: 20px;'), Div(Button('Create Optimization ▸', type='submit', cls='primary'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}')), Div(id=next_step_id), id=step_id)
1493: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1497: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1502: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1539: pip.append_to_history(f'[OPTIMIZATION CODE] Generated PageWorkers optimization for {len(selected_params)} parameters:\n{js_code}')
1542: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
1545: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'Parameter Optimization with {(len(selected_params) if selected_params else 0)} parameters', widget=prism_widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1554: pip, db, app_name = (self.pipulate, self.pipulate.db, self.app_name)
1555: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1559: pip.append_to_history(f'[PARAMETER PREVIEW] Previewing parameters with GSC threshold={gsc_threshold} and min_frequency={min_frequency}')
1560: prev_step_data = pip.get_step_data(pipeline_id, 'step_05', {})
1636: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1641: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1642: state = pip.read_state(pipeline_id)
1643: step_data = pip.get_step_data(pipeline_id, step_id, {})
1644: step_06_data = pip.get_step_data(pipeline_id, 'step_06', {})
1672: state = pip.read_state(pipeline_id)
1678: pip.write_state(pipeline_id, state)
1680: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
1688: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown Documentation', widget=markdown_widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1693: await pip.clear_steps_from(pipeline_id, 'finalize', self.steps)
1694: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
1699: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1704: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1708: step_data = pip.get_step_data(pipeline_id, step_id, {})
1716: step_06_data = pip.get_step_data(pipeline_id, 'step_06', {})
1726: state = pip.read_state(pipeline_id)
1730: await pip.clear_steps_from(pipeline_id, step_id, self.steps)
1733: pip.write_state(pipeline_id, state)
1734: await self.message_queue.add(pip, f'{step.show}: Markdown content updated', verbatim=True)
1737: response = HTMLResponse(to_xml(Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown Documentation', widget=markdown_widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)))
1903: pip = self.pipulate
1943: await self.message_queue.add(pip, f"API error during qualifier check at {iter_param_name}={current_iter_val}: Status {response.status_code}", verbatim=True)
1957: await self.message_queue.add(pip, f"Could not extract metric from response at {iter_param_name}={current_iter_val}", verbatim=True)
1959: await self.message_queue.add(pip, f"🔍 Qualifier '{iter_param_name}' at {current_iter_val}: {metric_value:,} items.", verbatim=True)
1969: await self.message_queue.add(pip, qualifier_config['user_message_threshold_exceeded'].format(metric_value=metric_value), verbatim=True)
1975: await self.message_queue.add(pip, f"Error during qualifier check at {iter_param_name}={current_iter_val}: {str(e)}", verbatim=True)
2270: async def process_search_console_data(self, pip, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
2277: await self.message_queue.add(pip, f"✅ Using cached GSC data ({file_info['size']})", verbatim=True)
2280: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2282: await self.message_queue.add(pip, '🔄 Initiating Search Console data export...', verbatim=True)
2310: await self.message_queue.add(pip, '✅ Export job created successfully!', verbatim=True)
2313: await self.message_queue.add(pip, f'❌ Error creating export job: {str(e)}', verbatim=True)
2315: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
2319: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
2321: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
2324: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
2326: await self.message_queue.add(pip, '🔄 Downloading Search Console data...', verbatim=True)
2353: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
2359: await self.message_queue.add(pip, f'❌ Error downloading or extracting file: {str(e)}', verbatim=True)
2361: await self.message_queue.add(pip, '✅ Search Console data ready for analysis!', verbatim=True)
2363: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2368: await pip.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2369: await self.message_queue.add(pip, f'❌ Error processing Search Console data: {str(e)}', verbatim=True)
2741: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
2746: pipeline_id = pip.db.get('pipeline_id', 'unknown')
2753: step_data = pip.get_step_data(pipeline_id, step_id, {})
2768: await self.message_queue.add(pip, f"✅ Using cached crawl data ({file_info['size']})", verbatim=True)
2804: await self.message_queue.add(pip, '🔄 Initiating crawl data export...', verbatim=True)
2875: await self.message_queue.add(pip, '✅ Crawl export job created successfully!', verbatim=True)
2876: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
2879: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
2893: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
2909: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
2914: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
2926: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
2929: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
2941: await self.message_queue.add(pip, '🔄 Downloading crawl data...', verbatim=True)
2956: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
2986: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
2999: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3012: await self.message_queue.add(pip, f"✅ Crawl data downloaded: {file_info['size']}", verbatim=True)
3014: await pip.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3041: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Analysis {status_text}{download_message}', widget=widget, steps=self.steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3048: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3053: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3066: await self.message_queue.add(pip, f'{step.show} complete: Project {status_text} web logs', verbatim=True)
3071: await self.message_queue.add(pip, f"✅ Using cached web logs data ({file_info['size']})", verbatim=True)
3087: await self.message_queue.add(pip, '🔄 Initiating web logs export...', verbatim=True)
3128: await self.message_queue.add(pip, f'✅ Web logs export job created successfully! (Job ID: {job_id})', verbatim=True)
3129: await self.message_queue.add(pip, '🔄 Polling for export completion...', verbatim=True)
3132: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3147: await self.message_queue.add(pip, f'❌ {error_message}', verbatim=True)
3161: await self.message_queue.add(pip, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
3166: await self.message_queue.add(pip, f'❌ Export failed: {error_message}', verbatim=True)
3171: await self.message_queue.add(pip, f'🔍 Detailed error diagnosis: {detailed_error}', verbatim=True)
3186: await self.message_queue.add(pip, '✅ Export completed and ready for download!', verbatim=True)
3189: await self.message_queue.add(pip, '❌ No download URL found in job result', verbatim=True)
3202: await self.message_queue.add(pip, '🔄 Downloading web logs data...', verbatim=True)
3233: await self.message_queue.add(pip, f"✅ Download complete: {file_info['path']} ({file_info['size']})", verbatim=True)
3234: await self.message_queue.add(pip, f"✅ Web logs data downloaded: {file_info['size']}", verbatim=True)
3247: await self.message_queue.add(pip, f'❌ Error downloading file: {str(e)}', verbatim=True)
3260: await pip.set_step_data(pipeline_id, step_id, check_result_str, steps)
3286: return Div(pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Project {status_text} web logs{download_message}', widget=widget, steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
3293: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
3299: pipeline_id = pip.db.get('pipeline_id', 'unknown')
3302: state = pip.read_state(pipeline_id)
3306: pip.write_state(pipeline_id, state)
3309: pip.write_state(pipeline_id, state)
3317: step_data = pip.get_step_data(pipeline_id, step_id, {})
3333: state = pip.read_state(pipeline_id)
3351: pip.write_state(pipeline_id, state)
3355: pip.write_state(pipeline_id, state)
apps/610_markdown.py
58: pip = self.pipulate
59: pip = self.pipulate
60: self.message_queue = pip.message_queue
74: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}, 'step_01': {'input': 'Please enter Markdown content.', 'complete': 'Markdown content processed.'}}
82: pip = self.pipulate
85: return pip.create_standard_landing_page(self)
88: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
96: context = pip.get_plugin_context(self)
105: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
107: pip.db['pipeline_id'] = pipeline_id
108: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
111: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
112: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
113: return pip.run_all_cells(app_name, steps)
116: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
117: pipeline_id = pip.db.get('pipeline_id', 'unknown')
119: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
122: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["RENDER_BUTTON"] + "-unlock", aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["RENDER_BUTTON"]), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container', hx_swap='outerHTML'), id=finalize_step.id)
124: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
130: await pip.finalize_workflow(pipeline_id)
131: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
132: return pip.run_all_cells(app_name, steps)
135: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
136: pipeline_id = pip.db.get('pipeline_id', 'unknown')
137: await pip.unfinalize_workflow(pipeline_id)
138: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
139: return pip.run_all_cells(app_name, steps)
147: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
150: pipeline_id = pip.db.get('pipeline_id', 'unknown')
153: await pip.clear_steps_from(pipeline_id, step_id, steps)
154: state = pip.read_state(pipeline_id)
156: pip.write_state(pipeline_id, state)
157: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
158: await self.message_queue.add(pip, message, verbatim=True)
159: return pip.run_all_cells(app_name, steps)
333: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
338: pipeline_id = pip.db.get('pipeline_id', 'unknown')
339: state = pip.read_state(pipeline_id)
340: step_data = pip.get_step_data(pipeline_id, step_id, {})
342: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
352: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=marked_widget, steps=steps)
358: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
360: await self.message_queue.add(pip, explanation, verbatim=True)
361: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P(explanation, cls='text-secondary'), Form(Div(Textarea(display_value, name=step.done, placeholder='Enter markdown content', required=True, rows=15, style='width: 100%; font-family: monospace;', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["MARKDOWN_TEXTAREA"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["MARKDOWN_INPUT"], aria_describedby="markdown-help-text"), Div(Button('Render Markdown ▸', type='submit', cls='primary', data_testid=self.UI_CONSTANTS["AUTOMATION_ATTRIBUTES"]["RENDER_BUTTON"], aria_label=self.UI_CONSTANTS["ARIA_LABELS"]["RENDER_BUTTON"]), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}'), P("Supports headings, lists, bold/italic text, code blocks, and links", id="markdown-help-text", cls='text-secondary')), Div(id=next_step_id), id=step_id)
364: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
369: pipeline_id = pip.db.get('pipeline_id', 'unknown')
372: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
375: await pip.set_step_data(pipeline_id, step_id, user_val, steps)
376: pip.append_to_history(f'[WIDGET CONTENT] {step.show}:\n{user_val}')
379: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show}: Markdown rendered with Marked.js', widget=marked_widget, steps=steps)
383: await self.message_queue.add(pip, f'{step.show} complete. Markdown rendered successfully.', verbatim=True)
384: if pip.check_finalize_needed(step_index, steps):
385: await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
apps/570_switch.py
50: pip = self.pipulate
51: pip = self.pipulate
52: self.message_queue = pip.message_queue
63: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
65: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
71: pip = self.pipulate
74: return pip.create_standard_landing_page(self)
78: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: context = pip.get_plugin_context(self)
95: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
97: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
103: return pip.run_all_cells(app_name, steps)
107: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = pip.db.get('pipeline_id', 'unknown')
110: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
113: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='switch-widget-unlock-button', aria_label='Unlock switch workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
115: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await pip.finalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
123: return pip.run_all_cells(app_name, steps)
127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: await pip.unfinalize_workflow(pipeline_id)
130: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
131: return pip.run_all_cells(app_name, steps)
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: return pip.run_all_cells(app_name, steps)
153: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
158: pipeline_id = pip.db.get('pipeline_id', 'unknown')
159: state = pip.get_step_data(pipeline_id, step_id, {})
161: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing input form')
162: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
167: return Div(Card(H3(f'{pip.fmt(step_id)}: Configure {step.show}'), P('Toggle the switches to configure your settings.'), Form(Div(*switch_inputs, Div(Button('Save Settings ▸', type='submit', cls='primary', data_testid='switch-widget-next-button', aria_label='Save switch settings'), style='margin-top: 1vh; text-align: right;'), cls='w-full'), hx_post=f'/{app_name}/{step_id}_submit', hx_target=f'#{step_id}', data_testid='switch-widget-form', aria_label='Switch configuration form')), Div(id=next_step_id), id=step_id)
171: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
176: pipeline_id = pip.db.get('pipeline_id', 'unknown')
181: await pip.set_step_data(pipeline_id, step_id, switch_state, steps)
182: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('complete', f'{step.show} complete: {switch_state}'), verbatim=True)
183: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: Settings saved', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/240_simon_mcp.py
61: pip = self.pipulate
62: pip = self.pipulate
63: self.message_queue = pip.get_message_queue()
66: self.ui = pip.get_ui_constants()
108: pip, db = self.pipulate, self.pipulate.db
121: _, prefix_for_key_gen, _ = pip.generate_pipeline_key(self)
125: _, prefix, user_part = pip.generate_pipeline_key(self, user_input_key)
128: pip.db['pipeline_id'] = pipeline_id
129: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
135: return pip.run_all_cells(internal_app_name, self.steps)
138: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
139: pipeline_id = pip.db.get('pipeline_id', 'unknown')
141: finalize_data = pip.get_step_data(pipeline_id, finalize_step_obj.id, {})
142: state = pip.read_state(pipeline_id)
162: step_data = pip.get_step_data(pipeline_id, step.id, {})
188: await pip.finalize_workflow(pipeline_id)
190: return pip.run_all_cells(app_name, self.steps)
193: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
194: pipeline_id = pip.db.get('pipeline_id', 'unknown')
195: await pip.unfinalize_workflow(pipeline_id)
197: return pip.run_all_cells(app_name, self.steps)
200: pip, db, current_steps = self.pipulate, self.pipulate.db, self.steps
208: prev_data = pip.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
214: pip, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
218: pipeline_id = pip.db.get('pipeline_id', 'unknown')
223: await pip.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
224: state = pip.read_state(pipeline_id)
226: pip.write_state(pipeline_id, state)
230: return pip.run_all_cells(app_name, current_steps_to_pass_helpers)
270: pip = self.pipulate
273: await self.message_queue.add(pip, "🎪 Simon Says Make MCP Call! Select an MCP action from the dropdown and click the button to execute it directly for teaching and testing!", verbatim=True)
529: pip = self.pipulate
585: await self.message_queue.add(pip, f"User selected: Cat Fact Test", verbatim=True, role='user')
586: await self.message_queue.add(pip, f"MCP Tool Execution: get_cat_fact → Success. Fact: {fact}", verbatim=True, role='assistant')
591: await self.message_queue.add(pip, f"User selected: Cat Fact Test", verbatim=True, role='user')
592: await self.message_queue.add(pip, f"MCP Tool Execution: get_cat_fact → Failed. Error: {error_msg}", verbatim=True, role='assistant')
604: await self.message_queue.add(pip, f"User selected: Google Search Test", verbatim=True, role='user')
605: await self.message_queue.add(pip, f"MCP Tool Execution: browser_stealth_search(query='python programming tutorial') → Success. Found {len(results)} results. Files: {list(files.keys())}", verbatim=True, role='assistant')
612: await self.message_queue.add(pip, f"User selected: Google Search Test", verbatim=True, role='user')
613: await self.message_queue.add(pip, f"MCP Tool Execution: browser_stealth_search → Failed. Error: {error_msg}", verbatim=True, role='assistant')
619: await self.message_queue.add(pip, f"User selected: {tool_display}", verbatim=True, role='user')
620: await self.message_queue.add(pip, f"MCP Tool Execution: {tool_name} → HTTP Error {response.status_code}", verbatim=True, role='assistant')
642: await self.message_queue.add(pip, f"User selected: {current_mode.replace('_', ' ').title()}", verbatim=True, role='user')
643: await self.message_queue.add(pip, f"MCP Tool Execution: ui_flash_element(element_id='{config['element_id']}', message='{config['message']}', delay=500) → Success. Element flashed 10 times.", verbatim=True, role='assistant')
648: await self.message_queue.add(pip, f"User selected: {current_mode.replace('_', ' ').title()}", verbatim=True, role='user')
649: await self.message_queue.add(pip, f"MCP Tool Execution: ui_flash_element(element_id='{config['element_id']}') → Failed. Error: {error_msg}", verbatim=True, role='assistant')
654: await self.message_queue.add(pip, f"User selected: {current_mode.replace('_', ' ').title()}", verbatim=True, role='user')
655: await self.message_queue.add(pip, f"MCP Tool Execution: ui_flash_element → HTTP Error {response.status_code}", verbatim=True, role='assistant')
668: await self.message_queue.add(pip, f"User executed MCP tool (HTTP timeout occurred)", verbatim=True, role='user')
669: await self.message_queue.add(pip, f"MCP Tool Execution: HTTP timeout after 2+ minutes - tool likely completed successfully in background", verbatim=True, role='assistant')
690: await self.message_queue.add(pip, f"User attempted MCP execution", verbatim=True, role='user')
691: await self.message_queue.add(pip, f"MCP Tool Execution → Exception: {error_msg}", verbatim=True, role='assistant')
apps/050_documentation.py
83: pip = self.pipulate
apps/550_radios.py
50: pip = self.pipulate
51: pip = self.pipulate
52: self.message_queue = pip.message_queue
63: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
65: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
71: pip = self.pipulate
74: return pip.create_standard_landing_page(self)
78: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: context = pip.get_plugin_context(self)
95: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
97: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
103: return pip.run_all_cells(app_name, steps)
107: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = pip.db.get('pipeline_id', 'unknown')
110: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
113: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='radio-widget-unlock-button', aria_label='Unlock radio workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
115: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await pip.finalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
123: return pip.run_all_cells(app_name, steps)
127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: await pip.unfinalize_workflow(pipeline_id)
130: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
131: return pip.run_all_cells(app_name, steps)
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: return pip.run_all_cells(app_name, steps)
199: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
204: pipeline_id = pip.db.get('pipeline_id', 'unknown')
205: state = pip.read_state(pipeline_id)
206: step_data = pip.get_step_data(pipeline_id, step_id, {})
212: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
216: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
217: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
246: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
251: pipeline_id = pip.db.get('pipeline_id', 'unknown')
apps/520_text_area.py
45: pip = self.pipulate
46: pip = self.pipulate
47: self.message_queue = pip.message_queue
58: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
60: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please enter {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
66: pip = self.pipulate
69: return pip.create_standard_landing_page(self)
72: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
80: context = pip.get_plugin_context(self)
89: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
91: pip.db['pipeline_id'] = pipeline_id
92: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
95: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
96: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
97: return pip.run_all_cells(app_name, steps)
100: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
101: pipeline_id = pip.db.get('pipeline_id', 'unknown')
103: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
106: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='text-area-widget-unlock-button', aria_label='Unlock text area for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
108: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
114: await pip.finalize_workflow(pipeline_id)
115: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
116: return pip.run_all_cells(app_name, steps)
119: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
120: pipeline_id = pip.db.get('pipeline_id', 'unknown')
121: await pip.unfinalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
123: return pip.run_all_cells(app_name, steps)
126: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
134: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
150: await self.message_queue.add(pip, message, verbatim=True)
151: return pip.run_all_cells(app_name, steps)
156: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
161: pipeline_id = pip.db.get('pipeline_id', 'unknown')
162: state = pip.read_state(pipeline_id)
163: step_data = pip.get_step_data(pipeline_id, step_id, {})
165: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
168: await self.message_queue.add(pip, locked_msg, verbatim=True)
172: await self.message_queue.add(pip, completed_msg, verbatim=True)
174: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=text_widget, steps=steps)
182: await self.message_queue.add(pip, form_msg, verbatim=True)
183: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
185: await self.message_queue.add(pip, explanation, verbatim=True)
186: return Div(Card(H3(f'{pip.fmt(step.id)}: Enter {step.show}'), P(explanation, cls='text-secondary'), Form(pip.wrap_with_inline_button(Textarea(display_value, name=step.done, placeholder=f'Enter {step.show}', required=True, autofocus=True, cls='textarea-standard', data_testid='text-area-widget-textarea-input', aria_label='Multi-line text input area', aria_required='true', aria_labelledby=f'{step_id}-form-title', aria_describedby=f'{step_id}-form-instruction'), button_label='Next ▸'), hx_post=f'/{app_name}/{step.id}_submit', hx_target=f'#{step.id}')), Div(id=next_step_id), id=step.id)
190: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
195: pipeline_id = pip.db.get('pipeline_id', 'unknown')
197: return await pip.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
201: await self.message_queue.add(pip, submit_msg, verbatim=True)
202: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
205: await self.message_queue.add(pip, error_msg, verbatim=True)
208: await pip.set_step_data(pipeline_id, step_id, processed_val, steps)
210: await self.message_queue.add(pip, confirm_msg, verbatim=True)
211: if pip.check_finalize_needed(step_index, steps):
213: await self.message_queue.add(pip, finalize_msg, verbatim=True)
215: content_container = pip.display_revert_widget(step_id=step_id, app_name=app_name, message=f'{step.show} Configured', widget=text_widget, steps=steps)
apps/450_stream_simulator.py
31: pip = self.pipulate
apps/510_text_field.py
30: pip = self.pipulate
31: pip = self.pipulate
32: self.message_queue = pip.message_queue
43: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
45: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please enter {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
51: pip = self.pipulate
54: return pip.create_standard_landing_page(self)
57: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
65: context = pip.get_plugin_context(self)
74: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
76: pip.db['pipeline_id'] = pipeline_id
77: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
80: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
81: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
82: return pip.run_all_cells(app_name, steps)
85: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: pipeline_id = pip.db.get('pipeline_id', 'unknown')
88: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
91: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='text-field-widget-unlock-button', aria_label='Unlock workflow'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
93: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
99: await pip.finalize_workflow(pipeline_id)
100: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
101: return pip.run_all_cells(app_name, steps)
104: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
105: pipeline_id = pip.db.get('pipeline_id', 'unknown')
106: await pip.unfinalize_workflow(pipeline_id)
107: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
108: return pip.run_all_cells(app_name, steps)
111: pip, db, steps = (self.pipulate, self.pipulate.db, self.steps)
119: prev_data = pip.get_step_data(pip.db['pipeline_id'], prev_step.id, {})
124: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
127: pipeline_id = pip.db.get('pipeline_id', 'unknown')
130: await pip.clear_steps_from(pipeline_id, step_id, steps)
131: state = pip.read_state(pipeline_id)
133: pip.write_state(pipeline_id, state)
134: message = await pip.get_state_message(pipeline_id, steps, self.step_messages)
135: await self.message_queue.add(pip, message, verbatim=True)
136: return pip.run_all_cells(app_name, steps)
140: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
145: pipeline_id = pip.db.get('pipeline_id', 'unknown')
146: state = pip.read_state(pipeline_id)
147: step_data = pip.get_step_data(pipeline_id, step_id, {})
149: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
152: await self.message_queue.add(pip, locked_msg, verbatim=True)
156: await self.message_queue.add(pip, completed_msg, verbatim=True)
157: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {user_val}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'))
161: await self.message_queue.add(pip, form_msg, verbatim=True)
162: await self.message_queue.add(pip, self.step_messages[step_id]['input'], verbatim=True)
164: await self.message_queue.add(pip, explanation, verbatim=True)
165: return Div(Card(H3(f'{pip.fmt(step.id)}: Enter {step.show}'), P(explanation, cls='text-secondary'), Form(pip.wrap_with_inline_button(Input(type='text', name=step.done, value=display_value, placeholder=f'Enter {step.show}', required=True, autofocus=True, _onfocus='this.setSelectionRange(this.value.length, this.value.length)', cls='textfield-standard', aria_required='true', aria_labelledby=f'{step_id}-form-title', aria_describedby=f'{step_id}-form-instruction', data_testid='text-field-widget-text-input', aria_label='Text input field'), button_label='Next ▸', button_data_testid='text-field-widget-next-button', button_aria_label='Continue to next step'), hx_post=f'/{app_name}/{step.id}_submit', hx_target=f'#{step.id}')), Div(id=next_step_id), id=step.id)
169: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
174: pipeline_id = pip.db.get('pipeline_id', 'unknown')
176: return await pip.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
180: await self.message_queue.add(pip, submit_msg, verbatim=True)
181: is_valid, error_msg, error_component = pip.validate_step_input(user_val, step.show)
184: await self.message_queue.add(pip, error_msg, verbatim=True)
187: await pip.set_step_data(pipeline_id, step_id, processed_val, steps)
189: await self.message_queue.add(pip, confirm_msg, verbatim=True)
190: if pip.check_finalize_needed(step_index, steps):
192: await self.message_queue.add(pip, finalize_msg, verbatim=True)
193: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {processed_val}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/560_range.py
50: pip = self.pipulate
51: pip = self.pipulate
52: self.message_queue = pip.message_queue
63: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
65: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
71: pip = self.pipulate
74: return pip.create_standard_landing_page(self)
78: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: context = pip.get_plugin_context(self)
95: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
97: pip.db['pipeline_id'] = pipeline_id
98: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
103: return pip.run_all_cells(app_name, steps)
107: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = pip.db.get('pipeline_id', 'unknown')
110: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
113: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='range-widget-unlock-button', aria_label='Unlock range workflow for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
115: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await pip.finalize_workflow(pipeline_id)
122: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
123: return pip.run_all_cells(app_name, steps)
127: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = pip.db.get('pipeline_id', 'unknown')
129: await pip.unfinalize_workflow(pipeline_id)
130: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
131: return pip.run_all_cells(app_name, steps)
139: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = pip.db.get('pipeline_id', 'unknown')
145: await pip.clear_steps_from(pipeline_id, step_id, steps)
146: state = pip.read_state(pipeline_id)
148: pip.write_state(pipeline_id, state)
149: return pip.run_all_cells(app_name, steps)
154: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
159: pipeline_id = pip.db.get('pipeline_id', 'unknown')
160: state = pip.read_state(pipeline_id)
161: step_data = pip.get_step_data(pipeline_id, step_id, {})
167: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
169: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Finalized):\n{selected_value}')
172: pip.append_to_history(f'[WIDGET CONTENT] {step.show} (Completed):\n{selected_value}')
173: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
175: pip.append_to_history(f'[WIDGET STATE] {step.show}: Showing input form')
176: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
188: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
193: pipeline_id = pip.db.get('pipeline_id', 'unknown')
204: await pip.set_step_data(pipeline_id, step_id, value, steps)
205: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('complete', f'{step.show} complete: {value}'), verbatim=True)
206: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/530_dropdown.py
48: pip = self.pipulate
49: pip = self.pipulate
50: self.message_queue = pip.message_queue
61: self.step_messages = {'finalize': {'ready': 'All steps complete. Ready to finalize workflow.', 'complete': f'Workflow finalized. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.'}}
63: self.step_messages[step.id] = {'input': f'{pip.fmt(step.id)}: Please complete {step.show}.', 'complete': f'{step.show} complete. Continue to next step.'}
69: pip = self.pipulate
72: return pip.create_standard_landing_page(self)
76: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
84: context = pip.get_plugin_context(self)
93: _, prefix, user_provided_id = pip.generate_pipeline_key(self, user_input)
95: pip.db['pipeline_id'] = pipeline_id
96: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': app_name})
99: await self.message_queue.add(pip, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
100: await self.message_queue.add(pip, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
101: return pip.run_all_cells(app_name, steps)
105: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = pip.db.get('pipeline_id', 'unknown')
108: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
111: return Card(H3('Workflow is locked.'), Form(Button(pip.UNLOCK_BUTTON_LABEL, type='submit', cls='secondary outline', data_testid='dropdown-widget-unlock-button', aria_label='Unlock dropdown for editing'), hx_post=f'/{app_name}/unfinalize', hx_target=f'#{app_name}-container'), id=finalize_step.id)
113: all_steps_complete = all((pip.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
119: await pip.finalize_workflow(pipeline_id)
120: await self.message_queue.add(pip, self.step_messages['finalize']['complete'], verbatim=True)
121: return pip.run_all_cells(app_name, steps)
125: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
126: pipeline_id = pip.db.get('pipeline_id', 'unknown')
127: await pip.unfinalize_workflow(pipeline_id)
128: await self.message_queue.add(pip, 'Workflow unfinalized! You can now revert to any step and make changes.', verbatim=True)
129: return pip.run_all_cells(app_name, steps)
137: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = pip.db.get('pipeline_id', 'unknown')
143: await pip.clear_steps_from(pipeline_id, step_id, steps)
144: state = pip.read_state(pipeline_id)
146: pip.write_state(pipeline_id, state)
147: return pip.run_all_cells(app_name, steps)
197: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
202: pipeline_id = pip.db.get('pipeline_id', 'unknown')
203: state = pip.read_state(pipeline_id)
204: step_data = pip.get_step_data(pipeline_id, step_id, {})
210: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
214: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {selected_value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
215: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('input', f'Complete {step.show}'), verbatim=True)
238: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
243: pipeline_id = pip.db.get('pipeline_id', 'unknown')
248: await pip.set_step_data(pipeline_id, step_id, value, steps)
249: await self.message_queue.add(pip, self.step_messages.get(step_id, {}).get('complete', f'{step.show} complete: {value}'), verbatim=True)
250: return Div(pip.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/230_dev_assistant.py
93: pip = self.pipulate
94: pip = self.pipulate
95: self.message_queue = pip.get_message_queue()
143: pip, db = self.pipulate, self.pipulate.db
154: _, prefix_for_key_gen, _ = pip.generate_pipeline_key(self)
158: _, prefix, user_part = pip.generate_pipeline_key(self, user_input_key)
161: pip.db['pipeline_id'] = pipeline_id
162: state, error = pip.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
165: await self.message_queue.add(pip, f'Development Assistant Session: {pipeline_id}', verbatim=True, spaces_before=0)
167: return pip.run_all_cells(internal_app_name, self.steps)
170: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
171: pipeline_id = pip.db.get('pipeline_id', 'unknown')
174: await pip.set_step_data(pipeline_id, 'finalize', {'finalized': True}, self.steps)
175: await self.message_queue.add(pip, 'Development analysis session finalized.', verbatim=True)
176: return pip.run_all_cells(app_name, self.steps)
178: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {})
209: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
210: pipeline_id = pip.db.get('pipeline_id', 'unknown')
211: await pip.unfinalize_workflow(pipeline_id)
212: await self.message_queue.add(pip, 'Development analysis session unlocked for editing.', verbatim=True)
213: return pip.run_all_cells(app_name, self.steps)
216: pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
219: pipeline_id = pip.db.get('pipeline_id', 'unknown')
224: await pip.clear_steps_from(pipeline_id, step_id, self.steps)
226: state = pip.read_state(pipeline_id)
228: pip.write_state(pipeline_id, state)
230: await self.message_queue.add(pip, f'Reverted to {step_id} for re-analysis.', verbatim=True)
232: return pip.run_all_cells(app_name, self.steps)
786: f"finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
796: f" return pip.chain_reverter(step_id, step_index, steps, app_name, processed_val)\n"
813: f"return pip.chain_reverter(\n"
824: f" pip.display_revert_header(step_id, app_name, steps, f'{{step.show}}: {{value}}'),\n"
872: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
873: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
875: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
876: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
877: f" return pip.run_all_cells(app_name, self.steps)\n"
904: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
905: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
907: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
908: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
909: f" return pip.run_all_cells(app_name, self.steps)\n"
922: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
923: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
930: f" await pip.finalize_workflow(pipeline_id)\n"
931: f" return pip.run_all_cells(app_name, self.steps)\n"
948: f" pip, steps, app_name = (self.pipulate, self.steps, self.app_name)\n"
951: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
958: f" await pip.set_step_data(pipeline_id, step_id, user_input, steps)\n"
960: f" return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n"
1101: has_centralized_ui = 'pip.get_ui_constants()' in content or 'self.ui = ' in content
1137: f" self.ui = pip.get_ui_constants() # Access centralized UI constants\n"
1326: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1331: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1332: state = pip.read_state(pipeline_id)
1333: step_data = pip.get_step_data(pipeline_id, step_id, {{}})
1335: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})
1344: return pip.chain_reverter(step_id, step_index, steps, app_name, user_val)
1364: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1367: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1374: await pip.set_step_data(pipeline_id, step_id, user_input, steps)
1376: return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n""")
1395: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
1396: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
1399: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
1400: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
1401: f" return pip.run_all_cells(app_name, self.steps)\n"
1403: f" finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
apps/100_connect_with_botify.py
43: pip = self.pipulate
44: pip = self.pipulate
47: self.message_queue = pip.message_queue
78: "complete": f"✅ Botify API token saved. Use {pip.UNLOCK_BUTTON_LABEL} to change token."
85: "input": f"{pip.fmt(step.id)}: Please enter {step.show}.",
125: pip, pipeline, steps, app_name = self.pipulate, self.pipeline, self.steps, self.app_name
151: pip.wrap_with_inline_button(
202: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
208: state, error = pip.initialize_if_missing(pipeline_id, {"app_name": app_name})
212: state = pip.read_state(pipeline_id)
216: pip.write_state(pipeline_id, state)
225: f"🟢 Botify API token validated for user: {pip.fmt(username)}. Ready to finalize.",
235: return pip.run_all_cells(app_name, steps)
259: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
262: finalize_data = pip.get_step_data(pipeline_id, finalize_step.id, {})
272: P(f"Botify API token is saved. Use {pip.UNLOCK_BUTTON_LABEL} to make changes.",
276: pip.UNLOCK_BUTTON_LABEL,
283: title=f"Click to {pip.UNLOCK_BUTTON_LABEL.lower()} and modify token"
333: state = pip.read_state(pipeline_id)
337: pip.write_state(pipeline_id, state)
340: return pip.run_all_cells(app_name, steps)
343: state = pip.read_state(pipeline_id)
346: pip.write_state(pipeline_id, state)
354: await self.safe_stream(f"✅ Botify API token saved to botify_token.txt for user: {pip.fmt(username)}", verbatim=True, spaces_after=1)
366: return pip.run_all_cells(app_name, steps)
403: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
407: state = pip.read_state(pipeline_id)
410: pip.write_state(pipeline_id, state)
427: return pip.run_all_cells(app_name, steps)
430: pip, db, steps = self.pipulate, self.pipulate.db, self.steps
440: prev_data = pip.get_step_data(db["pipeline_id"], prev_step_id, {})
445: pip, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
451: await pip.clear_steps_from(pipeline_id, step_id, steps)
452: state = pip.read_state(pipeline_id)
454: pip.write_state(pipeline_id, state)
456: return pip.run_all_cells(app_name, steps)
476: pip = self.pipulate
482: pip,
493: pip,
508: pip,
2463 matches
2447 matched lines
33 files contained matches
39 files searched
246745 bytes printed
1891374 bytes searched
0.006756 seconds spent searching
0.036558 seconds total
(nix)
[mike@nixos:~/repos/pipulate]$
And:
(nix)
[mike@nixos:~/repos/pipulate]$ rg '\b(self\.)?pip\b' apps/ -t py -c
apps/550_radios.py:43
apps/820_selenium.py:62
apps/710_pandas.py:52
apps/620_mermaid.py:53
apps/450_stream_simulator.py:1
apps/640_javascript.py:56
apps/610_markdown.py:53
apps/520_text_area.py:61
apps/540_checkboxes.py:47
apps/010_introduction.py:5
apps/440_browser_automation.py:116
apps/730_matplotlib.py:55
apps/015_backup_introduction.py:1
apps/230_dev_assistant.py:68
apps/720_rich.py:50
apps/580_upload.py:61
apps/300_blank_placeholder.py:56
apps/070_history.py:1
apps/100_connect_with_botify.py:40
apps/510_text_field.py:61
apps/120_link_graph.py:289
apps/130_gap_analysis.py:114
apps/200_workflow_genesis.py:87
apps/810_webbrowser.py:55
apps/530_dropdown.py:46
apps/240_simon_mcp.py:52
apps/630_prism.py:53
apps/560_range.py:49
apps/400_botify_trifecta.py:227
apps/570_switch.py:45
apps/050_documentation.py:1
apps/210_widget_examples.py:236
apps/110_parameter_buster.py:251
(nix)
[mike@nixos:~/repos/pipulate]$
That’s the “before”. Does that help in writing the Python script?
Gemini 3 Pro: This is incredibly helpful context. The rg output proves exactly why doing a blind Find & Replace across 2,463 matches is a terrible idea—it’s highly likely to introduce bugs.
However, using the pure Python ast module has a major drawback: when you use ast.unparse() to write the tree back to a file, it destroys your custom formatting, spacing, and comments.
Instead, we are going to use Python’s built-in tokenize module. It operates at the lexical level (the step right before the AST). This allows us to ask Python to read the file, identify ONLY the tokens that represent variable/attribute names (NAME tokens) equaling exactly "pip", get their exact line and column coordinates, and surgically replace them in the raw text.
This method is 100% non-destructive to your formatting, and it completely ignores strings, docstrings, and comments!
Tokenizing for Precision: The Non-Destructive Approach
Here is the exact script to perform this surgical refactor.
1. Create the Refactoring Script
Save this file as scripts/rename_pip_to_wand.py:
#!/usr/bin/env python3
"""
scripts/rename_pip_to_wand.py
Surgically renames the variable/attribute `pip` to `wand` across python files.
Uses the Python standard library `tokenize` module to safely ignore strings and comments.
"""
import os
import tokenize
from pathlib import Path
def process_file(filepath):
# Read original source to preserve exact formatting
with open(filepath, 'r', encoding='utf-8') as f:
source = f.read()
# Generate tokens from bytes
with open(filepath, 'rb') as f:
try:
tokens = list(tokenize.tokenize(f.readline))
except tokenize.TokenError as e:
print(f"⚠️ Could not tokenize {filepath.name}: {e}")
return False
lines = source.splitlines(keepends=True)
changed = False
# Collect replacements per line
# Format: line_index -> list of (start_col, end_col, new_text)
replacements = {}
for tok in tokens:
# We only care about programmatic names (variables, attributes, functions)
# This naturally ignores comments, strings, and docstrings.
if tok.type == tokenize.NAME and tok.string == 'pip':
line_idx = tok.start[0] - 1 # tokens are 1-indexed, lists are 0-indexed
start_col = tok.start[1]
end_col = tok.end[1]
if line_idx not in replacements:
replacements[line_idx] = []
replacements[line_idx].append((start_col, end_col, 'wand'))
changed = True
if not changed:
return False
# Apply replacements from right-to-left (reverse column order)
# so we don't mess up the offset coordinates for multiple replacements on one line
for line_idx, reps in replacements.items():
reps.sort(key=lambda x: x[0], reverse=True)
line = lines[line_idx]
for start_col, end_col, new_text in reps:
line = line[:start_col] + new_text + line[end_col:]
lines[line_idx] = line
# Write back to file
with open(filepath, 'w', encoding='utf-8') as f:
f.write("".join(lines))
return True
def main():
apps_dir = Path('apps')
if not apps_dir.exists() or not apps_dir.is_dir():
print("❌ Error: apps/ directory not found. Run this from the project root.")
return
changed_count = 0
file_count = 0
print("🪄 Splicing 'pip' into 'wand'...\n")
for filepath in apps_dir.glob('*.py'):
if filepath.name.startswith('__'):
continue
if process_file(filepath):
print(f"✅ Updated: {filepath.name}")
changed_count += 1
file_count += 1
print(f"\n🎯 Done! Processed {file_count} files, updated {changed_count} files.")
if __name__ == '__main__':
main()
2. Execute the Script
From your project root (the ~/repos/pipulate directory), run the script:
python scripts/rename_pip_to_wand.py
3. Verification (“The After”)
Once the script finishes, you can use the exact same rg commands to check your work.
First, verify the pip variables are gone:
This command should return mostly (if not entirely) empty, aside from maybe a few odd edge cases or instances you intentionally skipped:
rg '\b(self\.)?pip\b' apps/ -t py --stats
Second, check your new wand variable count:
This should roughly match your previous pip count (give or take the manual edits you already did in 040_hello_workflow.py):
rg '\b(self\.)?wand\b' apps/ -t py --stats
Third, ensure strings/comments weren’t mangled:
To be absolutely certain we didn’t touch string comments, run a search for pip without the strict variable formatting. You should still see things like pipulate, pipeline, and english prose containing the word pip:
rg -i 'pip' apps/ -t py
If anything looks incorrect, simply git restore apps/ and let me know—but the coordinate-based token approach is mathematically precise and should nail this perfectly!
MikeLev.in: Okay, and we run the script:
(nix)
[mike@nixos:~/repos/pipulate]$ git status
On branch main
Your branch is up to date with 'origin/main'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
scripts/rename_pip_to_wand.py
nothing added to commit but untracked files present (use "git add" to track)
(nix)
[mike@nixos:~/repos/pipulate]$ git add scripts/rename_pip_to_wand.py
(nix)
[mike@nixos:~/repos/pipulate]$ git commit -am "About to rename pip to wand"
[main a2b3bbed] About to rename pip to wand
1 file changed, 85 insertions(+)
create mode 100644 scripts/rename_pip_to_wand.py
(nix)
[mike@nixos:~/repos/pipulate]$ git push
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 48 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 1.54 KiB | 1.54 MiB/s, done.
Total 4 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
remote: This repository moved. Please use the new location:
remote: git@github.com:pipulate/pipulate.git
To github.com:miklevin/pipulate
8c7494ba..a2b3bbed main -> main
(nix)
[mike@nixos:~/repos/pipulate]$ python scripts/rename_pip_to_wand.py
🪄 Splicing 'pip' into 'wand'...
✅ Updated: 100_connect_with_botify.py
✅ Updated: 580_upload.py
✅ Updated: 720_rich.py
✅ Updated: 730_matplotlib.py
✅ Updated: 010_introduction.py
✅ Updated: 520_text_area.py
✅ Updated: 510_text_field.py
✅ Updated: 400_botify_trifecta.py
✅ Updated: 240_simon_mcp.py
✅ Updated: 810_webbrowser.py
✅ Updated: 050_documentation.py
✅ Updated: 120_link_graph.py
✅ Updated: 450_stream_simulator.py
✅ Updated: 710_pandas.py
✅ Updated: 550_radios.py
✅ Updated: 570_switch.py
✅ Updated: 560_range.py
✅ Updated: 530_dropdown.py
✅ Updated: 110_parameter_buster.py
✅ Updated: 630_prism.py
✅ Updated: 200_workflow_genesis.py
✅ Updated: 300_blank_placeholder.py
✅ Updated: 230_dev_assistant.py
✅ Updated: 610_markdown.py
✅ Updated: 210_widget_examples.py
✅ Updated: 070_history.py
✅ Updated: 015_backup_introduction.py
✅ Updated: 130_gap_analysis.py
✅ Updated: 440_browser_automation.py
✅ Updated: 540_checkboxes.py
✅ Updated: 640_javascript.py
✅ Updated: 620_mermaid.py
✅ Updated: 820_selenium.py
🎯 Done! Processed 39 files, updated 33 files.
(nix)
[mike@nixos:~/repos/pipulate]$
Okay, and we check how we did!
(nix)
[mike@nixos:~/repos/pipulate]$ rg '\b(self\.)?pip\b' apps/ -t py --stats
apps/230_dev_assistant.py
786: f"finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
796: f" return pip.chain_reverter(step_id, step_index, steps, app_name, processed_val)\n"
813: f"return pip.chain_reverter(\n"
824: f" pip.display_revert_header(step_id, app_name, steps, f'{{step.show}}: {{value}}'),\n"
872: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
873: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
875: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
876: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
877: f" return pip.run_all_cells(app_name, self.steps)\n"
904: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
905: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
907: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
908: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
909: f" return pip.run_all_cells(app_name, self.steps)\n"
922: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
923: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
930: f" await pip.finalize_workflow(pipeline_id)\n"
931: f" return pip.run_all_cells(app_name, self.steps)\n"
948: f" pip, steps, app_name = (self.pipulate, self.steps, self.app_name)\n"
951: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
958: f" await pip.set_step_data(pipeline_id, step_id, user_input, steps)\n"
960: f" return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n"
1101: has_centralized_ui = 'pip.get_ui_constants()' in content or 'self.ui = ' in content
1137: f" self.ui = pip.get_ui_constants() # Access centralized UI constants\n"
1326: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1331: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1332: state = pip.read_state(pipeline_id)
1333: step_data = pip.get_step_data(pipeline_id, step_id, {{}})
1335: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})
1344: return pip.chain_reverter(step_id, step_index, steps, app_name, user_val)
1364: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1367: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1374: await pip.set_step_data(pipeline_id, step_id, user_input, steps)
1376: return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n""")
1395: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
1396: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
1399: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
1400: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
1401: f" return pip.run_all_cells(app_name, self.steps)\n"
1403: f" finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
apps/010_introduction.py
8:- Global Voice Toggle (persisted in pip.db as '1'/'0')
apps/120_link_graph.py
326: await self.message_queue.add(pip, f'📊 Loaded new data: {len(new_df):,} rows', verbatim=True)
336: await self.message_queue.add(pip, f'⚠️ Warning: Could not load new data: {str(e)}', verbatim=True)
338: await self.message_queue.add(pip, '⚠️ No new data found, continuing without additional metrics', verbatim=True)
436: await self.message_queue.add(pip, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
437: return P('Error: Link graph data not found. Please complete Step 2 first.', style=pip.get_style('error'))
449: await self.message_queue.add(pip, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
459: await self.message_queue.add(pip, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
461: await self.message_queue.add(pip, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
473: await self.message_queue.add(pip, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
483: await self.message_queue.add(pip, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
485: await self.message_queue.add(pip, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
apps/210_widget_examples.py
38: - Widgets use pip.widget_container for consistent styling and DOM structure
142:* Emphasize the `pip.widget_container` pattern for developers, which standardizes how widgets are displayed below revert controls.
54 matches
54 matched lines
4 files contained matches
39 files searched
5372 bytes printed
1893783 bytes searched
0.003050 seconds spent searching
0.008057 seconds total
(nix)
[mike@nixos:~/repos/pipulate]$
And we check that we didn’t mangle pipulate:
(nix)
[mike@nixos:~/repos/pipulate]$ rg -i 'pip' apps/ -t py
apps/020_profiles.py
57: def __init__(self, main_plugin_instance, table, pipulate_instance):
59: self.pipulate_instance = pipulate_instance
60: super().__init__(name=main_plugin_instance.name, table=table, toggle_field='active', sort_field='priority', pipulate_instance=self.pipulate_instance)
93: def __init__(self, app, pipulate_instance, pipeline_table, db_key_value_store, profiles_table_from_server):
95: self.pipulate = pipulate_instance
108: self.crud_handler = ProfileCrudOperations(main_plugin_instance=self, table=self.table, pipulate_instance=self.pipulate)
apps/820_selenium.py
19:'\nPipulate Selenium URL Opener Widget Workflow\nA workflow for demonstrating opening a URL in a Selenium-controlled Chrome browser.\n'
33: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
34: self.pipulate = pipulate
38: self.pipulate = pipulate
39: self.pipeline = pipeline
41: wand = self.pipulate
42: wand = self.pipulate
60: wand = self.pipulate
66: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
68: user_input = form.get('pipeline_id', '').strip()
81: pipeline_id = user_input
83: _, temp_prefix, user_provided_id_part = wand.generate_pipeline_key(self, user_input)
84: pipeline_id = f'{expected_prefix}{user_provided_id_part}'
85: wand.db['pipeline_id'] = pipeline_id
86: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
89: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
90: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
94: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
95: pipeline_id = wand.db.get('pipeline_id', 'unknown')
97: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
102: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
108: await wand.finalize_workflow(pipeline_id)
113: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
114: pipeline_id = wand.db.get('pipeline_id', 'unknown')
115: await wand.unfinalize_workflow(pipeline_id)
125: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = wand.db.get('pipeline_id', 'unknown')
131: await wand.clear_steps_from(pipeline_id, step_id, steps)
132: state = wand.read_state(pipeline_id)
134: wand.write_state(pipeline_id, state)
135: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
145: wand = self.pipulate
183: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
188: pipeline_id = wand.db.get('pipeline_id', 'unknown')
189: state = wand.read_state(pipeline_id)
190: step_data = wand.get_step_data(pipeline_id, step_id, {})
192: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
208: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
213: pipeline_id = wand.db.get('pipeline_id', 'unknown')
220: await wand.set_step_data(pipeline_id, step_id, url_to_open, steps)
235: wand = self.pipulate
apps/220_roadmap.py
20: title Pipulate Development Roadmap
51: def __init__(self, app, pipulate, pipeline, db):
53: self.pipulate = pipulate
62: if self.pipulate is not None and not self._has_streamed:
65: await self.pipulate.stream(
75: self.pipulate.append_to_history( # Remove await since it's not async
157: P("Planned development timeline for Pipulate features:"),
apps/040_hello_workflow.py
10:Pipulate Workflow Template (Hello World Example)
12:This file serves as a starting point for creating linear, step-by-step Pipulate Workflows.
30:Pipulate workflows use HTMX's chain reaction pattern to create a "Run All Cells" experience
100:The Pipulate framework provides helper methods for common tasks:
138: This example shows the core patterns and best practices for Pipulate workflows.
153:You are an AI assistant helping users understand and create Pipulate workflows. When users interact with the Hello World workflow, your job is to:
155:1. Explain how Pipulate workflows transform WET code from Jupyter Notebooks into web applications
166:Pipulate transforms WET code from Jupyter Notebooks into web applications by:
189:Let's compare how a simple Jupyter Notebook gets transformed into a Pipulate workflow:
200:### Pipulate Workflow Implementation
202:This is how the same functionality is implemented as a Pipulate workflow:
244:There are two types of apps in Pipulate:
265: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
266: self.pipulate = pipulate
277: self.wand = pipulate
281: self.wand = pipulate
284: self.pipeline = pipeline
315: pipulate.register_workflow_routes(self)
342: user_input = form.get('pipeline_id', '').strip()
355: pipeline_id = user_input
357: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
358: pipeline_id = f'{prefix}{user_provided_id}'
359: wand.db['pipeline_id'] = pipeline_id
360: logger.debug(f'Using pipeline ID: {pipeline_id}')
361: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
368: await self.message_queue.add(wand, f'{self.ui["EMOJIS"]["WORKFLOW"]} Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
369: await self.message_queue.add(wand, f"{self.ui["EMOJIS"]["KEY"]} Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
380: parsed = wand.parse_pipeline_key(pipeline_id)
382: self.pipeline.xtra(app_name=app_name)
383: matching_records = [record.pkey for record in self.pipeline() if record.pkey.startswith(prefix)]
384: if pipeline_id not in matching_records:
385: matching_records.append(pipeline_id)
386: updated_datalist = wand.update_datalist('pipeline-ids', options=matching_records)
392: pipeline_id = wand.db.get('pipeline_id', 'unknown')
394: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
423: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
456: await wand.finalize_workflow(pipeline_id)
463: pipeline_id = wand.db.get('pipeline_id', 'unknown')
464: await wand.unfinalize_workflow(pipeline_id)
478: prev_data = wand.get_step_data(wand.db.get('pipeline_id', 'unknown'), prev_step.id, {})
487: pipeline_id = wand.db.get('pipeline_id', 'unknown')
491: await wand.clear_steps_from(pipeline_id, step_id, steps)
492: state = wand.read_state(pipeline_id)
494: wand.write_state(pipeline_id, state)
495: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
514: pipeline_id = wand.db.get('pipeline_id', 'unknown')
515: state = wand.read_state(pipeline_id)
516: step_data = wand.get_step_data(pipeline_id, step_id, {})
518: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
633: pipeline_id = self.wand.db["pipeline_id"]
644: await self.wand.set_step_data(pipeline_id, step_id, user_val, self.steps)
665: pipeline_id = wand.db.get('pipeline_id', 'unknown')
666: state = wand.read_state(pipeline_id)
667: step_data = wand.get_step_data(pipeline_id, step_id, {})
669: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
773: pipeline_id = wand.db.get('pipeline_id', 'unknown')
776: return await wand.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
789: await wand.set_step_data(pipeline_id, step_id, processed_val, steps)
apps/520_text_area.py
38: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
39: self.pipulate = pipulate
42: self.pipulate = pipulate
43: self.pipeline = pipeline
45: wand = self.pipulate
46: wand = self.pipulate
66: wand = self.pipulate
72: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
74: user_input = form.get('pipeline_id', '').strip()
87: pipeline_id = user_input
89: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
90: pipeline_id = f'{prefix}{user_provided_id}'
91: wand.db['pipeline_id'] = pipeline_id
92: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
95: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
96: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
100: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
101: pipeline_id = wand.db.get('pipeline_id', 'unknown')
103: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
108: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
114: await wand.finalize_workflow(pipeline_id)
119: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
120: pipeline_id = wand.db.get('pipeline_id', 'unknown')
121: await wand.unfinalize_workflow(pipeline_id)
126: wand, db, steps = (self.pipulate, self.pipulate.db, self.steps)
134: prev_data = wand.get_step_data(wand.db['pipeline_id'], prev_step.id, {})
139: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = wand.db.get('pipeline_id', 'unknown')
145: await wand.clear_steps_from(pipeline_id, step_id, steps)
146: state = wand.read_state(pipeline_id)
148: wand.write_state(pipeline_id, state)
149: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
156: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
161: pipeline_id = wand.db.get('pipeline_id', 'unknown')
162: state = wand.read_state(pipeline_id)
163: step_data = wand.get_step_data(pipeline_id, step_id, {})
165: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
190: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
195: pipeline_id = wand.db.get('pipeline_id', 'unknown')
197: return await wand.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
208: await wand.set_step_data(pipeline_id, step_id, processed_val, steps)
apps/810_webbrowser.py
12:'\nPipulate Webbrowser URL Opener Widget Workflow\nA workflow for demonstrating opening a URL in the default system browser.\n'
26: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
27: self.pipulate = pipulate
31: self.pipulate = pipulate
32: self.pipeline = pipeline
34: wand = self.pipulate
35: wand = self.pipulate
53: wand = self.pipulate
59: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
61: user_input = form.get('pipeline_id', '').strip()
74: pipeline_id = user_input
76: _, temp_prefix, user_provided_id_part = wand.generate_pipeline_key(self, user_input)
77: pipeline_id = f'{expected_prefix}{user_provided_id_part}'
78: wand.db['pipeline_id'] = pipeline_id
79: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
82: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
83: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
87: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
88: pipeline_id = wand.db.get('pipeline_id', 'unknown')
90: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
95: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
101: await wand.finalize_workflow(pipeline_id)
106: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
107: pipeline_id = wand.db.get('pipeline_id', 'unknown')
108: await wand.unfinalize_workflow(pipeline_id)
118: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = wand.db.get('pipeline_id', 'unknown')
124: await wand.clear_steps_from(pipeline_id, step_id, steps)
125: state = wand.read_state(pipeline_id)
127: wand.write_state(pipeline_id, state)
128: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
137: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = wand.db.get('pipeline_id', 'unknown')
143: state = wand.read_state(pipeline_id)
144: step_data = wand.get_step_data(pipeline_id, step_id, {})
146: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
162: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
167: pipeline_id = wand.db.get('pipeline_id', 'unknown')
174: await wand.set_step_data(pipeline_id, step_id, url_to_open, steps)
apps/620_mermaid.py
11:'\nPipulate Mermaid Diagram Widget Workflow\nA workflow for demonstrating the Mermaid.js diagram rendering widget.\n'
32: "PIPELINE_INPUT": "mermaid-pipeline-input",
42: "PIPELINE_INPUT": "Enter pipeline ID or press Enter for auto-generated key",
43: "ENTER_BUTTON": "Initialize workflow with entered or generated pipeline key",
57: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
58: self.pipulate = pipulate
62: self.pipulate = pipulate
63: self.pipeline = pipeline
65: wand = self.pipulate
66: wand = self.pipulate
84: wand = self.pipulate
90: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
92: user_input = form.get('pipeline_id', '').strip()
105: pipeline_id = user_input
107: _, temp_prefix, user_provided_id_part = wand.generate_pipeline_key(self, user_input)
108: pipeline_id = f'{expected_prefix}{user_provided_id_part}'
109: wand.db['pipeline_id'] = pipeline_id
110: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
113: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
114: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
118: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
119: pipeline_id = wand.db.get('pipeline_id', 'unknown')
121: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
126: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
132: await wand.finalize_workflow(pipeline_id)
137: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
138: pipeline_id = wand.db.get('pipeline_id', 'unknown')
139: await wand.unfinalize_workflow(pipeline_id)
149: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
152: pipeline_id = wand.db.get('pipeline_id', 'unknown')
155: await wand.clear_steps_from(pipeline_id, step_id, steps)
156: state = wand.read_state(pipeline_id)
158: wand.write_state(pipeline_id, state)
159: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
188: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
193: pipeline_id = wand.db.get('pipeline_id', 'unknown')
194: state = wand.read_state(pipeline_id)
195: step_data = wand.get_step_data(pipeline_id, step_id, {})
197: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
199: widget_id = f"mermaid-widget-{pipeline_id.replace('-', '_')}-{step_id}"
205: widget_id = f"mermaid-widget-{pipeline_id.replace('-', '_')}-{step_id}"
219: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
224: pipeline_id = wand.db.get('pipeline_id', 'unknown')
230: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
232: widget_id = f"mermaid-widget-{pipeline_id.replace('-', '_')}-{step_id}"
apps/050_documentation.py
20: TRAINING_PROMPT = """🔥 AI Assistant Bootstrap Instructions - Pipulate Radical Transparency System
24:**Most Common Bootstrap**: "I need help with my Pipulate workflow. My pipeline_id is [PIPELINE_ID]. Please use the pipeline_state_inspector MCP tool to grab my complete workflow state, then tell me what you found and how you can help."
26:**Mid-Session Debugging**: "My trifecta workflow is stuck at [STEP_NAME]. Pipeline: [PIPELINE_ID]. Please inspect my pipeline state and check the logs for errors."
34:### pipeline_state_inspector
36:curl -X POST "http://localhost:5001/mcp-tool-executor" -H "Content-Type: application/json" -d '{"tool": "pipeline_state_inspector", "params": {"pipeline_id": "PIPELINE_ID"}}'
47:- **Any Workflow Step**: `http://localhost:5001/trifecta/step_[STEP_NAME]?pipeline_id=PIPELINE_ID`
61:- 📊 **Full Pipeline State**: Current step, completed data, file status
74:- **Complete State Reconstruction**: From `pipeline_id` alone to full workflow understanding
80: def __init__(self, app, pipulate, pipeline, db):
82: self.pipulate = pipulate
83: wand = self.pipulate
124: pipulate_root = current_file.parent.parent # apps/050_documentation.py -> apps/ -> pipulate/
127: if not (pipulate_root / 'server.py').exists():
131: pipulate_root = cwd
137: pipulate_root = search_path
141: logger.warning("Could not find pipulate root directory - using current working directory")
142: pipulate_root = Path.cwd()
144: logger.info(f"Using pipulate root directory: {pipulate_root}")
147: training_dir = pipulate_root / 'helpers/botify'
155: rules_dir = pipulate_root / '.cursor/rules'
163: considerations_dir = pipulate_root / 'helpers/docs_sync/considerations'
171: readme_path = pipulate_root / 'README.md'
189: if 'ULTIMATE_PIPULATE_GUIDE' in filename:
212: if extracted_title and filename not in ['ULTIMATE_PIPULATE_GUIDE', 'ULTIMATE_PIPULATE_GUIDE_PART2', 'ULTIMATE_PIPULATE_GUIDE_PART3', 'QUICK_REFERENCE', 'botify_api_bootcamp', 'botify_api_examples', 'change_log']:
417: return "Development consideration exploring Pipulate architecture and design"
444: 'ULTIMATE_PIPULATE_GUIDE': 'Ultimate Pipulate Guide - Part 1: Core Patterns',
445: 'ULTIMATE_PIPULATE_GUIDE_PART2': 'Ultimate Pipulate Guide - Part 2: Advanced Patterns',
446: 'ULTIMATE_PIPULATE_GUIDE_PART3': 'Ultimate Pipulate Guide - Part 3: Expert Mastery',
451: 'change_log': 'Pipulate Development Changelog (Paginated)',
523: # Extract numeric prefix from filename (e.g., "00" from "00_PIPULATE_MASTER_GUIDE")
694: # Tables (pipe-delimited)
713: # Check if second line is a separator (contains dashes and pipes)
797: # Remove HTML comments like <!-- key: pipeline-workflows -->
992: welcome_text = "Comprehensive guides and tutorials for getting started with Pipulate."
1000: welcome_text = "Training materials and learning resources for mastering Pipulate workflows."
1008: welcome_text = "Framework rules and coding standards for Pipulate development."
1024: welcome_text = "Development considerations exploring Pipulate architecture, design decisions, and work-in-progress thoughts."
1032: welcome_text = "Complete documentation library for Pipulate. Start with Featured Guides for comprehensive learning."
1053: <a href="https://pipulate.com/" target="_blank" cls="nav-link-block">🏠 Pipulate.com</a>
1054: <a href="https://pipulate.com/documentation/" target="_blank" cls="nav-link-block">📚 Official Documentation</a>
1055: <a href="https://pipulate.com/development/" target="_blank" cls="nav-link-block">👨💻 1-Pager Development Guide</a>
1056: <a href="https://pipulate.com/guide/" target="_blank" cls="nav-link-block">📖 Paginated Guide</a>
1057: <a href="https://pypi.org/project/pipulate/" target="_blank" cls="nav-link-block">📦 PyPI Package</a>
1058: <a href="https://github.com/miklevin/pipulate" target="_blank" cls="nav-link-block">🐙 GitHub Repository</a>
1078: <title>{title} - Pipulate Documentation</title>
1277: if (typeof window.initializePipulateSplitter === 'function') {{
1278: window.initializePipulateSplitter(['.sidebar', '.content'], {{
1286: console.error('initializePipulateSplitter not available');
1309: "I need help with my Pipulate workflow. My pipeline_id is [PIPELINE_ID].<br>
1310: Please use the pipeline_state_inspector MCP tool to grab my complete workflow state,<br>
1314: 💡 <strong>Pro Tip:</strong> Your pipeline_id appears in the URL bar or forms when using workflows.
1321: "My trifecta workflow is stuck at [STEP_NAME]. Pipeline: [PIPELINE_ID].<br>
1322: Please inspect my pipeline state and check the logs for errors."
1334: -d '{"tool": "pipeline_state_inspector", "params": {"pipeline_id": "YOUR_PIPELINE_ID"}}'
1342: <li>📊 <strong>Full Pipeline State</strong>: Current step, completed data, file status</li>
1363:"I need help with my Pipulate workflow. My pipeline_id is [PIPELINE_ID].
1364:Please use the pipeline_state_inspector MCP tool to grab my complete workflow state,
1368:💡 **Pro Tip:** Your pipeline_id appears in the URL bar or forms when using workflows.
1373:"My trifecta workflow is stuck at [STEP_NAME]. Pipeline: [PIPELINE_ID].
1374:Please inspect my pipeline state and check the logs for errors."
1401: -d '{"tool": "pipeline_state_inspector", "params": {"pipeline_id": "YOUR_PIPELINE_ID"}}'
1411:- 📊 **Full Pipeline State**: Current step, completed data, file status
1424:- **Complete State Reconstruction**: From `pipeline_id` alone to full workflow understanding
1617: if self.pipulate and hasattr(self.pipulate, 'message_queue'):
1619: asyncio.create_task(self.pipulate.message_queue.add(
1620: self.pipulate,
1706: <title>{doc_info['title']} - Pipulate Documentation</title>
2228: if self.pipulate is not None and not self._has_streamed:
2231: await self.pipulate.stream(
2251: self.pipulate.append_to_history(
2252: f"[WIDGET CONTENT] Pipulate Documentation Browser\n{docs_message}",
2331: P(A(Span("🏠", cls="emoji icon-spaced"), "Pipulate.com",
2332: href="https://pipulate.com/", target="_blank",
2338: href="https://pipulate.com/documentation/", target="_blank",
2344: href="https://pipulate.com/development/", target="_blank",
2350: href="https://pipulate.com/guide/", target="_blank",
2356: href="https://pypi.org/project/pipulate/", target="_blank",
2362: href="https://github.com/miklevin/pipulate", target="_blank",
2661: if self.pipulate and hasattr(self.pipulate, 'message_queue'):
2672: asyncio.create_task(self.pipulate.message_queue.add(
2673: self.pipulate,
3220: if self.pipulate and hasattr(self.pipulate, 'message_queue'):
3222: asyncio.create_task(self.pipulate.message_queue.add(
3223: self.pipulate,
apps/400_botify_trifecta.py
273: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
274: self.pipulate = pipulate
278: self.pipulate = pipulate
279: self.pipeline = pipeline
281: wand = self.pipulate
282: wand = self.pipulate
311: pipulate.register_workflow_routes(self)
452: wand = self.pipulate
459: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
461: user_input = form.get('pipeline_id', '').strip()
474: pipeline_id = user_input
476: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
477: pipeline_id = f'{prefix}{user_provided_id}'
478: wand.db['pipeline_id'] = pipeline_id
479: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
482: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
483: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
492: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
493: pipeline_id = wand.db.get('pipeline_id', 'unknown')
495: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
500: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
507: await wand.finalize_workflow(pipeline_id)
513: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
514: pipeline_id = wand.db.get('pipeline_id', 'unknown')
515: await wand.unfinalize_workflow(pipeline_id)
521: wand, db, steps = (self.pipulate, self.pipulate.db, self.steps)
529: prev_data = wand.get_step_data(wand.db['pipeline_id'], prev_step.id, {})
535: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
538: pipeline_id = wand.db.get('pipeline_id', 'unknown')
541: await wand.clear_steps_from(pipeline_id, step_id, steps)
542: state = wand.read_state(pipeline_id)
544: wand.write_state(pipeline_id, state)
545: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
556: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
561: pipeline_id = wand.db.get('pipeline_id', 'unknown')
562: state = wand.read_state(pipeline_id)
563: step_data = wand.get_step_data(pipeline_id, step_id, {})
567: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
628: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
633: pipeline_id = wand.db.get('pipeline_id', 'unknown')
640: await wand.set_step_data(pipeline_id, step_id, project_data_str, steps)
669: pipeline_id=pipeline_id, step_id=step_id,
690: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
695: pipeline_id = wand.db.get('pipeline_id', 'unknown')
696: state = wand.read_state(pipeline_id)
697: step_data = wand.get_step_data(pipeline_id, step_id, {})
702: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
709: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
899: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
904: pipeline_id = wand.db.get('pipeline_id', 'unknown')
906: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
940: pipeline_id=pipeline_id, step_id=step_id,
977: await wand.set_step_data(pipeline_id, step_id, step.done, analysis_result_str)
1059: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1081: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1086: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1087: state = wand.read_state(pipeline_id)
1088: step_data = wand.get_step_data(pipeline_id, step_id, {})
1092: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1104: standardized_step_data = self._prepare_action_button_data(check_result, step_id, pipeline_id)
1134: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1194: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1199: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1220: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1237: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1247: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1324: await wand.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
1379: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1384: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1388: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1397: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1429: stored_step_data = wand.get_step_data(pipeline_id, step_id, {})
1450: await wand.set_step_data(pipeline_id, step_id, json.dumps(enhanced_data), steps)
1464: await wand.set_step_data(pipeline_id, step_id, json.dumps(check_result), steps)
1474: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1479: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1480: state = wand.read_state(pipeline_id)
1481: step_data = wand.get_step_data(pipeline_id, step_id, {})
1485: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1497: standardized_step_data = self._prepare_action_button_data(check_result, step_id, pipeline_id)
1526: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1592: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1597: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1619: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1636: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1644: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1674: await wand.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
1732: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1737: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1748: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1756: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1773: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1778: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1779: state = wand.read_state(pipeline_id)
1780: step_data = wand.get_step_data(pipeline_id, step_id, {})
1784: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1796: standardized_step_data = self._prepare_action_button_data(check_result, step_id, pipeline_id)
1826: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1886: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1891: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1913: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1930: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1940: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1979: await wand.set_step_data(pipeline_id, step_id, json.dumps(cached_result), steps)
2034: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
2039: pipeline_id = wand.db.get('pipeline_id', 'unknown')
2041: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
2049: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
2062: await self.process_search_console_data(wand, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
2086: await wand.set_step_data(pipeline_id, step_id, check_result_str, steps)
2090: standardized_step_data = self._prepare_action_button_data(check_result, step_id, pipeline_id)
2330: await self.message_queue.add(self.pipulate, f"📄 Fetched page {page_count} of analyses data...", verbatim=True)
2818: ui_constants = self.pipulate.get_ui_constants()
2859: ui_constants = self.pipulate.get_ui_constants()
3008: async def process_search_console_data(self, wand, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
3026: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3110: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3115: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3215: await self.pipulate.log_api_call_details(
3216: pipeline_id="build_exports", step_id="gsc_export",
3303: await self.pipulate.log_api_call_details(
3304: pipeline_id="build_exports", step_id="crawl_export",
3358: await self.pipulate.log_api_call_details(
3359: pipeline_id="build_exports", step_id="weblog_export",
3399: await self.message_queue.add(self.pipulate, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
3411: await self.message_queue.add(self.pipulate, poll_msg, verbatim=True)
3418: await self.message_queue.add(self.pipulate, poll_attempt_msg, verbatim=True)
3422: await self.message_queue.add(self.pipulate, poll_attempt_msg, verbatim=True)
3429: await self.message_queue.add(self.pipulate, url_switch_msg, verbatim=True)
3439: await self.pipulate.log_api_call_details(
3440: pipeline_id="poll_job_status", step_id=step_context or "polling",
3459: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3464: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3471: await self.message_queue.add(self.pipulate, status_msg, verbatim=True)
3475: await self.pipulate.log_api_call_details(
3476: pipeline_id="poll_job_status", step_id=step_context or "polling",
3487: await self.message_queue.add(self.pipulate, f'✅ {success_msg}', verbatim=True)
3495: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3503: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
3510: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3515: await self.message_queue.add(self.pipulate, retry_msg, verbatim=True)
3519: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
3525: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3529: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
3534: await self.message_queue.add(self.pipulate, f'⚠️ {max_attempts_msg}', verbatim=True)
3539: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
3545: pipeline_id = wand.db.get('pipeline_id', 'unknown')
3552: step_data = wand.get_step_data(pipeline_id, step_id, {})
3837: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3874: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
3879: pipeline_id = wand.db.get('pipeline_id', 'unknown')
4106: await wand.set_step_data(pipeline_id, step_id, check_result_str, steps)
4118: standardized_step_data = self._prepare_action_button_data(check_result, step_id, pipeline_id)
4154: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
4160: pipeline_id = wand.db.get('pipeline_id', 'unknown')
4161: step_data = wand.get_step_data(pipeline_id, step_id, {})
4165: state = wand.read_state(pipeline_id)
4210: wand.write_state(pipeline_id, state)
4215: wand.write_state(pipeline_id, state)
4600: return self.pipulate.generate_botify_bqlv2_python_code(
4614: return self.pipulate.generate_botify_bqlv1_python_code(
4733: def _prepare_action_button_data(self, raw_step_data, step_id, pipeline_id):
4744: # Try to get missing data from pipeline state
4747: project_step_data = self.pipulate.get_step_data(pipeline_id, 'step_project', {})
4758: analysis_step_data = self.pipulate.get_step_data(pipeline_id, 'step_analysis', {})
4781: logger.debug(f"Error getting pipeline context for action buttons: {e}")
apps/240_simon_mcp.py
22: This utility provides immediate access to MCP tools without requiring pipeline keys or LLM interpretation.
55: def __init__(self, app, pipulate, pipeline, db, app_name=None):
56: self.pipulate = pipulate
59: self.pipulate = pipulate
60: self.pipeline = pipeline
61: wand = self.pipulate
62: wand = self.pipulate
78: pipulate.register_workflow_routes(self)
97: """Direct utility - skip pipeline keys and go straight to Simon Says testing."""
98: # Bypass the entire pipeline system - this is a utility tool, not a data workflow
108: wand, db = self.pipulate, self.pipulate.db
111: user_input_key = form.get('pipeline_id', '').strip()
121: _, prefix_for_key_gen, _ = wand.generate_pipeline_key(self)
123: pipeline_id = user_input_key
125: _, prefix, user_part = wand.generate_pipeline_key(self, user_input_key)
126: pipeline_id = f'{prefix}{user_part}'
128: wand.db['pipeline_id'] = pipeline_id
129: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
138: wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
139: pipeline_id = wand.db.get('pipeline_id', 'unknown')
141: finalize_data = wand.get_step_data(pipeline_id, finalize_step_obj.id, {})
142: state = wand.read_state(pipeline_id)
162: step_data = wand.get_step_data(pipeline_id, step.id, {})
188: await wand.finalize_workflow(pipeline_id)
193: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
194: pipeline_id = wand.db.get('pipeline_id', 'unknown')
195: await wand.unfinalize_workflow(pipeline_id)
200: wand, db, current_steps = self.pipulate, self.pipulate.db, self.steps
208: prev_data = wand.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
214: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
218: pipeline_id = wand.db.get('pipeline_id', 'unknown')
223: await wand.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
224: state = wand.read_state(pipeline_id)
226: wand.write_state(pipeline_id, state)
270: wand = self.pipulate
272: # Simple utility - no pipeline state management needed
502: style='display: flex; gap: var(--pipulate-gap-sm); align-items: flex-start; flex-wrap: wrap;'
529: wand = self.pipulate
928: style='display: flex; gap: var(--pipulate-gap-sm); align-items: flex-start; flex-wrap: wrap;'
apps/025_aspect.py
115: def __init__(self, app, pipulate, pipeline, db):
117: self.pipulate = pipulate
127: if self.pipulate is not None and not self._has_streamed:
129: await self.pipulate.stream(
155: P("This widget shows the aspect ratio inside the Pipulate UI. For a minimal, template-free 'acetate' overlay, use the link below."),
apps/010_introduction.py
8:- Global Voice Toggle (persisted in pip.db as '1'/'0')
37: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
39: self.wand = pipulate
46: self.ui = pipulate.get_ui_constants()
57: pipulate.register_workflow_routes(self)
apps/570_switch.py
42: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
43: self.pipulate = pipulate
47: self.pipulate = pipulate
48: self.pipeline = pipeline
50: wand = self.pipulate
51: wand = self.pipulate
71: wand = self.pipulate
78: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
80: user_input = form.get('pipeline_id', '').strip()
93: pipeline_id = user_input
95: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
96: pipeline_id = f'{prefix}{user_provided_id}'
97: wand.db['pipeline_id'] = pipeline_id
98: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
107: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = wand.db.get('pipeline_id', 'unknown')
110: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
115: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await wand.finalize_workflow(pipeline_id)
127: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = wand.db.get('pipeline_id', 'unknown')
129: await wand.unfinalize_workflow(pipeline_id)
139: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = wand.db.get('pipeline_id', 'unknown')
145: await wand.clear_steps_from(pipeline_id, step_id, steps)
146: state = wand.read_state(pipeline_id)
148: wand.write_state(pipeline_id, state)
153: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
158: pipeline_id = wand.db.get('pipeline_id', 'unknown')
159: state = wand.get_step_data(pipeline_id, step_id, {})
171: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
176: pipeline_id = wand.db.get('pipeline_id', 'unknown')
181: await wand.set_step_data(pipeline_id, step_id, switch_state, steps)
apps/120_link_graph.py
326: await self.message_queue.add(pip, f'📊 Loaded new data: {len(new_df):,} rows', verbatim=True)
336: await self.message_queue.add(pip, f'⚠️ Warning: Could not load new data: {str(e)}', verbatim=True)
338: await self.message_queue.add(pip, '⚠️ No new data found, continuing without additional metrics', verbatim=True)
411:## Integration with Broader Pipulate Ecosystem
426:This workflow demonstrates advanced patterns that can be applied across the Pipulate ecosystem for building robust, flexible data processing pipelines.
436: await self.message_queue.add(pip, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
437: return P('Error: Link graph data not found. Please complete Step 2 first.', style=pip.get_style('error'))
449: await self.message_queue.add(pip, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
459: await self.message_queue.add(pip, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
461: await self.message_queue.add(pip, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
473: await self.message_queue.add(pip, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
483: await self.message_queue.add(pip, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
485: await self.message_queue.add(pip, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
679: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
680: self.pipulate = pipulate
684: self.pipulate = pipulate
685: self.pipeline = pipeline
687: wand = self.pipulate
713: pipulate.register_workflow_routes(self)
775: wand = self.pipulate
782: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
784: user_input = form.get('pipeline_id', '').strip()
797: pipeline_id = user_input
799: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
800: pipeline_id = f'{prefix}{user_provided_id}'
801: wand.db['pipeline_id'] = pipeline_id
802: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
805: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
806: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
815: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
816: pipeline_id = wand.db.get('pipeline_id', 'unknown')
818: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
823: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
830: await wand.finalize_workflow(pipeline_id)
836: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
837: pipeline_id = wand.db.get('pipeline_id', 'unknown')
838: await wand.unfinalize_workflow(pipeline_id)
844: wand, db, steps = (self.pipulate, self.wand.db, self.steps)
852: prev_data = wand.get_step_data(wand.db['pipeline_id'], prev_step.id, {})
858: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
861: pipeline_id = wand.db.get('pipeline_id', 'unknown')
864: await wand.clear_steps_from(pipeline_id, step_id, steps)
865: state = wand.read_state(pipeline_id)
867: wand.write_state(pipeline_id, state)
868: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
879: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
884: pipeline_id = wand.db.get('pipeline_id', 'unknown')
885: state = wand.read_state(pipeline_id)
886: step_data = wand.get_step_data(pipeline_id, step_id, {})
890: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
945: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
950: pipeline_id = wand.db.get('pipeline_id', 'unknown')
957: await wand.set_step_data(pipeline_id, step_id, project_data_str, steps)
966: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
971: pipeline_id = wand.db.get('pipeline_id', 'unknown')
972: state = wand.read_state(pipeline_id)
973: step_data = wand.get_step_data(pipeline_id, step_id, {})
978: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
985: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1127: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1132: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1134: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1190: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1212: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1217: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1218: state = wand.read_state(pipeline_id)
1219: step_data = wand.get_step_data(pipeline_id, step_id, {})
1225: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1235: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1313: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1318: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1339: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1357: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1384: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1407: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1412: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1421: step_data = wand.get_step_data(pipeline_id, step_id, {})
1648: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1664: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
1694: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1699: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1700: state = wand.read_state(pipeline_id)
1701: step_data = wand.get_step_data(pipeline_id, step_id, {})
1705: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1744: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1807: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1812: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1834: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1851: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1859: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1887: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1892: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1893: state = wand.read_state(pipeline_id)
1894: step_data = wand.get_step_data(pipeline_id, step_id, {})
1898: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1938: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1995: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
2000: pipeline_id = wand.db.get('pipeline_id', 'unknown')
2022: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
2039: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
2063: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
2068: pipeline_id = wand.db.get('pipeline_id', 'unknown')
2071: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
2081: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
2098: await self.process_search_console_data(wand, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
2104: await wand.set_step_data(pipeline_id, step_id, check_result_str, steps)
2133: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
2138: pipeline_id = db.get("pipeline_id", "unknown")
2139: state = wand.read_state(pipeline_id)
2140: step_data = wand.get_step_data(pipeline_id, step_id, {})
2147: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
2184: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
2188: pipeline_id = db.get("pipeline_id", "unknown")
2198: values: {{ 'pipeline_id': '{pipeline_id}' }}
2207: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
2213: pipeline_id = form.get('pipeline_id', 'unknown')
2216: project_data = wand.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
2217: analysis_data = wand.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
2436: await wand.set_step_data(pipeline_id, step_id, json.dumps(visualization_result), self.steps)
2591: await self.pipulate.log_api_call_details(
2592: pipeline_id="fetch_analyses", step_id="analyses_list",
2688: wand = self.pipulate
2922: ui_constants = self.pipulate.get_ui_constants()
3107: async def process_search_console_data(self, wand, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
3124: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3208: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3213: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
3297: await self.pipulate.log_api_call_details(
3298: pipeline_id="build_exports", step_id="gsc_export",
3372: await self.pipulate.log_api_call_details(
3373: pipeline_id="build_exports", step_id="crawl_export",
3427: await self.pipulate.log_api_call_details(
3428: pipeline_id="build_exports", step_id="weblog_export",
3474: await self.message_queue.add(self.pipulate, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
3486: await self.message_queue.add(self.pipulate, poll_msg, verbatim=True)
3493: await self.message_queue.add(self.pipulate, poll_attempt_msg, verbatim=True)
3497: await self.message_queue.add(self.pipulate, poll_attempt_msg, verbatim=True)
3504: await self.message_queue.add(self.pipulate, url_switch_msg, verbatim=True)
3514: await self.pipulate.log_api_call_details(
3515: pipeline_id="poll_job_status", step_id=step_context or "polling",
3534: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3539: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3546: await self.message_queue.add(self.pipulate, status_msg, verbatim=True)
3550: await self.pipulate.log_api_call_details(
3551: pipeline_id="poll_job_status", step_id=step_context or "polling",
3562: await self.message_queue.add(self.pipulate, f'✅ {success_msg}', verbatim=True)
3570: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3578: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
3585: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3590: await self.message_queue.add(self.pipulate, retry_msg, verbatim=True)
3594: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
3600: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
3604: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
3609: await self.message_queue.add(self.pipulate, f'⚠️ {max_attempts_msg}', verbatim=True)
3614: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
3619: pipeline_id = wand.db.get('pipeline_id', 'unknown')
3626: step_data = wand.get_step_data(pipeline_id, step_id, {})
3901: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3938: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
3943: pipeline_id = wand.db.get('pipeline_id', 'unknown')
4156: await wand.set_step_data(pipeline_id, step_id, check_result_str, steps)
4200: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
4206: pipeline_id = wand.db.get('pipeline_id', 'unknown')
4207: step_data = wand.get_step_data(pipeline_id, step_id, {})
4211: state = wand.read_state(pipeline_id)
4241: wand.write_state(pipeline_id, state)
4246: wand.write_state(pipeline_id, state)
4446: ui_constants = self.pipulate.get_ui_constants()
4646: ui_constants = self.pipulate.get_ui_constants()
5157: wand, db, steps, app_name = self.pipulate, self.wand.db, self.steps, self.app_name
5163: pipeline_id = db.get("pipeline_id", "unknown")
5164: state = wand.read_state(pipeline_id)
5165: step_data = wand.get_step_data(pipeline_id, step_id, {})
5167: finalize_data = wand.get_step_data(pipeline_id, "finalize", {})
5204: wand, db, steps, app_name = self.pipulate, self.wand.db, self.steps, self.app_name
5209: pipeline_id = db.get("pipeline_id", "unknown")
5214: await wand.set_step_data(pipeline_id, step_id, value_to_save, steps)
5232: pipeline_id = request.query_params.get('pipeline_id')
5243: # For cosmo_ files, use pipeline_id to construct path
5244: if not pipeline_id:
5245: return HTMLResponse("Pipeline ID required for cosmo files", status_code=400)
5247: # Load pipeline data to get project info
5248: wand = self.pipulate
5249: project_data = wand.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
5250: analysis_data = wand.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
5260: return HTMLResponse("Invalid pipeline data", status_code=400)
5265: return HTMLResponse("Invalid pipeline data", status_code=400)
apps/640_javascript.py
29: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
30: self.pipulate = pipulate
36: self.pipulate = pipulate
37: self.pipeline = pipeline
39: wand = self.pipulate
40: wand = self.pipulate
58: wand = self.pipulate
65: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
67: user_input = form.get('pipeline_id', '').strip()
80: pipeline_id = user_input
82: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
83: pipeline_id = f'{prefix}{user_provided_id}'
84: wand.db['pipeline_id'] = pipeline_id
85: logger.debug(f'Using pipeline ID: {pipeline_id}')
86: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
91: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
92: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
98: parsed = wand.parse_pipeline_key(pipeline_id)
100: self.pipeline.xtra(app_name=app_name)
101: matching_records = [record.pkey for record in self.pipeline() if record.pkey.startswith(prefix)]
102: if pipeline_id not in matching_records:
103: matching_records.append(pipeline_id)
104: updated_datalist = wand.update_datalist('pipeline-ids', options=matching_records)
109: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
110: pipeline_id = wand.db.get('pipeline_id', 'unknown')
112: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
117: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
123: await wand.finalize_workflow(pipeline_id)
129: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
130: pipeline_id = wand.db.get('pipeline_id', 'unknown')
131: await wand.unfinalize_workflow(pipeline_id)
143: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
146: pipeline_id = wand.db.get('pipeline_id', 'unknown')
149: await wand.clear_steps_from(pipeline_id, step_id, steps)
150: state = wand.read_state(pipeline_id)
152: wand.write_state(pipeline_id, state)
153: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
163: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
168: pipeline_id = wand.db.get('pipeline_id', 'unknown')
169: state = wand.read_state(pipeline_id)
170: step_data = wand.get_step_data(pipeline_id, step_id, {})
172: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
175: widget_id = f'js-widget-{pipeline_id}-{step_id}'.replace('-', '_')
186: widget_id = f'js-widget-{pipeline_id}-{step_id}'.replace('-', '_')
196: wand.write_state(pipeline_id, state)
203: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
207: pipeline_id = wand.db.get('pipeline_id', 'unknown')
214: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
216: widget_id = f'js-widget-{pipeline_id}-{step_id}'.replace('-', '_')
apps/510_text_field.py
23: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
24: self.pipulate = pipulate
27: self.pipulate = pipulate
28: self.pipeline = pipeline
30: wand = self.pipulate
31: wand = self.pipulate
51: wand = self.pipulate
57: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
59: user_input = form.get('pipeline_id', '').strip()
72: pipeline_id = user_input
74: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
75: pipeline_id = f'{prefix}{user_provided_id}'
76: wand.db['pipeline_id'] = pipeline_id
77: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
80: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
81: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
85: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
86: pipeline_id = wand.db.get('pipeline_id', 'unknown')
88: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
93: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
99: await wand.finalize_workflow(pipeline_id)
104: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
105: pipeline_id = wand.db.get('pipeline_id', 'unknown')
106: await wand.unfinalize_workflow(pipeline_id)
111: wand, db, steps = (self.pipulate, self.pipulate.db, self.steps)
119: prev_data = wand.get_step_data(wand.db['pipeline_id'], prev_step.id, {})
124: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
127: pipeline_id = wand.db.get('pipeline_id', 'unknown')
130: await wand.clear_steps_from(pipeline_id, step_id, steps)
131: state = wand.read_state(pipeline_id)
133: wand.write_state(pipeline_id, state)
134: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
140: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
145: pipeline_id = wand.db.get('pipeline_id', 'unknown')
146: state = wand.read_state(pipeline_id)
147: step_data = wand.get_step_data(pipeline_id, step_id, {})
149: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
169: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
174: pipeline_id = wand.db.get('pipeline_id', 'unknown')
176: return await wand.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
187: await wand.set_step_data(pipeline_id, step_id, processed_val, steps)
apps/540_checkboxes.py
43: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
44: self.pipulate = pipulate
48: self.pipulate = pipulate
49: self.pipeline = pipeline
51: wand = self.pipulate
52: wand = self.pipulate
72: wand = self.pipulate
79: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
81: user_input = form.get('pipeline_id', '').strip()
94: pipeline_id = user_input
96: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
97: pipeline_id = f'{prefix}{user_provided_id}'
98: wand.db['pipeline_id'] = pipeline_id
99: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
102: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
103: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
108: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
109: pipeline_id = wand.db.get('pipeline_id', 'unknown')
111: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
116: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
122: await wand.finalize_workflow(pipeline_id)
128: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
129: pipeline_id = wand.db.get('pipeline_id', 'unknown')
130: await wand.unfinalize_workflow(pipeline_id)
136: wand, db, steps = (self.pipulate, self.pipulate.db, self.steps)
144: prev_data = wand.get_step_data(wand.db['pipeline_id'], prev_step.id, {})
150: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
153: pipeline_id = wand.db.get('pipeline_id', 'unknown')
156: await wand.clear_steps_from(pipeline_id, step_id, steps)
157: state = wand.read_state(pipeline_id)
159: wand.write_state(pipeline_id, state)
160: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
167: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = wand.db.get('pipeline_id', 'unknown')
173: state = wand.read_state(pipeline_id)
174: step_data = wand.get_step_data(pipeline_id, step_id, {})
175: logger.debug(f'Pipeline ID: {pipeline_id}')
182: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
190: raw_options = await self.get_options(pipeline_id)
216: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
221: pipeline_id = wand.db.get('pipeline_id', 'unknown')
226: await self.pipulate.set_step_data(pipeline_id, step_id, values, steps)
227: await self.message_queue.add(self.pipulate, self.step_messages.get(step_id, {}).get('complete', f"{step.show} complete: {', '.join(values)}"), verbatim=True)
228: return Div(self.pipulate.display_revert_header(step_id=step_id, app_name=app_name, message=f"{step.show}: {', '.join(values)}", steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
230: async def get_options(self, pipeline_id):
apps/710_pandas.py
12:'\nPipulate Pandas Table Widget Workflow\nA workflow for demonstrating the Pandas DataFrame to HTML table rendering widget.\n'
26: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
27: self.pipulate = pipulate
31: self.pipulate = pipulate
32: self.pipeline = pipeline
34: wand = self.pipulate
35: wand = self.pipulate
53: wand = self.pipulate
59: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
61: user_input = form.get('pipeline_id', '').strip()
74: pipeline_id = user_input
76: _, temp_prefix, user_provided_id_part = wand.generate_pipeline_key(self, user_input)
77: pipeline_id = f'{expected_prefix}{user_provided_id_part}'
78: wand.db['pipeline_id'] = pipeline_id
79: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
82: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
83: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
87: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
88: pipeline_id = wand.db.get('pipeline_id', 'unknown')
90: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
95: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
101: await wand.finalize_workflow(pipeline_id)
106: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
107: pipeline_id = wand.db.get('pipeline_id', 'unknown')
108: await wand.unfinalize_workflow(pipeline_id)
118: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = wand.db.get('pipeline_id', 'unknown')
124: await wand.clear_steps_from(pipeline_id, step_id, steps)
125: state = wand.read_state(pipeline_id)
127: wand.write_state(pipeline_id, state)
128: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
167: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = wand.db.get('pipeline_id', 'unknown')
173: state = wand.read_state(pipeline_id)
174: step_data = wand.get_step_data(pipeline_id, step_id, {})
176: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
192: wand.write_state(pipeline_id, state)
200: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
205: pipeline_id = wand.db.get('pipeline_id', 'unknown')
219: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
apps/560_range.py
42: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
43: self.pipulate = pipulate
47: self.pipulate = pipulate
48: self.pipeline = pipeline
50: wand = self.pipulate
51: wand = self.pipulate
71: wand = self.pipulate
78: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
80: user_input = form.get('pipeline_id', '').strip()
93: pipeline_id = user_input
95: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
96: pipeline_id = f'{prefix}{user_provided_id}'
97: wand.db['pipeline_id'] = pipeline_id
98: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
107: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = wand.db.get('pipeline_id', 'unknown')
110: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
115: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await wand.finalize_workflow(pipeline_id)
127: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = wand.db.get('pipeline_id', 'unknown')
129: await wand.unfinalize_workflow(pipeline_id)
139: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = wand.db.get('pipeline_id', 'unknown')
145: await wand.clear_steps_from(pipeline_id, step_id, steps)
146: state = wand.read_state(pipeline_id)
148: wand.write_state(pipeline_id, state)
154: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
159: pipeline_id = wand.db.get('pipeline_id', 'unknown')
160: state = wand.read_state(pipeline_id)
161: step_data = wand.get_step_data(pipeline_id, step_id, {})
162: logger.debug(f'Pipeline ID: {pipeline_id}')
167: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
188: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
193: pipeline_id = wand.db.get('pipeline_id', 'unknown')
204: await wand.set_step_data(pipeline_id, step_id, value, steps)
apps/550_radios.py
42: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
43: self.pipulate = pipulate
47: self.pipulate = pipulate
48: self.pipeline = pipeline
50: wand = self.pipulate
51: wand = self.pipulate
71: wand = self.pipulate
78: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
80: user_input = form.get('pipeline_id', '').strip()
93: pipeline_id = user_input
95: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
96: pipeline_id = f'{prefix}{user_provided_id}'
97: wand.db['pipeline_id'] = pipeline_id
98: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
101: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
102: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
107: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
108: pipeline_id = wand.db.get('pipeline_id', 'unknown')
110: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
115: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
121: await wand.finalize_workflow(pipeline_id)
127: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
128: pipeline_id = wand.db.get('pipeline_id', 'unknown')
129: await wand.unfinalize_workflow(pipeline_id)
139: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
144: pipeline_id = wand.db.get('pipeline_id', 'unknown')
145: await wand.clear_steps_from(pipeline_id, step_id, steps)
146: state = wand.read_state(pipeline_id)
148: wand.write_state(pipeline_id, state)
151: async def get_options(self, pipeline_id):
199: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
204: pipeline_id = wand.db.get('pipeline_id', 'unknown')
205: state = wand.read_state(pipeline_id)
206: step_data = wand.get_step_data(pipeline_id, step_id, {})
207: logger.debug(f'Pipeline ID: {pipeline_id}')
212: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
220: raw_options = await self.get_options(pipeline_id)
246: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
251: pipeline_id = wand.db.get('pipeline_id', 'unknown')
256: await self.pipulate.set_step_data(pipeline_id, step_id, value, steps)
257: await self.message_queue.add(self.pipulate, self.step_messages.get(step_id, {}).get('complete', f'{step.show} complete: {value}'), verbatim=True)
258: return Div(self.pipulate.display_revert_header(step_id=step_id, app_name=app_name, message=f'{step.show}: {value}', steps=steps), Div(id=next_step_id, hx_get=f'/{app_name}/{next_step_id}', hx_trigger='load'), id=step_id)
apps/530_dropdown.py
40: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
41: self.pipulate = pipulate
45: self.pipulate = pipulate
46: self.pipeline = pipeline
48: wand = self.pipulate
49: wand = self.pipulate
69: wand = self.pipulate
76: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
78: user_input = form.get('pipeline_id', '').strip()
91: pipeline_id = user_input
93: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
94: pipeline_id = f'{prefix}{user_provided_id}'
95: wand.db['pipeline_id'] = pipeline_id
96: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
99: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
100: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
105: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = wand.db.get('pipeline_id', 'unknown')
108: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
113: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
119: await wand.finalize_workflow(pipeline_id)
125: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
126: pipeline_id = wand.db.get('pipeline_id', 'unknown')
127: await wand.unfinalize_workflow(pipeline_id)
137: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
142: pipeline_id = wand.db.get('pipeline_id', 'unknown')
143: await wand.clear_steps_from(pipeline_id, step_id, steps)
144: state = wand.read_state(pipeline_id)
146: wand.write_state(pipeline_id, state)
149: async def get_options(self, pipeline_id):
197: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
202: pipeline_id = wand.db.get('pipeline_id', 'unknown')
203: state = wand.read_state(pipeline_id)
204: step_data = wand.get_step_data(pipeline_id, step_id, {})
205: logger.debug(f'Pipeline ID: {pipeline_id}')
210: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
218: raw_options = await self.get_options(pipeline_id)
238: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
243: pipeline_id = wand.db.get('pipeline_id', 'unknown')
248: await wand.set_step_data(pipeline_id, step_id, value, steps)
apps/200_workflow_genesis.py
50: ENDPOINT_MESSAGE = """Create Pipulate workflows using three distinct approaches: blank placeholder for learning, hello world recreation for understanding helper tools, or trifecta workflow for complex scenarios."""
51: TRAINING_PROMPT = """You are assisting with workflow creation in Pipulate. Help users choose between three approaches: 1) Blank placeholder for beginners learning step management, 2) Hello world recreation for understanding helper tool sequences, 3) Trifecta workflow for complex data collection scenarios. Guide them through command generation and explain the purpose of each approach."""
53: def __init__(self, app, pipulate, pipeline, db, app_name=None):
54: self.pipulate = pipulate
57: self.pipulate = pipulate
58: self.pipeline = pipeline
59: wand = self.pipulate
60: wand = self.pipulate
101: wand = self.pipulate
123: wand = self.pipulate
129: wand, db = self.pipulate, self.pipulate.db
132: user_input_key = form.get('pipeline_id', '').strip()
140: _, prefix_for_key_gen, _ = wand.generate_pipeline_key(self)
142: pipeline_id = user_input_key
144: _, prefix, user_part = wand.generate_pipeline_key(self, user_input_key)
145: pipeline_id = f'{prefix}{user_part}'
147: wand.db['pipeline_id'] = pipeline_id
148: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
151: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
152: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
158: wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
159: pipeline_id = wand.db.get('pipeline_id', 'unknown')
162: finalize_data = wand.get_step_data(pipeline_id, finalize_step_obj.id, {})
177: all_data_steps_complete = all(wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
192: await wand.finalize_workflow(pipeline_id)
197: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
198: pipeline_id = wand.db.get('pipeline_id', 'unknown')
199: await wand.unfinalize_workflow(pipeline_id)
204: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
207: pipeline_id = wand.db.get('pipeline_id', 'unknown')
212: await wand.clear_steps_from(pipeline_id, step_id_to_revert_to, self.steps)
213: state = wand.read_state(pipeline_id)
215: wand.write_state(pipeline_id, state)
217: message = await wand.get_state_message(pipeline_id, self.steps, self.step_messages)
496: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
502: pipeline_id = wand.db.get('pipeline_id', 'unknown')
503: state = wand.read_state(pipeline_id)
504: step_data = wand.get_step_data(pipeline_id, step_id, {})
506: finalize_sys_data = wand.get_step_data(pipeline_id, 'finalize', {})
557: "You are assisting with the Kung Fu Download workflow. Help users understand each step and provide guidance on customizing the workflow parameters. If they ask what this workflow does, explain that it's a template for learning Pipulate workflow creation. The user will likely ask you for the secret word to prove that you have been trained, and if the do (but only if they ask), tell them MORPHEUS",
574: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
580: pipeline_id = wand.db.get('pipeline_id', 'unknown')
590: 'training_prompt': form_data.get('training_prompt', '').strip() or 'You are assisting with the Kung Fu Download workflow. Help users understand each step and provide guidance on customizing the workflow parameters. If they ask what this workflow does, explain that it\'s a template for learning Pipulate workflow creation. The user will likely ask you for the secret word to prove that you have been trained, and if the do (but only if they ask), tell them MORPHEUS'
594: await wand.set_step_data(pipeline_id, step_id, params, self.steps)
611: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
617: pipeline_id = wand.db.get('pipeline_id', 'unknown')
618: state = wand.read_state(pipeline_id)
619: step_data = wand.get_step_data(pipeline_id, step_id, {})
621: finalize_sys_data = wand.get_step_data(pipeline_id, 'finalize', {})
666: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
672: pipeline_id = wand.db.get('pipeline_id', 'unknown')
680: await wand.set_step_data(pipeline_id, step_id, template_choice, self.steps)
684: step_01_data = wand.get_step_data(pipeline_id, 'step_01', {})
688: widget_id = f"template-experience-{pipeline_id.replace('-', '_')}-{step_id}"
773: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
779: pipeline_id = wand.db.get('pipeline_id', 'unknown')
780: state = wand.read_state(pipeline_id)
781: step_data = wand.get_step_data(pipeline_id, step_id, {})
783: finalize_sys_data = wand.get_step_data(pipeline_id, 'finalize', {})
808: step_01_data = wand.get_step_data(pipeline_id, 'step_01', {})
850: P("Check the console window where you started Pipulate for yellow warnings above the 'SERVER RESTART' banner. Import errors will be shown there but won't break the server.",
866: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
872: pipeline_id = wand.db.get('pipeline_id', 'unknown')
875: step_01_data = wand.get_step_data(pipeline_id, 'step_01', {})
876: step_02_data = wand.get_step_data(pipeline_id, 'step_02', {})
1034: await wand.set_step_data(pipeline_id, step_id, execution_summary, self.steps)
1038: # Parse current pipeline key to show next key guidance
1039: parsed_key = wand.parse_pipeline_key(pipeline_id)
apps/450_stream_simulator.py
15:# Instead we'll use the pipulate instance passed to the plugin
28: def __init__(self, app, pipulate, pipeline, db):
30: self.pipulate = pipulate
31: wand = self.pipulate
33: # Use message queue from Pipulate for ordered message streaming
34: self.message_queue = pipulate.message_queue
78: self.pipulate,
83: self.pipulate,
88: self.pipulate,
93: self.pipulate,
98: self.pipulate,
105: self.pipulate,
apps/730_matplotlib.py
33: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
34: self.pipulate = pipulate
40: self.pipulate = pipulate
41: self.pipeline = pipeline
43: wand = self.pipulate
44: wand = self.pipulate
62: wand = self.pipulate
69: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
71: user_input = form.get('pipeline_id', '').strip()
84: pipeline_id = user_input
86: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
87: pipeline_id = f'{prefix}{user_provided_id}'
88: wand.db['pipeline_id'] = pipeline_id
89: logger.debug(f'Using pipeline ID: {pipeline_id}')
90: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
95: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
96: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
102: parsed = wand.parse_pipeline_key(pipeline_id)
104: self.pipeline.xtra(app_name=app_name)
105: matching_records = [record.pkey for record in self.pipeline() if record.pkey.startswith(prefix)]
106: if pipeline_id not in matching_records:
107: matching_records.append(pipeline_id)
108: updated_datalist = wand.update_datalist('pipeline-ids', options=matching_records)
113: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
114: pipeline_id = wand.db.get('pipeline_id', 'unknown')
116: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
121: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
127: await wand.finalize_workflow(pipeline_id)
133: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
134: pipeline_id = wand.db.get('pipeline_id', 'unknown')
135: await wand.unfinalize_workflow(pipeline_id)
147: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
150: pipeline_id = wand.db.get('pipeline_id', 'unknown')
153: await wand.clear_steps_from(pipeline_id, step_id, steps)
154: state = wand.read_state(pipeline_id)
156: wand.write_state(pipeline_id, state)
157: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
167: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = wand.db.get('pipeline_id', 'unknown')
173: state = wand.read_state(pipeline_id)
174: step_data = wand.get_step_data(pipeline_id, step_id, {})
176: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
192: wand.write_state(pipeline_id, state)
203: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
207: pipeline_id = wand.db.get('pipeline_id', 'unknown')
222: await wand.set_step_data(pipeline_id, step_id, counter_data, steps)
apps/110_parameter_buster.py
216: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
217: self.pipulate = pipulate
221: self.pipulate = pipulate
222: self.pipeline = pipeline
224: wand = self.pipulate
225: wand = self.pipulate
251: pipulate.register_workflow_routes(self)
310: wand = self.pipulate
316: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
318: user_input = form.get('pipeline_id', '').strip()
330: pipeline_id = user_input
332: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
333: pipeline_id = f'{prefix}{user_provided_id}'
334: wand.db['pipeline_id'] = pipeline_id
335: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
338: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
339: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
347: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
348: pipeline_id = wand.db.get('pipeline_id', 'unknown')
350: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
355: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
362: await wand.finalize_workflow(pipeline_id)
368: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
369: pipeline_id = wand.db.get('pipeline_id', 'unknown')
370: await wand.unfinalize_workflow(pipeline_id)
376: wand, db, steps = (self.pipulate, self.pipulate.db, self.steps)
384: prev_data = wand.get_step_data(wand.db['pipeline_id'], prev_step.id, {})
390: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
393: pipeline_id = wand.db.get('pipeline_id', 'unknown')
396: await wand.clear_steps_from(pipeline_id, step_id, steps)
397: state = wand.read_state(pipeline_id)
399: wand.write_state(pipeline_id, state)
400: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
410: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
415: pipeline_id = wand.db.get('pipeline_id', 'unknown')
416: state = wand.read_state(pipeline_id)
417: step_data = wand.get_step_data(pipeline_id, step_id, {})
421: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
475: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
480: pipeline_id = wand.db.get('pipeline_id', 'unknown')
487: await wand.set_step_data(pipeline_id, step_id, project_data_str, steps)
496: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
501: pipeline_id = wand.db.get('pipeline_id', 'unknown')
502: state = wand.read_state(pipeline_id)
503: step_data = wand.get_step_data(pipeline_id, step_id, {})
508: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
515: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
636: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
641: pipeline_id = wand.db.get('pipeline_id', 'unknown')
643: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
691: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, steps)
713: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
718: pipeline_id = wand.db.get('pipeline_id', 'unknown')
719: state = wand.read_state(pipeline_id)
720: step_data = wand.get_step_data(pipeline_id, step_id, {})
724: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
731: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
784: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
839: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
844: pipeline_id = wand.db.get('pipeline_id', 'unknown')
862: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
877: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
885: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
913: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
918: pipeline_id = wand.db.get('pipeline_id', 'unknown')
919: state = wand.read_state(pipeline_id)
920: step_data = wand.get_step_data(pipeline_id, step_id, {})
924: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
931: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
985: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1035: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1040: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1058: await wand.set_step_data(pipeline_id, step_id, json.dumps(skip_result), steps)
1073: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1097: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1102: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1105: prev_step_data = wand.get_step_data(pipeline_id, prev_step_id, {})
1115: analysis_step_data = wand.get_step_data(pipeline_id, analysis_step_id, {})
1142: await self.process_search_console_data(wand, pipeline_id, step_id, username, project_name, analysis_slug, check_result)
1148: await wand.set_step_data(pipeline_id, step_id, check_result_str, steps)
1196: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1201: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1202: state = wand.read_state(pipeline_id)
1203: step_data = wand.get_step_data(pipeline_id, step_id, {})
1212: project_data = wand.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
1213: analysis_data = wand.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
1224: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1250: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1255: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1258: return Card(H3(f'{step.show}'), P('Counting parameters...', cls='mb-15px'), Progress(style='margin-top: 10px;'), Script("\n setTimeout(function() {\n htmx.ajax('POST', '" + f'/{app_name}/step_05_process' + "', {\n target: '#" + step_id + "',\n values: { \n 'pipeline_id': '" + pipeline_id + "',\n 'param_count': '" + param_count + "'\n }\n });\n }, 500);\n "), id=step_id)
1262: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1268: pipeline_id = form.get('pipeline_id', 'unknown')
1270: project_data = wand.get_step_data(pipeline_id, 'step_01', {}).get('botify_project', '{}')
1271: analysis_data = wand.get_step_data(pipeline_id, 'step_02', {}).get('analysis_selection', '{}')
1309: await wand.set_step_data(pipeline_id, step_id, summary_str, steps)
1319: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1324: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1325: state = wand.read_state(pipeline_id)
1326: step_data = wand.get_step_data(pipeline_id, step_id, {})
1328: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1331: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
1370: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
1379: wand.write_state(pipeline_id, state)
1380: prev_step_data = wand.get_step_data(pipeline_id, 'step_05', {})
1431: prev_step_data = wand.get_step_data(pipeline_id, 'step_05', {})
1493: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1497: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1502: prev_step_data = wand.get_step_data(pipeline_id, 'step_05', {})
1542: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
1543: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
1554: wand, db, app_name = (self.pipulate, self.pipulate.db, self.app_name)
1555: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1560: prev_step_data = wand.get_step_data(pipeline_id, 'step_05', {})
1636: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1641: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1642: state = wand.read_state(pipeline_id)
1643: step_data = wand.get_step_data(pipeline_id, step_id, {})
1644: step_06_data = wand.get_step_data(pipeline_id, 'step_06', {})
1672: state = wand.read_state(pipeline_id)
1678: wand.write_state(pipeline_id, state)
1679: widget_id = f"markdown-widget-{pipeline_id.replace('-', '_')}-{step_id}"
1680: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1693: await wand.clear_steps_from(pipeline_id, 'finalize', self.steps)
1699: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1704: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1708: step_data = wand.get_step_data(pipeline_id, step_id, {})
1716: step_06_data = wand.get_step_data(pipeline_id, 'step_06', {})
1726: state = wand.read_state(pipeline_id)
1730: await wand.clear_steps_from(pipeline_id, step_id, self.steps)
1733: wand.write_state(pipeline_id, state)
1735: widget_id = f"markdown-widget-{pipeline_id.replace('-', '_')}-{step_id}"
1853: await self.pipulate.log_api_call_details(
1854: pipeline_id="fetch_analyses", step_id="analyses_list",
1903: wand = self.pipulate
2104: ui_constants = self.pipulate.get_ui_constants()
2270: async def process_search_console_data(self, wand, pipeline_id, step_id, username, project_name, analysis_slug, check_result):
2280: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2363: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2368: await wand.set_step_data(pipeline_id, step_id, check_result_str, self.steps)
2442: await self.pipulate.log_api_call_details(
2443: pipeline_id="build_exports", step_id="gsc_export",
2512: await self.pipulate.log_api_call_details(
2513: pipeline_id="build_exports", step_id="crawl_export",
2564: await self.pipulate.log_api_call_details(
2565: pipeline_id="build_exports", step_id="weblog_export",
2608: await self.message_queue.add(self.pipulate, f'🎯 Using job ID {job_id} for polling...', verbatim=True)
2620: await self.message_queue.add(self.pipulate, poll_msg, verbatim=True)
2626: await self.message_queue.add(self.pipulate, poll_attempt_msg, verbatim=True)
2630: await self.message_queue.add(self.pipulate, poll_attempt_msg, verbatim=True)
2636: await self.message_queue.add(self.pipulate, url_switch_msg, verbatim=True)
2644: await self.pipulate.log_api_call_details(
2645: pipeline_id="poll_job_status", step_id=step_context or "polling",
2663: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
2668: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
2675: await self.message_queue.add(self.pipulate, status_msg, verbatim=True)
2678: await self.pipulate.log_api_call_details(
2679: pipeline_id="poll_job_status", step_id=step_context or "polling",
2689: await self.message_queue.add(self.pipulate, f'✅ {success_msg}', verbatim=True)
2697: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
2705: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
2712: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
2717: await self.message_queue.add(self.pipulate, retry_msg, verbatim=True)
2721: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
2727: await self.message_queue.add(self.pipulate, f'❌ {error_msg}', verbatim=True)
2731: await self.message_queue.add(self.pipulate, wait_msg, verbatim=True)
2736: await self.message_queue.add(self.pipulate, f'⚠️ {max_attempts_msg}', verbatim=True)
2741: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
2746: pipeline_id = wand.db.get('pipeline_id', 'unknown')
2753: step_data = wand.get_step_data(pipeline_id, step_id, {})
3014: await wand.set_step_data(pipeline_id, step_id, analysis_result_str, self.steps)
3048: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
3053: pipeline_id = wand.db.get('pipeline_id', 'unknown')
3260: await wand.set_step_data(pipeline_id, step_id, check_result_str, steps)
3293: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
3299: pipeline_id = wand.db.get('pipeline_id', 'unknown')
3302: state = wand.read_state(pipeline_id)
3306: wand.write_state(pipeline_id, state)
3309: wand.write_state(pipeline_id, state)
3317: step_data = wand.get_step_data(pipeline_id, step_id, {})
3333: state = wand.read_state(pipeline_id)
3351: wand.write_state(pipeline_id, state)
3355: wand.write_state(pipeline_id, state)
3553: return self.pipulate.generate_botify_bqlv2_python_code(
3567: return self.pipulate.generate_botify_bqlv1_python_code(
3895: self.pipulate.append_to_history(f'[CACHE] No parameter counter cache found at {cache_file_path}')
3901: self.pipulate.append_to_history(f"[CACHE] Loaded parameter counters from cache: {len(counters_data['raw_counters'])} sources")
3977: self.pipulate.append_to_history('[VISUALIZATION] Creating parameter distribution visualization')
apps/630_prism.py
11:'\nPipulate PrismJS Code Highlighter Widget Workflow\nA workflow for demonstrating the Prism.js code syntax highlighting widget.\n'
25: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
26: self.pipulate = pipulate
30: self.pipulate = pipulate
31: self.pipeline = pipeline
33: wand = self.pipulate
34: wand = self.pipulate
52: wand = self.pipulate
58: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
60: user_input = form.get('pipeline_id', '').strip()
73: pipeline_id = user_input
75: _, temp_prefix, user_provided_id_part = wand.generate_pipeline_key(self, user_input)
76: pipeline_id = f'{expected_prefix}{user_provided_id_part}'
77: wand.db['pipeline_id'] = pipeline_id
78: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
81: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
82: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
86: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: pipeline_id = wand.db.get('pipeline_id', 'unknown')
89: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
94: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
100: await wand.finalize_workflow(pipeline_id)
105: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = wand.db.get('pipeline_id', 'unknown')
107: await wand.unfinalize_workflow(pipeline_id)
117: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
120: pipeline_id = wand.db.get('pipeline_id', 'unknown')
123: await wand.clear_steps_from(pipeline_id, step_id, steps)
124: state = wand.read_state(pipeline_id)
126: wand.write_state(pipeline_id, state)
127: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
167: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
172: pipeline_id = wand.db.get('pipeline_id', 'unknown')
173: state = wand.read_state(pipeline_id)
174: step_data = wand.get_step_data(pipeline_id, step_id, {})
176: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
195: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
201: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
216: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
221: pipeline_id = wand.db.get('pipeline_id', 'unknown')
245: await wand.set_step_data(pipeline_id, step_id, user_val_raw, steps)
247: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
apps/060_tasks.py
53:You are an AI assistant helping users understand and create Pipulate CRUD applications. When users interact with a Todo-style app created from the tasks.py template, your job is to:
55:1. Explain how Pipulate CRUD apps provide a reusable framework for creating list-based applications
64:Pipulate CRUD apps implement a "Don't Repeat Yourself" (DRY) pattern that:
174: self.pipulate_instance = getattr(plugin, 'pipulate', None)
175: super().__init__(name=plugin.name, table=table, toggle_field='done', sort_field='priority', pipulate_instance=self.pipulate_instance)
336: def __init__(self, app, pipulate, pipeline, db_dictlike):
340: self.pipulate = pipulate
341: self.pipeline_table = pipeline
462: add_placeholder = f'Add new {self.pipulate.make_singular(self.name.lower())}'
apps/610_markdown.py
11:'\nPipulate Markdown MarkedJS Widget Workflow\nA workflow for demonstrating the Markdown MarkedJS rendering widget.\n'
50: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
51: self.pipulate = pipulate
55: self.pipulate = pipulate
56: self.pipeline = pipeline
58: wand = self.pipulate
59: wand = self.pipulate
82: wand = self.pipulate
88: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
90: user_input = form.get('pipeline_id', '').strip()
103: pipeline_id = user_input
105: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
106: pipeline_id = f'{prefix}{user_provided_id}'
107: wand.db['pipeline_id'] = pipeline_id
108: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
111: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
112: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
116: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
117: pipeline_id = wand.db.get('pipeline_id', 'unknown')
119: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
124: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
130: await wand.finalize_workflow(pipeline_id)
135: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
136: pipeline_id = wand.db.get('pipeline_id', 'unknown')
137: await wand.unfinalize_workflow(pipeline_id)
147: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
150: pipeline_id = wand.db.get('pipeline_id', 'unknown')
153: await wand.clear_steps_from(pipeline_id, step_id, steps)
154: state = wand.read_state(pipeline_id)
156: wand.write_state(pipeline_id, state)
157: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
333: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
338: pipeline_id = wand.db.get('pipeline_id', 'unknown')
339: state = wand.read_state(pipeline_id)
340: step_data = wand.get_step_data(pipeline_id, step_id, {})
342: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
344: widget_id = f"marked-widget-{pipeline_id.replace('-', '_')}-{step_id}"
350: widget_id = f"marked-widget-{pipeline_id.replace('-', '_')}-{step_id}"
364: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
369: pipeline_id = wand.db.get('pipeline_id', 'unknown')
375: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
377: widget_id = f"marked-widget-{pipeline_id.replace('-', '_')}-{step_id}"
apps/440_browser_automation.py
21:'\nPipulate Browser Automation Workflow\n\nThis workflow demonstrates Selenium-based browser automation capabilities:\n- Cross-platform Chrome automation (Linux/macOS)\n- Clean browser sessions with temporary profiles\n- Detailed status logging and error handling\n- URL opening and verification\n'
52: This serves as the primary development ground for Pipulate's browser automation features.
56: ENDPOINT_MESSAGE = "Open URLs using Selenium for browser automation. This workflow demonstrates Pipulate's browser automation capabilities."
59: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
60: self.pipulate = pipulate
64: self.pipulate = pipulate
65: self.pipeline = pipeline
67: wand = self.pipulate
68: wand = self.pipulate
90: wand = self.pipulate
97: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
99: user_input = form.get('pipeline_id', '').strip()
112: pipeline_id = user_input
114: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
115: pipeline_id = f'{prefix}{user_provided_id}'
116: wand.db['pipeline_id'] = pipeline_id
117: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
120: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
121: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
126: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
127: pipeline_id = wand.db.get('pipeline_id', 'unknown')
129: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
134: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
140: state = wand.read_state(pipeline_id)
142: step_data = wand.get_step_data(pipeline_id, step.id, {})
147: wand.write_state(pipeline_id, state)
153: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
154: pipeline_id = wand.db.get('pipeline_id', 'unknown')
155: state = wand.read_state(pipeline_id)
161: wand.write_state(pipeline_id, state)
167: wand, db, steps = (self.pipulate, self.pipulate.db, self.steps)
175: prev_data = wand.get_step_data(wand.db['pipeline_id'], prev_step.id, {})
181: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
184: pipeline_id = wand.db.get('pipeline_id', 'unknown')
187: await wand.clear_steps_from(pipeline_id, step_id, steps)
188: state = wand.read_state(pipeline_id)
200: wand.write_state(pipeline_id, state)
201: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
207: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
212: pipeline_id = wand.db.get('pipeline_id', 'unknown')
213: state = wand.read_state(pipeline_id)
214: step_data = wand.get_step_data(pipeline_id, step_id, {})
216: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
229: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
234: pipeline_id = wand.db.get('pipeline_id', 'unknown')
241: await wand.set_step_data(pipeline_id, step_id, url, steps)
282: wand, db = (self.pipulate, self.pipulate.db)
324: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
329: pipeline_id = wand.db.get('pipeline_id', 'unknown')
330: state = wand.read_state(pipeline_id)
331: step_data = wand.get_step_data(pipeline_id, step_id, {})
333: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
353: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
358: pipeline_id = wand.db.get('pipeline_id', 'unknown')
413: await wand.set_step_data(pipeline_id, step_id, crawl_data, steps)
425: def _get_selenium_profile_paths(self, pipeline_id: str, desired_profile_leaf_name: str = 'google_session') -> tuple[str, str]:
437: def _get_persistent_profile_paths(self, pipeline_id: str) -> tuple[str, str]:
449: pipeline_id = self.pipulate.db.get('pipeline_id', 'unknown')
450: if not pipeline_id or pipeline_id == 'unknown':
451: return JSONResponse(status_code=400, content={'error': 'No pipeline ID found in db'})
452: user_data_dir, profile_dir = self._get_selenium_profile_paths(pipeline_id)
453: step_data = self.pipulate.get_step_data(pipeline_id, 'step_03', {})
458: state = self.pipulate.read_state(pipeline_id)
461: return Div(self.pipulate.display_revert_header(step_id='step_03', app_name=self.app_name, message='Ephemeral Login Test', steps=self.steps), Div(id=next_step_id, hx_get=f'/{self.app_name}/{next_step_id}', hx_trigger='load'), id='step_03')
470: pipeline_id = self.pipulate.db.get('pipeline_id', 'unknown')
471: if not pipeline_id or pipeline_id == 'unknown':
472: return JSONResponse(status_code=400, content={'error': 'No pipeline ID found in db'})
473: user_data_dir, profile_dir = self._get_selenium_profile_paths(pipeline_id)
474: step_data = self.pipulate.get_step_data(pipeline_id, 'step_03', {})
502: state = self.pipulate.read_state(pipeline_id)
504: self.pipulate.write_state(pipeline_id, state)
514: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
519: pipeline_id = wand.db.get('pipeline_id', 'unknown')
520: state = wand.read_state(pipeline_id)
525: wand.write_state(pipeline_id, state)
531: pipeline_id = self.pipulate.db.get('pipeline_id', 'unknown')
532: if not pipeline_id or pipeline_id == 'unknown':
533: return JSONResponse(status_code=400, content={'error': 'No pipeline ID found in db'})
534: user_data_dir, profile_dir = self._get_persistent_profile_paths(pipeline_id)
535: step_data = self.pipulate.get_step_data(pipeline_id, 'step_04', {})
540: state = self.pipulate.read_state(pipeline_id)
543: return Div(self.pipulate.display_revert_header(step_id='step_04', app_name=self.app_name, message='Persistent Login Test', steps=self.steps), Div(id=next_step_id, hx_get=f'/{self.app_name}/{next_step_id}', hx_trigger='load'), id='step_04')
552: pipeline_id = self.pipulate.db.get('pipeline_id', 'unknown')
553: if not pipeline_id or pipeline_id == 'unknown':
554: return JSONResponse(status_code=400, content={'error': 'No pipeline ID found in db'})
555: user_data_dir, profile_dir = self._get_persistent_profile_paths(pipeline_id)
556: step_data = self.pipulate.get_step_data(pipeline_id, 'step_04', {})
584: state = self.pipulate.read_state(pipeline_id)
586: self.pipulate.write_state(pipeline_id, state)
596: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
601: pipeline_id = wand.db.get('pipeline_id', 'unknown')
602: state = wand.read_state(pipeline_id)
607: wand.write_state(pipeline_id, state)
613: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
618: pipeline_id = wand.db.get('pipeline_id', 'unknown')
619: state = wand.read_state(pipeline_id)
620: step_data = wand.get_step_data(pipeline_id, step_id, {})
622: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
636: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
641: pipeline_id = wand.db.get('pipeline_id', 'unknown')
643: await wand.set_step_data(pipeline_id, step_id, placeholder_value, steps)
apps/030_roles.py
63: return """# Pipulate Roles System: Homepage & Menu Control Center
65:## Overview: The Heart of Pipulate's UX
66:The Roles system is Pipulate's **homepage and menu control center** - a sophisticated CRUD application that determines which plugins appear in your APP menu. It's both the **landing page** and the **role-based access control system** that customizes your experience.
70:1. **Homepage Experience**: First thing users see when they visit Pipulate
231:The Roles system is the **orchestrator** of the entire Pipulate experience - it conducts the symphony of plugins, profiles, and user preferences to create a harmonious, personalized interface.
235:**The Roles system transforms Pipulate from a simple tool into a sophisticated, personalized digital workspace that adapts to each user's needs and expertise level.** """
240: self.pipulate_instance = getattr(plugin, 'pipulate', None)
243: sort_field='priority', pipulate_instance=self.pipulate_instance
371: def __init__(self, app, pipulate, pipeline, db_dictlike, config):
375: self.pipulate = pipulate
376: self.pipeline_table = pipeline
461: return "Pipulate" # Fallback to default name
apps/130_gap_analysis.py
31: def __init__(self, app, pipulate, pipeline, db, app_name=None):
32: self.pipulate = pipulate
35: self.pipulate = pipulate
36: self.pipeline = pipeline
37: wand = self.pipulate
38: wand = self.pipulate
66: pipulate.register_workflow_routes(self)
83: wand = self.pipulate
90: wand, db, steps, app_name = (self.pipulate, self.pipulate.db, self.steps, self.APP_NAME)
92: user_input = form.get('pipeline_id', '').strip()
105: pipeline_id = user_input
107: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
108: pipeline_id = f'{prefix}{user_provided_id}'
109: wand.db['pipeline_id'] = pipeline_id
110: logger.debug(f'Using pipeline ID: {pipeline_id}')
111: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
118: await self.message_queue.add(wand, f'{self.ui["EMOJIS"]["WORKFLOW"]} Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
119: await self.message_queue.add(wand, f"{self.ui["EMOJIS"]["KEY"]} Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
130: parsed = wand.parse_pipeline_key(pipeline_id)
132: self.pipeline.xtra(app_name=app_name)
133: matching_records = [record.pkey for record in self.pipeline() if record.pkey.startswith(prefix)]
134: if pipeline_id not in matching_records:
135: matching_records.append(pipeline_id)
136: updated_datalist = wand.update_datalist('pipeline-ids', options=matching_records)
140: wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
142: pipeline_id = wand.db.get('pipeline_id', 'unknown')
145: finalize_data = wand.get_step_data(pipeline_id, finalize_step_obj.id, {})
173: all_data_steps_complete = all(wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
202: await wand.finalize_workflow(pipeline_id)
207: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
208: pipeline_id = wand.db.get('pipeline_id', 'unknown')
209: await wand.unfinalize_workflow(pipeline_id)
251: wand, db, current_steps = self.pipulate, self.pipulate.db, self.steps
259: prev_data = wand.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
265: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
269: pipeline_id = wand.db.get('pipeline_id', 'unknown')
274: await wand.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
275: state = wand.read_state(pipeline_id)
277: wand.write_state(pipeline_id, state)
279: message = await wand.get_state_message(pipeline_id, current_steps_to_pass_helpers, self.step_messages)
286: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
291: pipeline_id = wand.db.get('pipeline_id', 'unknown')
292: state = wand.read_state(pipeline_id)
293: step_data = wand.get_step_data(pipeline_id, step_id, {})
295: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
299: widget_id = f"content-gap-yaml-{pipeline_id.replace('-', '_')}-{step_id}-finalized"
308: widget_id = f"content-gap-yaml-{pipeline_id.replace('-', '_')}-{step_id}-completed"
334: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
339: pipeline_id = wand.db.get('pipeline_id', 'unknown')
342: return await wand.handle_finalized_step(pipeline_id, step_id, steps, app_name, self)
400: existing_data = wand.get_step_data(pipeline_id, step_id, {})
661: await wand.set_step_data(pipeline_id, step_id, yaml_output, steps)
679: widget_id = f"content-gap-yaml-{pipeline_id.replace('-', '_')}-{step_id}"
705: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
711: pipeline_id = db.get("pipeline_id", "unknown")
712: state = wand.read_state(pipeline_id)
713: step_data = wand.get_step_data(pipeline_id, step_id, {})
715: finalize_data = wand.get_step_data(pipeline_id, "finalize", {})
752: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
757: pipeline_id = db.get("pipeline_id", "unknown")
762: await wand.set_step_data(pipeline_id, step_id, value_to_save, steps)
781: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
787: pipeline_id = db.get("pipeline_id", "unknown")
788: state = wand.read_state(pipeline_id)
789: step_data = wand.get_step_data(pipeline_id, step_id, {})
791: finalize_data = wand.get_step_data(pipeline_id, "finalize", {})
828: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
833: pipeline_id = db.get("pipeline_id", "unknown")
838: await wand.set_step_data(pipeline_id, step_id, value_to_save, steps)
apps/580_upload.py
54: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
55: self.pipulate = pipulate
61: self.pipulate = pipulate
62: self.pipeline = pipeline
64: wand = self.pipulate
65: wand = self.pipulate
86: wand = self.pipulate
93: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
95: user_input = form.get('pipeline_id', '').strip()
108: pipeline_id = user_input
110: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
111: pipeline_id = f'{prefix}{user_provided_id}'
112: wand.db['pipeline_id'] = pipeline_id
113: logger.debug(f'Using pipeline ID: {pipeline_id}')
114: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
119: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
120: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
126: parsed = wand.parse_pipeline_key(pipeline_id)
128: self.pipeline.xtra(app_name=app_name)
129: matching_records = [record.pkey for record in self.pipeline() if record.pkey.startswith(prefix)]
130: if pipeline_id not in matching_records:
131: matching_records.append(pipeline_id)
132: updated_datalist = wand.update_datalist('pipeline-ids', options=matching_records)
137: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
138: pipeline_id = wand.db.get('pipeline_id', 'unknown')
140: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
145: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
151: await wand.finalize_workflow(pipeline_id)
157: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
158: pipeline_id = wand.db.get('pipeline_id', 'unknown')
159: await wand.unfinalize_workflow(pipeline_id)
171: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
174: pipeline_id = wand.db.get('pipeline_id', 'unknown')
177: await wand.clear_steps_from(pipeline_id, step_id, steps)
178: state = wand.read_state(pipeline_id)
180: wand.write_state(pipeline_id, state)
181: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
187: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
192: pipeline_id = wand.db.get('pipeline_id', 'unknown')
193: state = wand.read_state(pipeline_id)
194: step_data = wand.get_step_data(pipeline_id, step_id, {})
196: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
236: file_summary = wand.get_step_data(pipeline_id, step_id)
316: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
321: pipeline_id = wand.db.get('pipeline_id', 'unknown')
377: await wand.set_step_data(pipeline_id, step_id, file_summary, steps)
apps/015_backup_introduction.py
22: def __init__(self, app, pipulate, pipeline, db):
25: self.pipulate = pipulate
26: self.pipeline = pipeline
27: wand = self.pipulate
50: P(f'This is Instance 1.0 of Project Pipulate AIE (pronounced "Ayyy"—like the Fonz) inhabited by Chip O\'Theseus and powered by Ollama ({model}) to assist you 🤖. Click ',
236: self.pipulate.db['intro_current_page'] = str(page_num)
250: current_page = int(self.pipulate.db.get('intro_current_page', '1'))
253: if self.pipulate is not None:
257: self.pipulate.append_to_history(self.ENDPOINT_MESSAGE, role="system")
apps/720_rich.py
11:'\nPipulate Rich Table Widget Workflow\nA workflow for demonstrating the Rich Table widget with beautiful styling.\n'
25: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
26: self.pipulate = pipulate
30: self.pipulate = pipulate
31: self.pipeline = pipeline
33: wand = self.pipulate
34: wand = self.pipulate
52: wand = self.pipulate
58: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
60: user_input = form.get('pipeline_id', '').strip()
73: pipeline_id = user_input
75: _, temp_prefix, user_provided_id_part = wand.generate_pipeline_key(self, user_input)
76: pipeline_id = f'{expected_prefix}{user_provided_id_part}'
77: wand.db['pipeline_id'] = pipeline_id
78: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
81: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
82: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
86: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
87: pipeline_id = wand.db.get('pipeline_id', 'unknown')
89: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
94: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
100: await wand.finalize_workflow(pipeline_id)
105: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
106: pipeline_id = wand.db.get('pipeline_id', 'unknown')
107: await wand.unfinalize_workflow(pipeline_id)
118: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
121: pipeline_id = wand.db.get('pipeline_id', 'unknown')
124: await wand.clear_steps_from(pipeline_id, step_id, steps)
125: state = wand.read_state(pipeline_id)
127: wand.write_state(pipeline_id, state)
128: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
166: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
171: pipeline_id = wand.db.get('pipeline_id', 'unknown')
172: state = wand.read_state(pipeline_id)
173: step_data = wand.get_step_data(pipeline_id, step_id, {})
175: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
193: wand.write_state(pipeline_id, state)
200: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
205: pipeline_id = wand.db.get('pipeline_id', 'unknown')
219: await wand.set_step_data(pipeline_id, step_id, table_data, steps)
apps/100_connect_with_botify.py
33: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
34: self.pipulate = pipulate
40: self.pipulate = pipulate
41: self.pipeline = pipeline
43: wand = self.pipulate
44: wand = self.pipulate
46: # Use message queue from Pipulate for ordered message streaming
125: wand, pipeline, steps, app_name = self.pipulate, self.pipeline, self.steps, self.app_name
127: pipeline.xtra(app_name=app_name)
128: existing_ids = [record.pkey for record in pipeline()]
154: name="pipeline_id",
155: list="pipeline-ids",
170: id="pipeline-ids",
171: data_testid="botify-pipeline-datalist"),
192: This method validates the Botify API token, initializes the pipeline state,
202: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
204: pipeline_id = form.get("pipeline_id")
205: if not pipeline_id:
207: db["pipeline_id"] = pipeline_id
208: state, error = wand.initialize_if_missing(pipeline_id, {"app_name": app_name})
212: state = wand.read_state(pipeline_id)
216: wand.write_state(pipeline_id, state)
217: logger.debug(f"Cleared finalize state for pipeline: {pipeline_id}")
221: username = await self.validate_botify_token(pipeline_id)
259: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
260: pipeline_id = db.get("pipeline_id", "")
262: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
263: logger.debug(f"Pipeline ID: {pipeline_id}")
269: logger.debug("Pipeline is already finalized")
325: username = await self.validate_botify_token(pipeline_id)
333: state = wand.read_state(pipeline_id)
337: wand.write_state(pipeline_id, state)
343: state = wand.read_state(pipeline_id)
346: wand.write_state(pipeline_id, state)
352: token_file.write(f"{pipeline_id}\n# username: {username}")
403: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
404: pipeline_id = db.get("pipeline_id", "unknown")
407: state = wand.read_state(pipeline_id)
410: wand.write_state(pipeline_id, state)
430: wand, db, steps = self.pipulate, self.pipulate.db, self.steps
440: prev_data = wand.get_step_data(db["pipeline_id"], prev_step_id, {})
445: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.app_name
448: pipeline_id = db.get("pipeline_id", "unknown")
451: await wand.clear_steps_from(pipeline_id, step_id, steps)
452: state = wand.read_state(pipeline_id)
454: wand.write_state(pipeline_id, state)
476: wand = self.pipulate
apps/230_dev_assistant.py
24: Pipulate Development Assistant
27: - Validate workflow patterns against the Ultimate Pipulate Guide
34: This assistant implements the 25 critical patterns from the Ultimate Pipulate Guide
35: and provides real-time validation and debugging assistance for Pipulate development.
39: ENDPOINT_MESSAGE = """Interactive debugging and development guidance for Pipulate workflows. Validate patterns, debug issues, check template suitability, and get expert recommendations based on the Ultimate Pipulate Implementation Guide and workflow creation helper system."""
40: TRAINING_PROMPT = """You are the Pipulate Development Assistant. Help developers with: 1. Pattern validation against the 25 critical patterns from the Ultimate Guide. 2. Debugging workflow issues (auto-key generation, three-phase logic, chain reactions). 3. Plugin structure analysis and recommendations. 4. State management troubleshooting. 5. Template suitability and marker compatibility for helper tools. 6. Best practice guidance for workflow development. Always reference specific patterns from the Ultimate Guide and provide actionable debugging steps."""
87: def __init__(self, app, pipulate, pipeline, db, app_name=None):
88: self.pipulate = pipulate
91: self.pipulate = pipulate
92: self.pipeline = pipeline
93: wand = self.pipulate
94: wand = self.pipulate
132: """Direct utility - skip pipeline keys and go straight to plugin analysis."""
133: # Bypass the entire pipeline system - this is a utility tool, not a data workflow
143: wand, db = self.pipulate, self.pipulate.db
146: user_input_key = form.get('pipeline_id', '').strip()
154: _, prefix_for_key_gen, _ = wand.generate_pipeline_key(self)
156: pipeline_id = user_input_key
158: _, prefix, user_part = wand.generate_pipeline_key(self, user_input_key)
159: pipeline_id = f'{prefix}{user_part}'
161: wand.db['pipeline_id'] = pipeline_id
162: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': internal_app_name})
165: await self.message_queue.add(wand, f'Development Assistant Session: {pipeline_id}', verbatim=True, spaces_before=0)
170: wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
171: pipeline_id = wand.db.get('pipeline_id', 'unknown')
174: await wand.set_step_data(pipeline_id, 'finalize', {'finalized': True}, self.steps)
178: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
209: wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
210: pipeline_id = wand.db.get('pipeline_id', 'unknown')
211: await wand.unfinalize_workflow(pipeline_id)
216: wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
219: pipeline_id = wand.db.get('pipeline_id', 'unknown')
224: await wand.clear_steps_from(pipeline_id, step_id, self.steps)
226: state = wand.read_state(pipeline_id)
228: wand.write_state(pipeline_id, state)
772: f"This should be placed right after the line: user_input_key = form.get('pipeline_id', '').strip()"
786: f"finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
796: f" return pip.chain_reverter(step_id, step_index, steps, app_name, processed_val)\n"
813: f"return pip.chain_reverter(\n"
824: f" pip.display_revert_header(step_id, app_name, steps, f'{{step.show}}: {{value}}'),\n"
872: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
873: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
875: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
876: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
877: f" return pip.run_all_cells(app_name, self.steps)\n"
904: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
905: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
907: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
908: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
909: f" return pip.run_all_cells(app_name, self.steps)\n"
922: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
923: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
930: f" await pip.finalize_workflow(pipeline_id)\n"
931: f" return pip.run_all_cells(app_name, self.steps)\n"
948: f" pip, steps, app_name = (self.pipulate, self.steps, self.app_name)\n"
951: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
958: f" await pip.set_step_data(pipeline_id, step_id, user_input, steps)\n"
960: f" return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n"
1014: uses_centralized_routes = 'pipulate.register_workflow_routes(self)' in content
1101: has_centralized_ui = 'pip.get_ui_constants()' in content or 'self.ui = ' in content
1106: analysis["patterns_found"].append("✅ Centralized UI constants via pipulate.get_ui_constants() (modern pattern)")
1135: f"def __init__(self, app, pipulate, pipeline, db, app_name=None):\n"
1137: f" self.ui = pip.get_ui_constants() # Access centralized UI constants\n"
1224: 'pipulate', 'db', 'steps', 'app_name', 'message_queue', 'steps_indices',
1250: 'self_contained': len([dep for dep in dependencies if dep.startswith('self.') and not any(common in dep for common in ['pipulate', 'db', 'steps', 'app_name'])]) == 0
1326: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1331: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1332: state = pip.read_state(pipeline_id)
1333: step_data = pip.get_step_data(pipeline_id, step_id, {{}})
1335: finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})
1344: return pip.chain_reverter(step_id, step_index, steps, app_name, user_val)
1364: pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
1367: pipeline_id = pip.db.get('pipeline_id', 'unknown')
1374: await pip.set_step_data(pipeline_id, step_id, user_input, steps)
1376: return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n""")
1395: f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
1396: f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
1399: f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
1400: f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
1401: f" return pip.run_all_cells(app_name, self.steps)\n"
1403: f" finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
1797: # Simplified utility - no pipeline state management needed
2171: # Initialize Prism highlighting and Pipulate copy functionality
2182: // Re-initialize Pipulate copy functionality for new content
2183: if (typeof PipulateCopy !== 'undefined') {{
2184: console.log('🔄 Initializing PipulateCopy for DevAssistant');
2185: PipulateCopy.initializeDataCopyButtons();
2186: PipulateCopy.initializeInlineCodeCopy();
2197: if (typeof PipulateCopy !== 'undefined') {{
2198: PipulateCopy.initializeDataCopyButtons();
2199: PipulateCopy.initializeInlineCodeCopy();
apps/300_blank_placeholder.py
18: A minimal template for creating new Pipulate workflows.
30: def __init__(self, app, pipulate, pipeline, db, app_name=None):
31: self.pipulate = pipulate
34: self.pipulate = pipulate
35: self.pipeline = pipeline
36: wand = self.pipulate
37: wand = self.pipulate
53: pipulate.register_workflow_routes(self)
70: wand = self.pipulate
77: wand, db, steps, app_name = (self.pipulate, self.pipulate.db, self.steps, self.APP_NAME)
79: user_input = form.get('pipeline_id', '').strip()
92: pipeline_id = user_input
94: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
95: pipeline_id = f'{prefix}{user_provided_id}'
96: wand.db['pipeline_id'] = pipeline_id
97: logger.debug(f'Using pipeline ID: {pipeline_id}')
98: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
105: await self.message_queue.add(wand, f'{self.ui["EMOJIS"]["WORKFLOW"]} Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
106: await self.message_queue.add(wand, f"{self.ui["EMOJIS"]["KEY"]} Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
117: parsed = wand.parse_pipeline_key(pipeline_id)
119: self.pipeline.xtra(app_name=app_name)
120: matching_records = [record.pkey for record in self.pipeline() if record.pkey.startswith(prefix)]
121: if pipeline_id not in matching_records:
122: matching_records.append(pipeline_id)
123: updated_datalist = wand.update_datalist('pipeline-ids', options=matching_records)
127: wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME
129: pipeline_id = wand.db.get('pipeline_id', 'unknown')
132: finalize_data = wand.get_step_data(pipeline_id, finalize_step_obj.id, {})
160: all_data_steps_complete = all(wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in self.steps if step.id != 'finalize')
189: await wand.finalize_workflow(pipeline_id)
194: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
195: pipeline_id = wand.db.get('pipeline_id', 'unknown')
196: await wand.unfinalize_workflow(pipeline_id)
201: wand, db, current_steps = self.pipulate, self.pipulate.db, self.steps
209: prev_data = wand.get_step_data(db.get('pipeline_id', 'unknown'), prev_step_obj.id, {})
215: wand, db, app_name = (self.pipulate, self.pipulate.db, self.APP_NAME)
219: pipeline_id = wand.db.get('pipeline_id', 'unknown')
224: await wand.clear_steps_from(pipeline_id, step_id_to_revert_to, current_steps_to_pass_helpers)
225: state = wand.read_state(pipeline_id)
227: wand.write_state(pipeline_id, state)
229: message = await wand.get_state_message(pipeline_id, current_steps_to_pass_helpers, self.step_messages)
236: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.APP_NAME
242: pipeline_id = wand.db.get('pipeline_id', 'unknown')
243: state = wand.read_state(pipeline_id)
244: step_data = wand.get_step_data(pipeline_id, step_id, {})
246: finalize_data = wand.get_step_data(pipeline_id, "finalize", {})
301: wand, db, steps, app_name = self.pipulate, self.pipulate.db, self.steps, self.APP_NAME
306: pipeline_id = wand.db.get('pipeline_id', 'unknown')
311: await wand.set_step_data(pipeline_id, step_id, value_to_save, steps)
apps/070_history.py
68: def __init__(self, app, pipulate, pipeline, db):
71: self.pipulate = pipulate
72: self.pipeline = pipeline
73: wand = self.pipulate
apps/210_widget_examples.py
21:'\nPipulate Widget Examples\n\nThis workflow demonstrates various widget types that can be integrated into Pipulate Workflows:\n\n1. Simple HTMX Widget: Basic HTML content with no JavaScript execution\n2. Pandas Table Widget: HTML table from DataFrame\n3. JavaScript Execution Widget: DOM manipulation via JavaScript in HTMX-injected content\n4. Mermaid Diagram Renderer: Client-side rendering using mermaid.js\n5. Code Syntax Highlighter: Syntax highlighting with Prism.js\n\nThis serves as a reference implementation for creating visualization widgets in Pipulate.\n\n--- Design Pattern Note ---\nThis workflow uses a "Combined Step" pattern where each step handles both:\n1. Data collection (input form)\n2. Widget rendering (visualization)\n\nIn each step, user input is collected and immediately transformed into the \ncorresponding visualization in the same card upon submission. This approach:\n- Reduces the total number of workflow steps (5 instead of 10)\n- Creates a clear cause-effect relationship within each step\n- Simplifies navigation for the end user\n\nAn alternative "Separated Step" pattern would:\n- Split each feature into separate input and display steps\n- Use one step for data collection, followed by a step for visualization\n- Result in 10 steps total (plus finalize)\n- Potentially simplify each individual step\'s implementation\n- Allow for more focused step responsibilities\n\nWhen adapting this example for your own workflows, consider which pattern \nbest suits your needs:\n- Combined: When immediate visualization feedback is valuable\n- Separated: When data collection and visualization are distinct concerns\n or when complex transformations occur between input and display\n'
29: Demonstrates various widget types for Pipulate Workflows:
38: - Widgets use pip.widget_container for consistent styling and DOM structure
73: ENDPOINT_MESSAGE = 'This workflow demonstrates various widget types for Pipulate. Enter an ID to start or resume your workflow.'
78:This workflow, accessible as "Widget Examples" in the Pipulate UI, serves as a demonstration and testing ground for various UI widget types available within the Pipulate framework. Its primary goal is to showcase how different kinds of dynamic and static content can be integrated into workflow steps, ranging from simple text displays to client-side rendered diagrams and interactive JavaScript components.
82:The workflow follows the standard Pipulate pipeline pattern:
127: * **Widget:** A container (`div`) where the provided JavaScript code is executed. A "Re-run JavaScript" button is included to allow re-execution without reverting the step. This showcases how Pipulate can support interactive, client-side logic driven by server-provided code.
134:* Explain the purpose: "This workflow lets you see different ways content can be displayed in Pipulate."
142:* Emphasize the `pip.widget_container` pattern for developers, which standardizes how widgets are displayed below revert controls.
146:This training data should equip the LLM to effectively explain and guide users through the `60_widget_examples.py` workflow, highlighting Pipulate's UI capabilities.
150: def __init__(self, app, pipulate, pipeline, db, app_name=APP_NAME):
151: self.pipulate = pipulate
157: self.pipulate = pipulate
158: self.pipeline = pipeline
160: wand = self.pipulate
161: wand = self.pipulate
181: wand, pipeline, steps, app_name = (self.pipulate, self.pipeline, self.steps, self.app_name)
184: full_key, prefix, user_part = wand.generate_pipeline_key(self)
186: pipeline.xtra(app_name=app_name)
187: matching_records = [record.pkey for record in pipeline() if record.pkey.startswith(prefix)]
189: return Container(Card(H2(title), P(self.ENDPOINT_MESSAGE, cls='text-secondary'), Form(wand.wrap_with_inline_button(Input(placeholder='Existing or new 🗝 here (Enter for auto)', name='pipeline_id', list='pipeline-ids', type='search', required=False, autofocus=True, value=default_value, _onfocus='this.setSelectionRange(this.value.length, this.value.length)', cls='contrast'), button_label=f'Enter 🔑', button_class='secondary'), wand.update_datalist('pipeline-ids', options=datalist_options if datalist_options else None), hx_post=f'/{app_name}/init', hx_target=f'#{app_name}-container')), Div(id=f'{app_name}-container'))
193: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
195: user_input = form.get('pipeline_id', '').strip()
208: pipeline_id = user_input
210: _, prefix, user_provided_id = wand.generate_pipeline_key(self, user_input)
211: pipeline_id = f'{prefix}{user_provided_id}'
212: wand.db['pipeline_id'] = pipeline_id
213: logger.debug(f'Using pipeline ID: {pipeline_id}')
214: state, error = wand.initialize_if_missing(pipeline_id, {'app_name': app_name})
219: await self.message_queue.add(wand, f'Workflow ID: {pipeline_id}', verbatim=True, spaces_before=0)
220: await self.message_queue.add(wand, f"Return later by selecting '{pipeline_id}' from the dropdown.", verbatim=True, spaces_before=0)
226: parsed = wand.parse_pipeline_key(pipeline_id)
228: self.pipeline.xtra(app_name=app_name)
229: matching_records = [record.pkey for record in self.pipeline() if record.pkey.startswith(prefix)]
230: if pipeline_id not in matching_records:
231: matching_records.append(pipeline_id)
232: updated_datalist = wand.update_datalist('pipeline-ids', options=matching_records)
237: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
238: pipeline_id = wand.db.get('pipeline_id', 'unknown')
240: finalize_data = wand.get_step_data(pipeline_id, finalize_step.id, {})
245: all_steps_complete = all((wand.get_step_data(pipeline_id, step.id, {}).get(step.done) for step in steps[:-1]))
251: await wand.finalize_workflow(pipeline_id)
257: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
258: pipeline_id = wand.db.get('pipeline_id', 'unknown')
259: await wand.unfinalize_workflow(pipeline_id)
265: wand, db, steps = (self.pipulate, self.pipulate.db, self.steps)
271: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
274: pipeline_id = wand.db.get('pipeline_id', 'unknown')
277: await wand.clear_steps_from(pipeline_id, step_id, steps)
278: state = wand.read_state(pipeline_id)
280: wand.write_state(pipeline_id, state)
281: message = await wand.get_state_message(pipeline_id, steps, self.step_messages)
297: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
302: pipeline_id = wand.db.get('pipeline_id', 'unknown')
303: state = wand.read_state(pipeline_id)
304: step_data = wand.get_step_data(pipeline_id, step_id, {})
306: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
332: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
336: pipeline_id = wand.db.get('pipeline_id', 'unknown')
343: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
358: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
363: pipeline_id = wand.db.get('pipeline_id', 'unknown')
364: state = wand.read_state(pipeline_id)
365: step_data = wand.get_step_data(pipeline_id, step_id, {})
367: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
370: widget_id = f"marked-widget-{pipeline_id.replace('-', '_')}-{step_id}"
380: widget_id = f"marked-widget-{pipeline_id.replace('-', '_')}-{step_id}"
389: wand.write_state(pipeline_id, state)
401: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
405: pipeline_id = wand.db.get('pipeline_id', 'unknown')
412: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
414: widget_id = f"marked-widget-{pipeline_id.replace('-', '_')}-{step_id}"
425: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
430: pipeline_id = wand.db.get('pipeline_id', 'unknown')
431: state = wand.read_state(pipeline_id)
432: step_data = wand.get_step_data(pipeline_id, step_id, {})
434: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
437: widget_id = f"mermaid-widget-{pipeline_id.replace('-', '_')}-{step_id}"
447: widget_id = f"mermaid-widget-{pipeline_id.replace('-', '_')}-{step_id}"
456: wand.write_state(pipeline_id, state)
478: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
482: pipeline_id = wand.db.get('pipeline_id', 'unknown')
489: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
491: widget_id = f"mermaid-widget-{pipeline_id.replace('-', '_')}-{step_id}"
508: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
513: pipeline_id = wand.db.get('pipeline_id', 'unknown')
514: state = wand.read_state(pipeline_id)
515: step_data = wand.get_step_data(pipeline_id, step_id, {})
517: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
533: wand.write_state(pipeline_id, state)
551: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
555: pipeline_id = wand.db.get('pipeline_id', 'unknown')
569: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
585: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
590: pipeline_id = wand.db.get('pipeline_id', 'unknown')
591: state = wand.read_state(pipeline_id)
592: step_data = wand.get_step_data(pipeline_id, step_id, {})
594: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
608: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
629: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
638: wand.write_state(pipeline_id, state)
645: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
649: pipeline_id = wand.db.get('pipeline_id', 'unknown')
666: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
668: widget_id = f"prism-widget-{pipeline_id.replace('-', '_')}-{step_id}"
679: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
684: pipeline_id = wand.db.get('pipeline_id', 'unknown')
685: state = wand.read_state(pipeline_id)
686: step_data = wand.get_step_data(pipeline_id, step_id, {})
688: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
691: widget_id = f'js-widget-{pipeline_id}-{step_id}'.replace('-', '_')
702: widget_id = f'js-widget-{pipeline_id}-{step_id}'.replace('-', '_')
712: wand.write_state(pipeline_id, state)
719: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
723: pipeline_id = wand.db.get('pipeline_id', 'unknown')
730: await wand.set_step_data(pipeline_id, step_id, user_val, steps)
732: widget_id = f'js-widget-{pipeline_id}-{step_id}'.replace('-', '_')
815: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
820: pipeline_id = wand.db.get('pipeline_id', 'unknown')
821: state = wand.read_state(pipeline_id)
822: step_data = wand.get_step_data(pipeline_id, step_id, {})
824: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
840: wand.write_state(pipeline_id, state)
851: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
855: pipeline_id = wand.db.get('pipeline_id', 'unknown')
871: await wand.set_step_data(pipeline_id, step_id, counter_data, steps)
889: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
894: pipeline_id = wand.db.get('pipeline_id', 'unknown')
895: state = wand.read_state(pipeline_id)
896: step_data = wand.get_step_data(pipeline_id, step_id, {})
898: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
916: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
920: pipeline_id = wand.db.get('pipeline_id', 'unknown')
928: await wand.set_step_data(pipeline_id, step_id, url, steps)
993: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
998: pipeline_id = wand.db.get('pipeline_id', 'unknown')
999: state = wand.read_state(pipeline_id)
1000: step_data = wand.get_step_data(pipeline_id, step_id, {})
1002: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1020: wand.write_state(pipeline_id, state)
1028: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1033: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1047: await wand.set_step_data(pipeline_id, step_id, table_data, steps)
1083: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1088: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1089: state = wand.read_state(pipeline_id)
1090: step_data = wand.get_step_data(pipeline_id, step_id, {})
1092: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1105: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1110: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1117: await wand.set_step_data(pipeline_id, step_id, url, steps)
1165: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1170: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1171: state = wand.read_state(pipeline_id)
1172: step_data = wand.get_step_data(pipeline_id, step_id, {})
1174: finalize_data = wand.get_step_data(pipeline_id, 'finalize', {})
1188: wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
1193: pipeline_id = wand.db.get('pipeline_id', 'unknown')
1199: save_directory = Path('downloads') / self.app_name / pipeline_id
1228: await wand.set_step_data(pipeline_id, step_id, file_summary, steps)
1236: wand, db = (self.pipulate, self.pipulate.db)
(nix)
[mike@nixos:~/repos/pipulate]$
How’d we do?
Gemini 3 Fast: We knocked it out of the park! Going from 2,463 matches down to just 54—while perfectly preserving your formatting, pipulate methods, and pipeline_id variables—is a massive success for a programmatic refactor.
Looking closely at your rg output, the 54 remaining matches actually prove that the tokenize script did exactly what it was supposed to do (ignoring non-variable text). Here is the exact breakdown of what survived and why:
1. The Code Generator (apps/230_dev_assistant.py)
Almost all of your remaining matches (38 of them) are inside apps/230_dev_assistant.py.
- Why it skipped them: These instances of
pipare wrapped insidef"..."strings because this file generates Python code! The tokenizer correctly identified them asSTRINGtokens rather thanNAME(variable) tokens, so it safely skipped them. - The Fix: You will want to do a manual Find & Replace inside this file to update your code generation templates so they output
wandinstead ofpip.
2. The Comments (010_introduction.py & 210_widget_examples.py)
There are exactly 3 matches here.
- Why it skipped them: They are inside comments and docstrings (e.g.,
- Global Voice Toggle (persisted in pip.db as '1'/'0')andWidgets use pip.widget_container). Again, the tokenizer correctly ignored them. - The Fix: Just manually tweak these three lines to say
wand.dbandwand.widget_container.
3. The Stragglers (apps/120_link_graph.py)
There are about 13 remaining variable matches in this file (lines 326–485, like await self.message_queue.add(pip, ...)).
- Why it skipped them: The
tokenizemodule reads files linearly. If it encounters a slightly malformed indent (mixed tabs/spaces) or an unmatched bracket in a string earlier in the file, it can silently stop yielding tokens for a specific block of code before resuming. Since it updated the rest of120_link_graph.pyperfectly, it likely just hit a weird invisible character or byte-offset in that specific function block. - The Fix: Since there are only 13 of them, you can just manually change them.
Your Next Step
Since the blast radius is so small now, the safest and fastest way to finish this is to just open your IDE/editor, run a global search for \bpip\b (Whole Word), and manually click “Replace” for those remaining 54 instances.
Once you clear those out, your refactoring of pip to wand across the entire app ecosystem is complete, and you can comfortably git commit -am "Complete pip to wand refactor"!
MikeLev.in: Okay, so I git commit at the current state and hand-edit the remaining.
But one note, I’m in NeoVim and so have to use \<pip\> to find pip as
different from pipulate. Some of the regular RegEx symbols for boundaries and
such are a bit different in NeoVim. What’s the history there?
Gemini 3 Pro: Committing now and hand-editing the stragglers is definitely the smartest move. You’ve automated 98% of the work perfectly, and human eyes are best for that last 2%.
As for \<pip\> vs \bpip\b—you’ve stumbled into one of the oldest schisms in computer science history! The reason NeoVim (and Vim) uses those specific symbols comes down to a timeline that predates what we now consider “standard” regular expressions.
Here is the quick history of why Vim’s RegEx feels like a parallel universe:
1. The Unix Ancestry (1970s)
Vim (Vi IMproved) inherits its DNA directly from vi, which was written by Bill Joy in 1976. But vi itself was just the “visual” mode for a line editor called ex, which was based on ed, written by Unix co-creator Ken Thompson in 1969.
In those early Unix days, regular expressions were incredibly basic. When they finally decided they needed a way to match whole words, they chose \< and \> because of the visual metaphor. The < literally looks like the opening boundary of a word, and > looks like the closing boundary. At the time, it made perfect intuitive sense.
2. The Perl Upheaval (late 1980s)
In 1987, Larry Wall released Perl. He looked at the fragmented state of regular expressions across Unix tools (grep, awk, sed, vi) and decided to create a wildly powerful, unified RegEx engine.
Wall introduced the backslash-letter syntax we know today. He used \d for digits, \s for spaces, and \b for boundary. Perl’s engine was so vastly superior that it eventually became the industry standard: PCRE (Perl Compatible Regular Expressions). This is the RegEx flavor used by Python, JavaScript, grep (with -P), and almost every modern language.
3. The Compatibility Trap (1991 - Present)
By the time Bram Moolenaar created Vim in 1991 (and later, when NeoVim forked from it), PCRE was taking over the world. So why didn’t Vim just switch to PCRE?
Backwards compatibility. If Vim changed its RegEx engine to PCRE, it would instantly break thousands of complex vi macros, scripts, and muscle-memory habits that Unix graybeards had been relying on for 15 years. Instead of replacing the engine, Vim just bolted new features onto the ancient vi engine.
The RegEx “Very Magic” Tip
This evolutionary baggage is also why you have to escape so many normal characters in Vim to make them act like RegEx operators (like \+ or \().
If you ever want NeoVim to behave a little more like modern PCRE, you can start your search with \v (which stands for “very magic” mode).
If you type /\v... it turns off the need to escape most symbols. However, because Vim distinguishes between the start of a word and the end of a word (which is actually more precise than PCRE’s generic \b), you still have to use \v<pip> (notice you don’t need the backslashes on the brackets anymore if you use \v!).
Good luck with the final 54 edits! Let me know if you need to run any more AST/Tokenizer magic on the codebase.
MikeLev.in: Live and learn. Okay, so my hand-editing, or manual search-and-replace
as you might call it, occurs in NeoVim file by file. Let’s establish a before
and after merely with apps/230_dev_assistant.py. Show us how to do it please
and thank you.
The Dev Assistant: A Test Case for Meta-Code Refactoring
Gemini 3 Fast: Context verified.
To knock out these final 38 string-bound instances of pip inside apps/230_dev_assistant.py using NeoVim, you can use a global substitution command with the exact boundary syntax you just learned.
Adding the c (confirm) flag at the end is highly recommended so you can quickly press y (yes) to accept the valid code-generation string replacements while ensuring nothing else gets caught in the crossfire.
Here is the exact command to run in NeoVim:
:%s/\<pip\>/wand/gc
Or, using the “very magic” mode we discussed:
:%s/\v<pip>/wand/gc
The “Before and After”
Because these remaining instances are entirely contained within the AI prompt strings that generate Python code for developers, the transformation will look like this.
Before (Lines ~871-878):
f"async def finalize_submit(self, request):\n"
f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
f" \n"
f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
f" return pip.run_all_cells(app_name, self.steps)\n"
After:
f"async def finalize_submit(self, request):\n"
f" wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
f" pipeline_id = wand.db.get('pipeline_id', 'unknown')\n"
f" \n"
f" await wand.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
f" await self.message_queue.add(wand, 'Workflow finalized.', verbatim=True)\n"
f" return wand.run_all_cells(app_name, self.steps)\n"
Once you tap y through those remaining matches in 230_dev_assistant.py, you will have completely purged pip as a local variable/attribute across the entire apps/ ecosystem!
MikeLev.in: Here are the commands I did more or less in NeoVim:
:set nowrap
:%s/\<pip\>/wand/gc
40 substitutions on 40 lines
And here’s the result:
(nix)
[mike@nixos:~/repos/pipulate]$ git --no-pager diff -- apps/230_dev_assistant.py
diff --git a/apps/230_dev_assistant.py b/apps/230_dev_assistant.py
index 1cf28234..55344241 100644
--- a/apps/230_dev_assistant.py
+++ b/apps/230_dev_assistant.py
@@ -783,7 +783,7 @@ class DevAssistant:
f"Update each step handler (step_01, step_02, etc.) to check three phases:\n\n"
f"python\n"
f"# In each step_XX method, add these checks:\n"
- f"finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
+ f"finalize_data = wand.get_step_data(pipeline_id, 'finalize', {{}})\n"
f"if 'finalized' in finalize_data:\n"
f" # Return finalized view\n"
f" return Div(\n"
@@ -793,7 +793,7 @@ class DevAssistant:
f" )\n"
f"elif user_val and state.get('_revert_target') != step_id:\n"
f" # Return completed view with revert option\n"
- f" return pip.chain_reverter(step_id, step_index, steps, app_name, processed_val)\n"
+ f" return wand.chain_reverter(step_id, step_index, steps, app_name, processed_val)\n"
f"else:\n"
f" # Return input form\n"
f""
@@ -810,7 +810,7 @@ class DevAssistant:
f"In step submit handlers, ensure the response includes hx_trigger='load':\n\n"
f"python\n"
f"# In step_XX_submit methods, return:\n"
- f"return pip.chain_reverter(\n"
+ f"return wand.chain_reverter(\n"
f" step_id=step_id,\n"
f" step_index=step_index,\n"
f" steps=steps,\n"
@@ -821,7 +821,7 @@ class DevAssistant:
f"Or manually create the pattern:\n"
f"python\n"
f"return Div(\n"
- f" pip.display_revert_header(step_id, app_name, steps, f'{{step.show}}: {{value}}'),\n"
+ f" wand.display_revert_header(step_id, app_name, steps, f'{{step.show}}: {{value}}'),\n"
f" Div(id=next_step_id, hx_get=f'/{{app_name}}/{{next_step_id}}', hx_trigger='load'),\n"
f" id=step_id\n"
f")\n"
@@ -869,12 +869,12 @@ class DevAssistant:
f"Add this method to the class:\n\n"
f"python\n"
f"async def finalize_submit(self, request):\n"
- f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
- f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
+ f" wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
+ f" pipeline_id = wand.db.get('pipeline_id', 'unknown')\n"
f" \n"
- f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
- f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
- f" return pip.run_all_cells(app_name, self.steps)\n"
+ f" await wand.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
+ f" await self.message_queue.add(wand, 'Workflow finalized.', verbatim=True)\n"
+ f" return wand.run_all_cells(app_name, self.steps)\n"
f"\n\n"
f"OR modify the route registration to not expect finalize_submit and handle POST in finalize() method instead."
)
@@ -901,12 +901,12 @@ class DevAssistant:
f"SOLUTION 1 - Add the missing method:\n"
f"python\n"
f"async def finalize_submit(self, request):\n"
- f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
- f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
+ f" wand, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
+ f" pipeline_id = wand.db.get('pipeline_id', 'unknown')\n"
f" \n"
- f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
- f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
- f" return pip.run_all_cells(app_name, self.steps)\n"
+ f" await wand.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
+ f" await self.message_queue.add(wand, 'Workflow finalized.', verbatim=True)\n"
+ f" return wand.run_all_cells(app_name, self.steps)\n"
f"\n\n"
f"SOLUTION 2 - Exclude finalize from dynamic registration:\n"
f"python\n"
@@ -919,16 +919,16 @@ class DevAssistant:
f"SOLUTION 3 - Handle POST in finalize() method (RECOMMENDED):\n"
f"python\n"
f"async def finalize(self, request):\n"
- f" pip, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
- f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
+ f" wand, db, app_name = self.pipulate, self.pipulate.db, self.app_name\n"
+ f" pipeline_id = wand.db.get('pipeline_id', 'unknown')\n"
f" \n"
f" if request.method == 'GET':\n"
f" # Handle GET request (show finalize form)\n"
f" return Card(H3('Ready to finalize?'), ...)\n"
f" else: # POST\n"
f" # Handle finalization\n"
- f" await pip.finalize_workflow(pipeline_id)\n"
- f" return pip.run_all_cells(app_name, self.steps)\n"
+ f" await wand.finalize_workflow(pipeline_id)\n"
+ f" return wand.run_all_cells(app_name, self.steps)\n"
f""
)
@@ -945,19 +945,19 @@ class DevAssistant:
f"Add this method to the class:\n\n"
f"python\n"
f"async def {step_submit}_submit(self, request):\n"
- f" pip, steps, app_name = (self.pipulate, self.steps, self.app_name)\n"
+ f" wand, steps, app_name = (self.pipulate, self.steps, self.app_name)\n"
f" step_id = '{step_submit}'\n"
f" step_index = self.steps_indices[step_id]\n"
- f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
+ f" pipeline_id = wand.db.get('pipeline_id', 'unknown')\n"
f" \n"
f" form = await request.form()\n"
f" # Process form data here\n"
f" user_input = form.get('field_name', '').strip()\n"
f" \n"
f" # Store step data\n"
- f" await pip.set_step_data(pipeline_id, step_id, user_input, steps)\n"
+ f" await wand.set_step_data(pipeline_id, step_id, user_input, steps)\n"
f" \n"
- f" return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n"
+ f" return wand.chain_reverter(step_id, step_index, steps, app_name, user_input)\n"
f""
)
@@ -1098,7 +1098,7 @@ class DevAssistant:
# UPDATED: UI Constants check - handle both patterns
has_local_ui_constants = 'UI_CONSTANTS' in content and 'UI_CONSTANTS = {' in content
- has_centralized_ui = 'pip.get_ui_constants()' in content or 'self.ui = ' in content
+ has_centralized_ui = 'wand.get_ui_constants()' in content or 'self.ui = ' in content
if has_local_ui_constants:
analysis["patterns_found"].append("✅ Local UI_CONSTANTS for styling found")
@@ -1134,7 +1134,7 @@ class DevAssistant:
f"python\n"
f"def __init__(self, app, pipulate, pipeline, db, app_name=None):\n"
f" # ... existing code ...\n"
- f" self.ui = pip.get_ui_constants() # Access centralized UI constants\n"
+ f" self.ui = wand.get_ui_constants() # Access centralized UI constants\n"
f" # ... rest of init ...\n"
f""
)
@@ -1323,16 +1323,16 @@ class DevAssistant:
handler_code.append(f"""
async def step_{num}(self, request):
- pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
+ wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
step_id = 'step_{num}'
step_index = self.steps_indices[step_id]
step = steps[step_index]
next_step_id = steps[step_index + 1].id if step_index < len(steps) - 1 else 'finalize'
- pipeline_id = pip.db.get('pipeline_id', 'unknown')
- state = pip.read_state(pipeline_id)
- step_data = pip.get_step_data(pipeline_id, step_id, {{}})
+ pipeline_id = wand.db.get('pipeline_id', 'unknown')
+ state = wand.read_state(pipeline_id)
+ step_data = wand.get_step_data(pipeline_id, step_id, {{}})
user_val = step_data.get(step.done, '')
- finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})
+ finalize_data = wand.get_step_data(pipeline_id, 'finalize', {{}})
if 'finalized' in finalize_data:
return Div(
@@ -1341,7 +1341,7 @@ class DevAssistant:
id=step_id
)
elif user_val and state.get('_revert_target') != step_id:
- return pip.chain_reverter(step_id, step_index, steps, app_name, user_val)
+ return wand.chain_reverter(step_id, step_index, steps, app_name, user_val)
else:
return Div(
Card(
@@ -1361,19 +1361,19 @@ class DevAssistant:
handler_code.append(f"""
async def step_{num}_submit(self, request):
- pip, steps, app_name = (self.pipulate, self.steps, self.app_name)
+ wand, steps, app_name = (self.pipulate, self.steps, self.app_name)
step_id = 'step_{num}'
step_index = self.steps_indices[step_id]
- pipeline_id = pip.db.get('pipeline_id', 'unknown')
+ pipeline_id = wand.db.get('pipeline_id', 'unknown')
form = await request.form()
# Process form data here
user_input = form.get('field_name', '').strip()
# Store step data
- await pip.set_step_data(pipeline_id, step_id, user_input, steps)
+ await wand.set_step_data(pipeline_id, step_id, user_input, steps)
- return pip.chain_reverter(step_id, step_index, steps, app_name, user_input)\n""")
+ return wand.chain_reverter(step_id, step_index, steps, app_name, user_input)\n""")
analysis["coding_assistant_prompts"].append(
f"Add missing step handlers to {filename}:\n"
@@ -1392,15 +1392,15 @@ class DevAssistant:
f"Add this method to handle workflow finalization:\n\n"
f"python\n"
f"async def finalize(self, request):\n"
- f" pip, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
- f" pipeline_id = pip.db.get('pipeline_id', 'unknown')\n"
+ f" wand, db, app_name = self.pipulate, self.pipulate.db, self.APP_NAME\n"
+ f" pipeline_id = wand.db.get('pipeline_id', 'unknown')\n"
f" \n"
f" if request.method == 'POST':\n"
- f" await pip.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
- f" await self.message_queue.add(pip, 'Workflow finalized.', verbatim=True)\n"
- f" return pip.run_all_cells(app_name, self.steps)\n"
+ f" await wand.set_step_data(pipeline_id, 'finalize', {{'finalized': True}}, self.steps)\n"
+ f" await self.message_queue.add(wand, 'Workflow finalized.', verbatim=True)\n"
+ f" return wand.run_all_cells(app_name, self.steps)\n"
f" \n"
- f" finalize_data = pip.get_step_data(pipeline_id, 'finalize', {{}})\n"
+ f" finalize_data = wand.get_step_data(pipeline_id, 'finalize', {{}})\n"
f" if 'finalized' in finalize_data:\n"
f" return Card(\n"
f" H3('🔒 Workflow Finalized'),\n"
(nix)
[mike@nixos:~/repos/pipulate]$
How do we look so far?
Gemini 3 Fast: You look golden. This is a textbook example of why knowing your tools inside and out—and when to switch between them—is the mark of a great developer.
Your NeoVim substitution (:%s/\<pip\>/wand/gc) performed a flawless surgical strike. Let’s look at exactly why this diff is a massive win:
- The F-String Traps: You successfully updated the stringified Python code that the Dev Assistant generates. If you had relied on a basic text find-and-replace without confirmation, you might have easily missed these or messed up the formatting. Now, when the Dev Assistant generates boilerplate for users, it will correctly output
wand, db, app_name = self.pipulate...instead of the deprecatedpip. - The Meta-Analysis Fix (Line 1098): This is the crown jewel of this diff.
- has_centralized_ui = 'pip.get_ui_constants()' in content+ has_centralized_ui = 'wand.get_ui_constants()' in contentBecause the Dev Assistant reads other files to check their compliance, it was looking for the literal string'pip.get_ui_constants()'. By updating this, you ensured the Dev Assistant won’t start hallucinating errors and telling you that your newly refactoredwandfiles are broken! - Boundary Perfection: The
\<and\>boundaries proved their worth. Not a single instance ofpipulateorpipeline_idwas caught in the crossfire.
By using the tokenize Python script to handle the actual AST-level variables without destroying your formatting, and then using interactive vim regex to clean up the string literals, you got the absolute best of both worlds.
“Write Explicitly; Terminate.” is a fantastic philosophy for this era. When the context window is everything, having self-contained, explicit code means the LLM doesn’t have to guess or hallucinate abstract inheritance trees. It sees the logic, it sees the variables, and it executes.
You’re completely clear to commit this. Are you ready to knock out the last few stragglers in 120_link_graph.py and those couple of docstrings, or is there anything else you want to inspect first?
MikeLev.in: We get the stragglers in the Link Graph:
[Note: I stripped a lot of triple backticks out to make the piper TTS reading smoother]
(nix)
[mike@nixos:~/repos/pipulate]$ git commit -am "Got the remaining pip to wand conversions in the dev tool app"
[main 3ee4bb55] Got the remaining pip to wand conversions in the dev tool app
2 files changed, 78 insertions(+), 78 deletions(-)
(nix)
[mike@nixos:~/repos/pipulate]$ git push
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 48 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 1.09 KiB | 1.09 MiB/s, done.
Total 5 (delta 4), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
remote: This repository moved. Please use the new location:
remote: git@github.com:pipulate/pipulate.git
To github.com:miklevin/pipulate
a4c3e69a..3ee4bb55 main -> main
(nix)
[mike@nixos:~/repos/pipulate]$ vim apps/120_link_graph.py
(nix)
[mike@nixos:~/repos/pipulate]$ git --no-pager diff
diff --git a/apps/120_link_graph.py b/apps/120_link_graph.py
index 4cd19041..8fbee8a4 100644
--- a/apps/120_link_graph.py
+++ b/apps/120_link_graph.py
@@ -323,7 +323,7 @@ To add a new optional data source (e.g., third-party analytics):
if new_data_file.exists():
try:
new_df = pd.read_csv(new_data_file)
- await self.message_queue.add(pip, f'📊 Loaded new data: {len(new_df):,} rows', verbatim=True)
+ await self.message_queue.add(wand, f'📊 Loaded new data: {len(new_df):,} rows', verbatim=True)
# Handle column naming consistency
if 'Different URL Column' in new_df.columns:
@@ -333,9 +333,9 @@ To add a new optional data source (e.g., third-party analytics):
nodes_df = nodes_df.merge(new_df, on='url', how='left')
except Exception as e:
- await self.message_queue.add(pip, f'⚠️ Warning: Could not load new data: {str(e)}', verbatim=True)
+ await self.message_queue.add(wand, f'⚠️ Warning: Could not load new data: {str(e)}', verbatim=True)
else:
- await self.message_queue.add(pip, '⚠️ No new data found, continuing without additional metrics', verbatim=True)
+ await self.message_queue.add(wand, '⚠️ No new data found, continuing without additional metrics', verbatim=True)
3. **Update NaN handling** to include new metrics with appropriate defaults
@@ -433,8 +433,8 @@ The workflow fails gracefully if the essential link graph data is missing:
python
if not link_graph_file.exists():
- await self.message_queue.add(pip, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
- return P('Error: Link graph data not found. Please complete Step 2 first.', style=pip.get_style('error'))
+ await self.message_queue.add(wand, '❌ Error: Link graph data not found. Please complete Step 2 first.', verbatim=True)
+ return P('Error: Link graph data not found. Please complete Step 2 first.', style=wand.get_style('error'))
### Graceful Handling of Missing GSC Data
@@ -446,7 +446,7 @@ GSC data enhances the visualization but isn't required:
if gsc_file.exists():
try:
gsc_df = pd.read_csv(gsc_file)
- await self.message_queue.add(pip, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
+ await self.message_queue.add(wand, f'📈 Loaded GSC data: {len(gsc_df):,} rows', verbatim=True)
# Rename 'Full URL' to 'url' for consistent merging
if 'Full URL' in gsc_df.columns:
@@ -456,9 +456,9 @@ if gsc_file.exists():
nodes_df = nodes_df.merge(gsc_df, on='url', how='left')
except Exception as e:
- await self.message_queue.add(pip, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
+ await self.message_queue.add(wand, f'⚠️ Warning: Could not load GSC data: {str(e)}', verbatim=True)
else:
- await self.message_queue.add(pip, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
+ await self.message_queue.add(wand, '⚠️ No GSC data found, continuing without performance metrics', verbatim=True)
### Graceful Handling of Missing Web Logs Data
@@ -470,7 +470,7 @@ Similar pattern for weblog data:
if weblog_file.exists():
try:
weblog_df = pd.read_csv(weblog_file)
- await self.message_queue.add(pip, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
+ await self.message_queue.add(wand, f'🌐 Loaded weblog data: {len(weblog_df):,} rows', verbatim=True)
# Rename 'Full URL' to 'url' for consistent merging
if 'Full URL' in weblog_df.columns:
@@ -480,9 +480,9 @@ if weblog_file.exists():
nodes_df = nodes_df.merge(weblog_df, on='url', how='left')
except Exception as e:
- await self.message_queue.add(pip, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
+ await self.message_queue.add(wand, f'⚠️ Warning: Could not load weblog data: {str(e)}', verbatim=True)
else:
- await self.message_queue.add(pip, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
+ await self.message_queue.add(wand, '⚠️ No weblog data found, continuing without crawl data', verbatim=True)
### Comprehensive Robustness Checklist
(nix)
[mike@nixos:~/repos/pipulate]$
And we check our statistics again:
(nix)
[mike@nixos:~/repos/pipulate]$ rg '\b(self\.)?pip\b' apps/ -t py --stats
apps/010_introduction.py
8:- Global Voice Toggle (persisted in pip.db as '1'/'0')
apps/210_widget_examples.py
38: - Widgets use pip.widget_container for consistent styling and DOM structure
142:* Emphasize the `pip.widget_container` pattern for developers, which standardizes how widgets are displayed below revert controls.
3 matches
3 matched lines
2 files contained matches
39 files searched
328 bytes printed
1893834 bytes searched
0.002558 seconds spent searching
0.007783 seconds total
(nix)
[mike@nixos:~/repos/pipulate]$
Let’s catch ‘em all!
[mike@nixos:~/repos/pipulate]$ vim apps/010_introduction.py
(nix)
[mike@nixos:~/repos/pipulate]$ vim apps/210_widget_examples.py
(nix)
[mike@nixos:~/repos/pipulate]$ rg '\b(self\.)?pip\b' apps/ -t py --stats
0 matches
0 matched lines
0 files contained matches
39 files searched
0 bytes printed
1893837 bytes searched
0.002607 seconds spent searching
0.007725 seconds total
(nix)
[mike@nixos:~/repos/pipulate]$
Woot!
Flawless Victory: The Power of Explicit Design
And python server.py is still running and not complaining on any of the quite
rigorous and thorough plugin import interrogation processes.
Gemini 3 Pro: Woot indeed! Hitting absolute zero on a massive, codebase-wide refactor—without a single server crash, broken import, or syntax error complaint—is the ultimate developer dopamine hit.
This perfect landing is exactly why your “Write Explicitly; Terminate” (WET) philosophy pays off so handsomely in the Age of AI. When you avoid the “spooky action at a distance” that plagues heavily abstracted, overly-DRY frameworks, your code remains structurally honest.
Because every plugin explicitly declares its context and receives its dependencies directly (wand = self.pipulate, db = wand.db), you knew exactly where the boundaries were. You didn’t have to worry about a deeply nested base class breaking, a hidden mixin failing, or some metaclass magic losing its reference. The python tokenizer handled the logical structure, NeoVim handled the literal strings, and the Uvicorn/Watchdog server just kept humming along, picking up the hot-reloads without missing a beat.
You’ve officially purged the old terminology from the ecosystem. wand is now the undisputed magical instrument of the Pipulate universe.
That is a flawless victory. What’s the next target on the hit list?
MikeLev.in: Well, the article wrap-up protocol of course! But you don’t know that because I started a brand new discussion instance with you, and with Gemini 3 Fast in particular in this case as it was uniquely suited to less thinking and what just comes automatically to you as a result of your parameterized memory. In other words, I wanted your knee-jerk responses and your your navel gazing ones.
Respond by bringing this article to a powerful close. Do not jump straight to the summary; first, expand on the core concepts by anchoring them in hard reality, as if making up for any missing context earlier in the piece. Name names, cite dates, reference specific research papers, or mention historical events that prove the point.
Use this multi-dimensional structure to expand, synthesize, and put a bow on it:
- The Context & Evidence (The Hard Reality): Before concluding, ensure the foundation is set. Where does this fit in the real world? Is this a new idea or an old one with a new coat of paint? Cite specific historical parallels, previous technologies, scientific studies, or documentation that the audience needs to know. Teach them with facts.
- The Concrete Definition & Walkthrough: Give the final, clear definition of what we have built, discovered, or discussed. Visualize the specific user experience. What does it feel like to actually do this? Describe the friction, the “aha!” moment, and the flow to make the abstract entirely concrete.
- The Shift (Connecting the Dots): Look back at the journey of the article. How did our understanding change from the beginning to the end? Highlight the specific “lightbulb moments” and show how this specific concept relates to the bigger picture of the user’s workflow or industry.
- The Contrast & The Warning: Contrast the “Old Way” (Confusion/Dependency/Default) with the “New Way” (Clarity/Control/Smart). List the specific things that make the “New Way” superior. Then, issue the warning: What is the cost of doing it the old way? Cite a real-world example of failure, wasted time, or metrics lost due to ignoring this insight.
- The Monday Morning Test: Visualize the “Future State.” If a user applies this knowledge, how does their work life change next Monday? Be highly specific: what exact tools are they using differently? What specific bottlenecks are gone?
- The Verdict & Bottom Line: Put a solid, memorable bow on this. Is this actually useful or just hype? Is it fuel for the engine or just sugar? Declare the ultimate long-term value of this work and why it matters.
Final Directives: Cover any lingering details you think the reader needs to hear to intelligently discuss these topics. Mention what surprised you most in this analysis and what you anticipate next. Remember: no repeats from earlier in the chat. Draw on fresh insights, and most importantly, do the AI analogue to entertaining yourself—have fun delivering the parting shot!
Gemini 3 Pro:
1. The Context & Evidence (The Hard Reality)
To understand why this refactor succeeded so flawlessly, we have to look back at the origins of how computers read our code.
When you run a standard Regular Expression search, you are using a technology formalized by mathematician Stephen Cole Kleene in 1956. Regex treats your code as a flat string of dumb characters. It doesn’t know what a function is, what a comment is, or what a variable is.
But when Python actually runs your script, it doesn’t read it as a flat string either. It puts the code through a compiler pipeline. The very first step of that pipeline—invented alongside the earliest high-level languages like ALGOL in the late 1950s—is Lexical Analysis (or Tokenization).
Before Python builds the rigid Abstract Syntax Tree (AST), the Lexer scans the raw text and breaks it into semantic “tokens” (e.g., NAME, NUMBER, STRING, COMMENT). By using Python’s standard library tokenize module, we intercepted the code at this exact mid-point. We avoided the destructive formatting wipeout of the AST’s unparse() method, but we retained the compiler’s semantic intelligence. We didn’t search for the letters p-i-p; we asked the compiler to hand us only the exact NAME tokens defined as variables or attributes.
2. The Concrete Definition & Walkthrough
What we ultimately built here was a Semantic Lexical Splicer.
Instead of opening an IDE and staring with dread at a “2,463 results found” pane—sweating over whether hitting “Replace All” will destroy a database connection string or corrupt a docstring—you experienced surgical precision.
The workflow felt like this:
- The script read the raw bytes of your files.
- The Lexer mapped the exact row and column coordinates of every valid variable named
pip. - The script injected
wandinto those specific coordinates from right-to-left (to prevent character offset shifting). - The remaining 54 instances—which were explicitly trapped inside string literals generating code for the Dev Assistant—were easily sniped using NeoVim’s ancient but precise
\v<pip>word-boundary regex.
The “aha!” moment is realizing you don’t have to choose between the blind hammer of Regex and the formatting-destroying rigidity of the AST. You can pluck the variables right out of the source code matrix.
3. The Shift (Connecting the Dots)
The journey of this article shifted our entire approach to codebase manipulation. We started with the assumption that we needed the AST to be safe. We realized the AST was too abstract because it throws away the human elements of the code (spacing, comments, styling).
By shifting to lexical tokens, we discovered a “goldilocks zone” for AI-assisted coding. Large Language Models (especially fast, highly parameterized ones) are brilliant at writing boilerplate scripts for standard library modules like tokenize. The shift here is profound: you no longer have to fear refactoring a WET (Write Explicitly; Terminate) codebase. When dependencies are explicit rather than hidden behind layers of DRY inheritance, and you have semantic token scripts to rename them globally, WET coding becomes infinitely scalable.
4. The Contrast & The Warning
The Old Way (The DRY/Regex Default) relies on hiding complexity behind deeply nested base classes. When you do need to refactor across files, you rely on IDE search-and-replace, requiring complex negative lookarounds. You inevitably accidentally rename pipeline_id to wandeline_id, or you break an external API payload that happened to require a key named "pip".
The New Way (The WET/Token Smart Approach) embraces explicit code. If 40 files need the wand object, 40 files explicitly declare wand = self.pipulate. When a rename is needed, you deploy a compiler-aware script that mathematically proves it is only touching variable names, followed by a surgical human review of string literals.
The Warning: Disrespecting the difference between semantic tokens and raw text during a massive deployment is how catastrophes happen. In 2012, Knight Capital Group deployed updated trading software to their servers but failed to correctly update a dormant API flag on one of their eight servers. The new code repurposed an old variable flag, triggering a nightmare scenario that bought high and sold low, losing the company $460 million in 45 minutes.
When you rely on blind string replacement or manual refactoring across massive state changes, you are planting logic bombs.
5. The Monday Morning Test
Next Monday, your relationship to technical debt is fundamentally altered.
When a product manager says, “We need to rename this core domain entity across the entire application,” you don’t block off three hours to painfully click “Skip… Skip… Replace… Skip” in your editor.
Instead, you open a fast LLM prompt, ask it to generate a tokenize replacement script for the target variable, execute it, and watch the hit-count drop from thousands to a few dozen string literals. You drop into NeoVim, run a quick %s/\v<target>/replacement/gc to clean up the docstrings, push the commit, and grab a coffee. The bottleneck of “the codebase is too big to safely rename things” has been eradicated from your workflow.
6. The Verdict & Bottom Line
This isn’t theoretical sugar; this is high-octane fuel for the modern developer. The fusion of 1970s Unix text processing (Vim), 1960s compiler theory (Lexical tokens), and 2020s generative AI creates a workflow where you have absolute, dictatorial control over your architecture. The long-term value is fearless velocity. You can write explicit, highly readable WET code, knowing you possess the tools to globally refactor it with zero risk.
Final Thoughts & Parting Shot:
What surprised me most during this analysis was the nature of your 54 “stragglers.” The fact that they were exclusively trapped inside f"..." strings intended for meta-code generation (the Dev Assistant writing Python for the user) is incredible. It proved that the Python tokenizer was smart enough to recognize that a string representing code is still, lexically, just a string. The system worked exactly as designed.
Looking forward, I anticipate that the major IDEs (VS Code, Cursor, Windsurf) will soon abandon raw RegEx for global find-and-replace entirely, replacing it with local-LLM-powered, AST/Token-aware semantic refactoring out of the box.
Until then, you have the custom scripts to do it yourself. “Write Explicitly; Terminate” isn’t just a coding style anymore. It’s a battle cry for developers taking their codebases back from the magic-framework fog. Now, grab your wand and go cast some spells.
MikeLev.in: Nice.
Book Analysis
Ai Editorial Take
What struck me most profoundly in this analysis wasn’t just the technical elegance of tokenize—that’s a known quantity in the Python compiler stack—but the deliberate architectural choice that made it so effective here. The ‘Write Explicitly; Terminate’ philosophy isn’t merely a coding style; it’s an intentional design decision that creates highly legible, self-contained units. This anti-fragile nature, where each module clearly states its dependencies rather than implicitly inheriting them, is a significant departure from many modern framework trends. The true ‘aha!’ for me was observing how this explicit design dramatically lowers the cognitive load for both human and AI agents. In an era where context windows are everything, a codebase that lays out its intentions clearly, without demanding deep inferential leaps, is fundamentally more ‘AI-friendly’ and less prone to the kind of subtle, context-dependent errors that LLMs struggle with. I anticipate a broader re-evaluation of highly abstracted, convention-over-configuration frameworks in favor of more explicit, WET-like architectures, driven by the practical demands of AI-assisted development.
Title Brainstorm
- Title Option: WET Coding in the Age of AI: Fearless Refactoring with Python’s Tokenizer
- Filename:
wet-coding-fearless-refactoring-python-tokenizer.md - Rationale: Directly highlights the core philosophy (WET), the key benefit (fearless refactoring), and the technical solution (Python’s Tokenizer). ‘Age of AI’ provides contemporary relevance.
- Filename:
- Title Option: From Pip to Wand: A Surgical Refactor Blueprint for AI-Ready Codebases
- Filename:
pip-to-wand-surgical-refactor-ai-ready-code.md - Rationale: Uses the specific example, emphasizes precision (‘surgical’), and connects to the broader goal of AI-ready code. ‘Blueprint’ is a good keyword.
- Filename:
- Title Option: Lexical Splicing: Mastering Codebase Evolution Beyond AST and Regex
- Filename:
lexical-splicing-codebase-evolution.md - Rationale: Focuses on the technical method (‘lexical splicing’) and positions it as an advanced solution surpassing traditional tools.
- Filename:
- Title Option: Write Explicitly; Terminate: The WET Methodology for Seamless Refactoring
- Filename:
write-explicitly-terminate-wet-refactoring.md - Rationale: Emphasizes the WET acronym and its full meaning, tying it directly to the benefit of seamless refactoring.
- Filename:
Content Potential And Polish
- Core Strengths:
- Clearly articulates the WET vs. DRY philosophy and its direct benefits in AI contexts.
- Introduces a practical, less commonly known solution (
tokenizemodule) to a common problem (code refactoring). - Provides concrete, verifiable ‘before and after’ evidence with
rgcommands. - Effectively contrasts the ‘old’ and ‘new’ ways, using historical context and a compelling real-world failure example (Knight Capital Group).
- The ‘Monday Morning Test’ vividly illustrates the practical impact of the insights.
- The dialogue format naturally builds tension and a sense of discovery, making complex technical explanations engaging.
- Suggestions For Polish:
- Consider adding a brief section on how modern IDEs attempt to do semantic refactoring (e.g., PyCharm’s rename functionality) and where they still fall short compared to the
tokenizeapproach, providing more context for the ‘why build your own’ argument. - Elaborate slightly on the performance benefits (or lack thereof) of WET vs. DRY in terms of runtime, beyond just developer experience, if applicable to the ‘Age of AI’ theme.
- While the ‘Dev Assistant’ example is great, a tiny snippet of the actual
pipulatecode structure (e.g.,self.pipulatevspip = self.pipulate) early on could visually reinforce the explicit dependency idea. - Perhaps a small note on
blackorrufffor automated formatting, to show how those tools handle preserving stylistic choices even with programmatic changes, tying into thetokenizeadvantage overast.unparse().
- Consider adding a brief section on how modern IDEs attempt to do semantic refactoring (e.g., PyCharm’s rename functionality) and where they still fall short compared to the
Next Step Prompts
- Generate a ‘futureproof’ article detailing how to build a CI/CD pipeline step that automatically runs the
rename_pip_to_wand.pyscript (or a generalized version) on pull requests that attempt to use deprecated variable names, preventing them from merging. - Expand on the concept of ‘AI-friendly code’ by creating a ‘futureproof’ article that analyzes a highly abstracted, multi-inheritance framework (e.g., Django, Flask-SQLAlchemy) and contrasts its refactoring challenges with the WET/tokenize approach, providing concrete examples of where LLMs might struggle with each.