77"""
88
99from __future__ import print_function
10- from IPython .core .magic import Magics
10+ from IPython .core .magic import Magics , line_cell_magic
1111from string import Template
12+ import argparse
1213
1314class BaseStackqlMagic (Magics ):
1415 """Base Jupyter magic extension enabling running StackQL queries.
@@ -26,6 +27,7 @@ def __init__(self, shell, server_mode):
2627 from ..core import StackQL
2728 super (BaseStackqlMagic , self ).__init__ (shell )
2829 self .stackql_instance = StackQL (server_mode = server_mode , output = 'pandas' )
30+ self .server_mode = server_mode
2931
3032 def get_rendered_query (self , data ):
3133 """Substitute placeholders in a query template with variables from the current namespace.
@@ -52,10 +54,53 @@ def run_query(self, query):
5254
5355 return self .stackql_instance .execute (query )
5456
57+ @line_cell_magic
58+ def stackql (self , line , cell = None ):
59+ """A Jupyter magic command to run StackQL queries.
60+
61+ Can be used as both line and cell magic:
62+ - As a line magic: `%stackql QUERY`
63+ - As a cell magic: `%%stackql [OPTIONS]` followed by the QUERY in the next line.
64+
65+ :param line: The arguments and/or StackQL query when used as line magic.
66+ :param cell: The StackQL query when used as cell magic.
67+ :return: StackQL query results as a named Pandas DataFrame (`stackql_df`).
68+ """
69+ is_cell_magic = cell is not None
70+
71+ if is_cell_magic :
72+ parser = argparse .ArgumentParser ()
73+ parser .add_argument ("--no-display" , action = "store_true" , help = "Suppress result display." )
74+ parser .add_argument ("--csv-download" , action = "store_true" , help = "Add CSV download link to output." )
75+ args = parser .parse_args (line .split ())
76+ query_to_run = self .get_rendered_query (cell )
77+ else :
78+ args = None
79+ query_to_run = self .get_rendered_query (line )
80+
81+ results = self .run_query (query_to_run )
82+ self .shell .user_ns ['stackql_df' ] = results
83+
84+ if is_cell_magic and args and args .no_display :
85+ return None
86+ elif is_cell_magic and args and args .csv_download and not args .no_display :
87+ # First display the DataFrame
88+ import IPython .display
89+ IPython .display .display (results )
90+ # Then add the download button without displaying the DataFrame again
91+ self ._display_with_csv_download (results )
92+ return results
93+ elif is_cell_magic and args and not args .no_display :
94+ return results
95+ elif not is_cell_magic :
96+ return results
97+ else :
98+ return results
99+
55100 def _display_with_csv_download (self , df ):
56- """Display DataFrame with CSV download link.
101+ """Display a CSV download link for the DataFrame without displaying the DataFrame again .
57102
58- :param df: The DataFrame to display and make downloadable.
103+ :param df: The DataFrame to make downloadable.
59104 """
60105 import IPython .display
61106
@@ -73,22 +118,7 @@ def _display_with_csv_download(self, df):
73118 # Create download link
74119 download_link = f'data:text/csv;base64,{ csv_base64 } '
75120
76- # Display the DataFrame first
77- IPython .display .display (df )
78-
79- # # Create and display the download button
80- # download_html = f'''
81- # <div style="margin-top: 10px;">
82- # <a href="{download_link}" download="stackql_results.csv"
83- # style="display: inline-block; padding: 8px 16px; background-color: #007cba;
84- # color: white; text-decoration: none; border-radius: 4px;
85- # font-family: Arial, sans-serif; font-size: 14px; border: none; cursor: pointer;">
86- # 📥 Download CSV
87- # </a>
88- # </div>
89- # '''
90-
91- # Create and display the download button
121+ # Only display the download button, not the DataFrame
92122 download_html = f'''
93123 <div style="margin-top: 15px; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', sans-serif;">
94124 <a href="{ download_link } " download="stackql_results.csv"
@@ -104,11 +134,9 @@ def _display_with_csv_download(self, df):
104134 Download CSV
105135 </a>
106136 </div>
107- '''
108-
137+ '''
109138 IPython .display .display (IPython .display .HTML (download_html ))
110139
111140 except Exception as e :
112- # If CSV generation fails, just display the DataFrame normally
113- IPython .display .display (df )
141+ # If CSV generation fails, just print an error message without displaying anything
114142 print (f"Error generating CSV download: { e } " )
0 commit comments