Skip to content

Commit 895c586

Browse files
authored
Merge pull request #56 from stackql/feature/refactor
output formatting
2 parents 0a3cf00 + 7765819 commit 895c586

File tree

6 files changed

+292
-493
lines changed

6 files changed

+292
-493
lines changed

pystackql/magic_ext/base.py

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
"""
88

99
from __future__ import print_function
10-
from IPython.core.magic import Magics
10+
from IPython.core.magic import Magics, line_cell_magic
1111
from string import Template
12+
import argparse
1213

1314
class 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}")

pystackql/magic_ext/local.py

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
using a local StackQL binary.
88
"""
99

10-
from IPython.core.magic import (magics_class, line_cell_magic)
10+
from IPython.core.magic import magics_class
1111
from .base import BaseStackqlMagic
12-
import argparse
1312

1413
@magics_class
1514
class StackqlMagic(BaseStackqlMagic):
@@ -22,43 +21,6 @@ def __init__(self, shell):
2221
"""
2322
super().__init__(shell, server_mode=False)
2423

25-
@line_cell_magic
26-
def stackql(self, line, cell=None):
27-
"""A Jupyter magic command to run StackQL queries.
28-
29-
Can be used as both line and cell magic:
30-
- As a line magic: `%stackql QUERY`
31-
- As a cell magic: `%%stackql [OPTIONS]` followed by the QUERY in the next line.
32-
33-
:param line: The arguments and/or StackQL query when used as line magic.
34-
:param cell: The StackQL query when used as cell magic.
35-
:return: StackQL query results as a named Pandas DataFrame (`stackql_df`).
36-
"""
37-
is_cell_magic = cell is not None
38-
39-
if is_cell_magic:
40-
parser = argparse.ArgumentParser()
41-
parser.add_argument("--no-display", action="store_true", help="Suppress result display.")
42-
parser.add_argument("--csv-download", action="store_true", help="Add CSV download link to output.")
43-
args = parser.parse_args(line.split())
44-
query_to_run = self.get_rendered_query(cell)
45-
else:
46-
args = None
47-
query_to_run = self.get_rendered_query(line)
48-
49-
results = self.run_query(query_to_run)
50-
self.shell.user_ns['stackql_df'] = results
51-
52-
if is_cell_magic and args and args.no_display:
53-
return None
54-
elif is_cell_magic and args and args.csv_download and not args.no_display:
55-
self._display_with_csv_download(results)
56-
return results
57-
elif is_cell_magic and args and not args.no_display:
58-
return results
59-
elif not is_cell_magic:
60-
return results
61-
6224
def load_ipython_extension(ipython):
6325
"""Load the non-server magic in IPython.
6426

pystackql/magic_ext/server.py

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
using a StackQL server connection.
88
"""
99

10-
from IPython.core.magic import (magics_class, line_cell_magic)
10+
from IPython.core.magic import magics_class
1111
from .base import BaseStackqlMagic
12-
import argparse
1312

1413
@magics_class
1514
class StackqlServerMagic(BaseStackqlMagic):
@@ -22,45 +21,6 @@ def __init__(self, shell):
2221
"""
2322
super().__init__(shell, server_mode=True)
2423

25-
@line_cell_magic
26-
def stackql(self, line, cell=None):
27-
"""A Jupyter magic command to run StackQL queries.
28-
29-
Can be used as both line and cell magic:
30-
- As a line magic: `%stackql QUERY`
31-
- As a cell magic: `%%stackql [OPTIONS]` followed by the QUERY in the next line.
32-
33-
:param line: The arguments and/or StackQL query when used as line magic.
34-
:param cell: The StackQL query when used as cell magic.
35-
:return: StackQL query results as a named Pandas DataFrame (`stackql_df`).
36-
"""
37-
is_cell_magic = cell is not None
38-
39-
if is_cell_magic:
40-
parser = argparse.ArgumentParser()
41-
parser.add_argument("--no-display", action="store_true", help="Suppress result display.")
42-
parser.add_argument("--csv-download", action="store_true", help="Add CSV download link to output.")
43-
args = parser.parse_args(line.split())
44-
query_to_run = self.get_rendered_query(cell)
45-
else:
46-
args = None
47-
query_to_run = self.get_rendered_query(line)
48-
49-
results = self.run_query(query_to_run)
50-
self.shell.user_ns['stackql_df'] = results
51-
52-
if is_cell_magic and args and args.no_display:
53-
return None
54-
elif is_cell_magic and args and args.csv_download and not args.no_display:
55-
self._display_with_csv_download(results)
56-
return results
57-
elif is_cell_magic and args and not args.no_display:
58-
return results
59-
elif not is_cell_magic:
60-
return results
61-
else:
62-
return results
63-
6424
def load_ipython_extension(ipython):
6525
"""Load the server magic in IPython."""
6626
# Create an instance of the magic class and register it

0 commit comments

Comments
 (0)