tensorwatch-api 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ import warnings
2
+ import logging
3
+
4
+ warnings.filterwarnings("ignore", category=UserWarning, module="pykafka")
5
+ warnings.filterwarnings("ignore", category=UserWarning, module='pkg_resources')
6
+ warnings.filterwarnings("ignore",message=".*cache_frame_data.*",category=UserWarning )
7
+
8
+ # Suppress "No partitions assigned" warnings from pykafka.balancedconsumer, which can be noisy during rebalancing.
9
+ logging.getLogger('pykafka.balancedconsumer').setLevel(logging.ERROR)
10
+
11
+ from .twapi import twapi
@@ -0,0 +1,191 @@
1
+ import tensorwatchext as tw
2
+ from tensorwatchext import kafka_connector as kc
3
+ from tensorwatchext import pykafka_connector as pyc
4
+ from IPython.display import display
5
+ from ipywidgets import widgets
6
+ import time
7
+ import logging
8
+ import matplotlib.pyplot as plt
9
+
10
+ class twapi:
11
+ """TensorWatch API Wrapper for Kafka Streaming and Visualization"""
12
+
13
+ def __init__(self):
14
+ """Initializes the twapi class, setting up the UI widgets and event handlers."""
15
+ self.default_value = 10
16
+ self.visualizer = None # Initialize visualizer as None
17
+ self.client = tw.WatcherClient()
18
+ self.out = widgets.Output(layout={})
19
+
20
+ # Initialize UI widgets
21
+ self.update_interval = 0.5 # Delay in seconds
22
+ self.my_slider = widgets.IntSlider(value=self.default_value, min=1, max=100, step=1, description="Window Size:")
23
+ self.my_slider2 = widgets.IntSlider(value=self.default_value, min=1, max=100, step=1, description="Window Width:")
24
+ self.datebutton = widgets.Checkbox(value=False, description="Date")
25
+ self.offsetbutton = widgets.Checkbox(value=False, description="Use Offset")
26
+ self.dimhistorybutton = widgets.Checkbox(value=True, description="Dim History")
27
+ self.colorpicker = widgets.ColorPicker(value="blue", description="Pick a Color")
28
+
29
+ self.button_reset = widgets.Button(description="Reset", tooltip="Reset stream settings")
30
+ self.button_apply = widgets.Button(description="Please wait", tooltip="Apply changes to the visualization", disabled=True)
31
+
32
+ # Group widgets for a cleaner UI
33
+ left_box = widgets.VBox([self.my_slider, self.my_slider2, self.colorpicker])
34
+ right_box = widgets.VBox([self.offsetbutton, self.dimhistorybutton, self.datebutton])
35
+ self.options_box = widgets.HBox([left_box, right_box])
36
+ self.accordion = widgets.Accordion(children=[self.options_box])
37
+ self.accordion.set_title(0, 'Visualization Options')
38
+
39
+ # Event handlers
40
+ self._last_update = time.time()
41
+ self.button_reset.on_click(self.reset)
42
+ self.button_apply.on_click(self.apply_with_debounce)
43
+ self.metrics_label = widgets.Label(value="")
44
+
45
+ # Observe widget changes directly
46
+ self.my_slider.observe(self.apply_with_debounce, names='value')
47
+ self.my_slider2.observe(self.apply_with_debounce, names='value')
48
+ self.colorpicker.observe(self.apply_with_debounce, names='value')
49
+
50
+ def stream(self, expr):
51
+ """Creates a TensorWatch stream from an expression."""
52
+ self.expr = expr
53
+ try:
54
+ self.streamdata = self.client.create_stream(expr=expr)
55
+ logging.debug("Stream created successfully")
56
+ except Exception as e:
57
+ logging.error(f"Error creating stream: {e}")
58
+ print(f"Error creating stream: {e}")
59
+ return self
60
+
61
+ def apply_with_debounce(self, _=None):
62
+ """Debounced apply function to prevent too frequent updates."""
63
+ now = time.time()
64
+ if now - self._last_update > self.update_interval:
65
+ self.update_visualizer()
66
+ self._last_update = now
67
+ if self.button_apply.description == "Start":
68
+ self.button_apply.description = "Apply Changes"
69
+
70
+ def update_visualizer(self, _=None):
71
+ """Updates the TensorWatch visualizer with the latest widget values."""
72
+ if not hasattr(self, 'streamdata') or not self.streamdata:
73
+ self.out.clear_output(wait=True)
74
+ with self.out:
75
+ print("Stream data not available or empty yet. Please wait for data.")
76
+ return
77
+
78
+ try:
79
+ # Always clear output before drawing
80
+ self.out.clear_output(wait=True)
81
+
82
+ # Close previous visualizer if it exists to free resources
83
+ if self.visualizer:
84
+ # self.visualizer.close()
85
+ try:
86
+ plt.pause(0.05)
87
+ plt.close('all') # Also close any lingering matplotlib figures
88
+ except Exception:
89
+ pass
90
+ # Create a new visualizer with the current settings
91
+ self.visualizer = tw.Visualizer(
92
+ self.streamdata,
93
+ vis_type="line",
94
+ window_width=self.my_slider2.value,
95
+ window_size=self.my_slider.value,
96
+ Date=self.datebutton.value,
97
+ useOffset=self.offsetbutton.value,
98
+ dim_history=self.dimhistorybutton.value,
99
+ color=self.colorpicker.value,
100
+ )
101
+ with self.out:
102
+ self.visualizer.show()
103
+
104
+ except Exception as e:
105
+ self.out.clear_output(wait=True)
106
+ with self.out:
107
+ print(f"Error updating visualizer: {e}")
108
+
109
+ def enable_apply_button(self):
110
+ """Enables the apply button and changes its description to 'Start'."""
111
+ logging.debug("Enabling apply button.")
112
+ self.button_apply.disabled = False
113
+ self.button_apply.description = "Start"
114
+
115
+ def reset(self, _=None):
116
+ """Resets all widget values to their defaults and clears the visualization."""
117
+ self.my_slider.value = self.default_value
118
+ self.my_slider2.value = self.default_value
119
+ self.datebutton.value = False
120
+ self.offsetbutton.value = False
121
+ self.dimhistorybutton.value = True
122
+ self.colorpicker.value = "blue"
123
+
124
+ # Clear the output and close the visualizer
125
+ self.out.clear_output()
126
+ plt.close('all')
127
+ self.visualizer = None
128
+
129
+ def draw(self):
130
+ """Displays the UI for controlling the visualization."""
131
+ ui = widgets.VBox([
132
+ widgets.HBox([self.button_reset, self.button_apply]),
133
+ self.accordion,
134
+ self.out
135
+ ])
136
+ display(ui)
137
+
138
+ def draw_with_metrics(self):
139
+ """Displays the UI for controlling the visualization with a metrics label."""
140
+ ui = widgets.VBox([
141
+ self.metrics_label,
142
+ widgets.HBox([self.button_reset, self.button_apply]),
143
+ self.accordion,
144
+ self.out
145
+ ])
146
+ display(ui)
147
+
148
+ def update_metrics(self, metrics):
149
+ """Updates the metrics label with the provided text."""
150
+ self.metrics_label.value = metrics
151
+
152
+ def connector(self, topic, host, parsetype="json", cluster_size=1, conn_type="kafka", queue_length=50000,
153
+ group_id="mygroup", schema_path=None, protobuf_message=None, parser_extra=None,
154
+ random_sampling=None, countmin_width=None, countmin_depth=None, ordering_field=None):
155
+ """
156
+ Creates and returns a Kafka or PyKafka connector.
157
+
158
+ Args:
159
+ topic (str): The Kafka topic to consume from.
160
+ host (str): The Kafka broker host.
161
+ parsetype (str): The message format (e.g., 'json', 'pickle', 'avro').
162
+ cluster_size (int): The number of consumer threads.
163
+ conn_type (str): The type of connector to use ('kafka' or 'pykafka').
164
+ queue_length (int): The maximum size of the message queue.
165
+ group_id (str): The Kafka consumer group ID.
166
+ schema_path (str): The path to the schema file.
167
+ protobuf_message (str): The name of the Protobuf message class.
168
+ parser_extra (str): Extra data for the parser (e.g., Avro schema for 'pykafka').
169
+ random_sampling (int): The percentage of messages to sample.
170
+ countmin_width (int): The width of the Count-Min Sketch.
171
+ countmin_depth (int): The depth of the Count-Min Sketch.
172
+
173
+ Returns:
174
+ A KafkaConnector or pykafka_connector instance.
175
+ """
176
+ if conn_type == "kafka":
177
+ return kc(
178
+ topic=topic, hosts=host, parsetype=parsetype, cluster_size=cluster_size, queue_length=queue_length, group_id=group_id,
179
+ schema_path=schema_path, protobuf_message=protobuf_message,parser_extra=parser_extra,
180
+ random_sampling=random_sampling, countmin_width=countmin_width,ordering_field=ordering_field,
181
+ countmin_depth=countmin_depth,
182
+ twapi_instance=self)
183
+ elif conn_type == "pykafka":
184
+ return pyc(
185
+ topic=topic, hosts=host, parsetype=parsetype, cluster_size=cluster_size,twapi_instance=self,
186
+ queue_length=queue_length, consumer_group=bytes(group_id, 'utf-8'),
187
+ parser_extra=parser_extra, schema_path=schema_path, protobuf_message=protobuf_message,
188
+ random_sampling=random_sampling, countmin_width=countmin_width,ordering_field=ordering_field,
189
+ countmin_depth=countmin_depth)
190
+ else:
191
+ raise ValueError("Invalid connector type. Choose 'kafka' or 'pykafka'.")
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: tensorwatch-api
3
+ Version: 0.1.0
4
+ Summary: Interactive Kafka stream visualization API for Jupyter using TensorWatch.
5
+ Home-page: https://github.com/costasrevi/jupyterstreamvis
6
+ Author: Konstantinos Revythis
7
+ Author-email: Konstantinos Revythis <krevythis@tuc.gr>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/costasrevi/jupyterstreamvis
10
+ Project-URL: Source, https://github.com/costasrevi/jupyterstreamvis
11
+ Keywords: kafka,tensorwatch,jupyter,streaming,visualization
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE.txt
21
+ Requires-Dist: tensorwatch
22
+ Requires-Dist: ipywidgets
23
+ Requires-Dist: matplotlib
24
+ Requires-Dist: confluent-kafka; extra == "kafka"
25
+ Requires-Dist: pykafka; extra == "pykafka"
26
+ Dynamic: author
27
+ Dynamic: home-page
28
+ Dynamic: license-file
29
+ Dynamic: requires-python
30
+
31
+ # TensorWatch API (`tensorwatch-api`)
32
+
33
+ `tensorwatch-api` is a Python library designed to simplify **real-time streaming data visualization from Kafka** directly inside Jupyter notebooks.
34
+ It wraps **TensorWatch** and provides an interactive, high-level API with **ipywidgets** for controlling visualization parameters on the fly.
35
+
36
+ This package is ideal for data scientists, engineers, and analysts who want to inspect, monitor, and visualize streaming data **without leaving JupyterLab**.
37
+
38
+ ---
39
+
40
+ ## Features
41
+
42
+ - **Interactive UI**: Control plot parameters like window size, colors, and history in real-time.
43
+ - **Chainable API**: Fluent API for connecting to Kafka, defining streams, and drawing visualizations.
44
+ - **Dual Kafka Backends**: Supports `confluent-kafka` (default) and `pykafka`.
45
+ - **Live Metrics**: Display real-time throughput and latency metrics alongside visualizations.
46
+ - **Custom Processing**: Apply Python lambda expressions for aggregation or preprocessing.
47
+ - **Efficient Updates**: Debouncing ensures smooth UI updates without overloading the kernel.
48
+
49
+ ---
50
+
51
+ ## Installation
52
+
53
+ Install the package and its dependencies:
54
+
55
+ ```bash
56
+ pip install tensorwatch-api
57
+ pip install tensorwatch ipywidgets matplotlib
58
+ ```
59
+ Install a Kafka client:
60
+ ```bash
61
+ # Default Kafka connector
62
+ pip install confluent-kafka
63
+
64
+ # Or pykafka connector
65
+ pip install pykafka
66
+ ```
67
+ ##Quick Start
68
+ # Enable interactive matplotlib backend
69
+ %matplotlib widget
70
+
71
+ # Import the library
72
+ from tensorwatch_api import twapi as tw
73
+
74
+ # Initialize the API
75
+ test = tw()
76
+
77
+ # Create a connector to a Kafka topic
78
+ test.connector(
79
+ topic='mytopic',
80
+ host='localhost:9092',
81
+ cluster_size=5,
82
+ queue_length=100000,
83
+ parsetype="protobuf", # optional: json, pickle, avro, protobuf
84
+ parser_extra="benchmark_pb2", # module name for protobuf
85
+ protobuf_message="BenchmarkMessage",# class name for protobuf
86
+ schema_path=r"C:\path\to\protobuf" # path to protobuf module
87
+ )
88
+
89
+ # Define the stream processing logic
90
+ test.stream(expr='lambda d: sum(msg["seq"] for msg in d.data) if d.data else 0')
91
+
92
+ # Draw the UI with metrics
93
+ test.draw_with_metrics()
94
+
95
+
96
+ ## API Reference
97
+
98
+ ### `twapi()`
99
+ Initializes the API wrapper and UI components.
100
+
101
+ ### `.connector(...)`
102
+ Creates a Kafka consumer in a background thread, fetching data and reporting metrics.
103
+
104
+ **Arguments:**
105
+
106
+ - `topic` (str): Kafka topic to consume.
107
+ - `host` (str): Broker host (`localhost:9092`).
108
+ - `conn_type` (str): Connector type (`'kafka'` or `'pykafka'`). Default is `'kafka'`.
109
+ - `parsetype` (str): Message format (`'json'`, `'pickle'`, `'avro'`, `'protobuf'`). Default `'json'`.
110
+ - `cluster_size` (int): Number of consumer threads. Default 1.
111
+ - `queue_length` (int): Max messages in memory. Default 50000.
112
+ - Other parameters: `schema_path`, `protobuf_message`, `parser_extra`, `random_sampling`, `countmin_width`, `countmin_depth`, `ordering_field`.
113
+
114
+ ### `.stream(expr)`
115
+ Defines the data processing logic for the stream.
116
+
117
+ - `expr` (str): A Python lambda expression. Receives `d` (connector instance) and returns a single numerical value to plot.
118
+
119
+ ### `.draw()` / `.draw_with_metrics()`
120
+ Renders interactive UI widgets and plot area.
121
+
122
+ - `.draw()`: Standard UI.
123
+ - `.draw_with_metrics()`: Includes real-time benchmark metrics.
124
+
125
+ ---
126
+
127
+ ## UI Controls
128
+
129
+ - **Reset Button**: Reset all options and clear the plot.
130
+ - **Start / Apply Changes Button**: Initially `"Start"`. Activates on first Kafka message, then applies UI changes.
131
+ - **Accordion Options**:
132
+ - **Window Size**: Number of points shown.
133
+ - **Window Width**: Width of plot.
134
+ - **Pick a Color**: Plot line color.
135
+ - **Date / Use Offset / Dim History**: TensorWatch visualization options.
136
+
137
+ ---
138
+
139
+ ## License
140
+
141
+ This project is licensed under the **MIT License**. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,7 @@
1
+ tensorwatch_api/__init__.py,sha256=RE2b0TnopwHIV_NcvhQA3M8Q4fEYItq-HJsPwdpzZWA,485
2
+ tensorwatch_api/twapi.py,sha256=WyISrhNbMwVt659oej8VA4gNZbzPs56i7DjZl8aC9O0,9016
3
+ tensorwatch_api-0.1.0.dist-info/licenses/LICENSE.txt,sha256=oHOsDZJ_S9nBtvRFJZv-9L31kmphhq2zf48PGQ-JHwU,1091
4
+ tensorwatch_api-0.1.0.dist-info/METADATA,sha256=99FoB0FFr9wC_58Avl2gwkT0yKIjmlmjOKDbzDaRrOg,5100
5
+ tensorwatch_api-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ tensorwatch_api-0.1.0.dist-info/top_level.txt,sha256=wL9uSNrri-CFUXFN-uolrvW4Bfg8_hHXhjH1uhaO7dk,16
7
+ tensorwatch_api-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Costa S. Revi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ tensorwatch_api