ttnn-visualizer 0.25.1__py3-none-any.whl → 0.27.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.
Files changed (25) hide show
  1. ttnn_visualizer/app.py +32 -5
  2. ttnn_visualizer/csv_queries.py +20 -4
  3. ttnn_visualizer/decorators.py +6 -6
  4. ttnn_visualizer/exceptions.py +7 -0
  5. ttnn_visualizer/file_uploads.py +7 -0
  6. ttnn_visualizer/models.py +21 -12
  7. ttnn_visualizer/queries.py +5 -5
  8. ttnn_visualizer/sessions.py +95 -40
  9. ttnn_visualizer/settings.py +1 -1
  10. ttnn_visualizer/sockets.py +17 -17
  11. ttnn_visualizer/static/assets/{allPaths-BsZhgQ3T.js → allPaths-DfvpbqXZ.js} +1 -1
  12. ttnn_visualizer/static/assets/{allPathsLoader-DgFQ6cmn.js → allPathsLoader-C1wkx2sM.js} +2 -2
  13. ttnn_visualizer/static/assets/{index-C6f3UdbP.css → index-BTfoVg9a.css} +2 -2
  14. ttnn_visualizer/static/assets/{index-Fd4wdmZt.js → index-CeYOVqR1.js} +247 -247
  15. ttnn_visualizer/static/assets/{splitPathsBySizeLoader-BFDp4kIf.js → splitPathsBySizeLoader-C_Kuh9CJ.js} +1 -1
  16. ttnn_visualizer/static/index.html +3 -3
  17. ttnn_visualizer/utils.py +6 -0
  18. ttnn_visualizer/views.py +73 -32
  19. {ttnn_visualizer-0.25.1.dist-info → ttnn_visualizer-0.27.0.dist-info}/METADATA +6 -9
  20. {ttnn_visualizer-0.25.1.dist-info → ttnn_visualizer-0.27.0.dist-info}/RECORD +25 -25
  21. {ttnn_visualizer-0.25.1.dist-info → ttnn_visualizer-0.27.0.dist-info}/LICENSE +0 -0
  22. {ttnn_visualizer-0.25.1.dist-info → ttnn_visualizer-0.27.0.dist-info}/LICENSE_understanding.txt +0 -0
  23. {ttnn_visualizer-0.25.1.dist-info → ttnn_visualizer-0.27.0.dist-info}/WHEEL +0 -0
  24. {ttnn_visualizer-0.25.1.dist-info → ttnn_visualizer-0.27.0.dist-info}/entry_points.txt +0 -0
  25. {ttnn_visualizer-0.25.1.dist-info → ttnn_visualizer-0.27.0.dist-info}/top_level.txt +0 -0
ttnn_visualizer/app.py CHANGED
@@ -2,6 +2,7 @@
2
2
  #
3
3
  # SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
4
4
 
5
+ import argparse
5
6
  import logging
6
7
  import os
7
8
  import subprocess
@@ -19,7 +20,8 @@ from flask_cors import CORS
19
20
  from werkzeug.debug import DebuggedApplication
20
21
  from werkzeug.middleware.proxy_fix import ProxyFix
21
22
 
22
- from ttnn_visualizer.exceptions import DatabaseFileNotFoundException
23
+ from ttnn_visualizer.exceptions import DatabaseFileNotFoundException, InvalidProfilerPath, InvalidReportPath
24
+ from ttnn_visualizer.sessions import create_instance_from_local_paths
23
25
  from ttnn_visualizer.settings import Config, DefaultConfig
24
26
 
25
27
  logger = logging.getLogger(__name__)
@@ -135,9 +137,10 @@ def middleware(app: flask.Flask):
135
137
  return None
136
138
 
137
139
 
138
- def open_browser(host, port, protocol="http"):
139
-
140
- url = f"{protocol}://{host}:{port}"
140
+ def open_browser(host, port, instance_id=None):
141
+ url = f"http://{host}:{port}"
142
+ if instance_id:
143
+ url = f"{url}?instanceId={instance_id}"
141
144
 
142
145
  print(f"Launching browser with url: {url}")
143
146
  try:
@@ -149,6 +152,13 @@ def open_browser(host, port, protocol="http"):
149
152
  print(f"Could not open the default browser: {e}")
150
153
 
151
154
 
155
+ def parse_args():
156
+ parser = argparse.ArgumentParser(description="A tool for visualizing the Tenstorrent Neural Network model (TT-NN)")
157
+ parser.add_argument("--report-path", type=str, help="Specify a report path", default=None)
158
+ parser.add_argument("--profiler-path", help="Specify a profiler path", default=None)
159
+ return parser.parse_args()
160
+
161
+
152
162
  def main():
153
163
 
154
164
  run_command = sys.argv[0].split("/")
@@ -156,6 +166,23 @@ def main():
156
166
  os.environ.setdefault("FLASK_ENV", "production")
157
167
 
158
168
  config = cast(DefaultConfig, Config())
169
+ args = parse_args()
170
+ instance_id = None
171
+
172
+ if args.report_path or args.profiler_path:
173
+ app = create_app()
174
+ app.app_context().push()
175
+ try:
176
+ session = create_instance_from_local_paths(
177
+ report_path=args.report_path,
178
+ profiler_path=args.profiler_path,
179
+ )
180
+ except InvalidReportPath:
181
+ sys.exit("Invalid report path")
182
+ except InvalidProfilerPath:
183
+ sys.exit("Invalid profiler path")
184
+
185
+ instance_id = session.instance_id
159
186
 
160
187
  # Check if DEBUG environment variable is set
161
188
  debug_mode = os.environ.get("DEBUG", "false").lower() == "true"
@@ -182,7 +209,7 @@ def main():
182
209
  flask_env = os.getenv("FLASK_ENV", "development")
183
210
  port = config.PORT if flask_env == "production" else config.DEV_SERVER_PORT
184
211
  host = config.HOST if flask_env == "production" else config.DEV_SERVER_HOST
185
- threading.Timer(2, open_browser, [host, port]).start()
212
+ threading.Timer(2, open_browser, [host, port, instance_id]).start()
186
213
  try:
187
214
  subprocess.run(gunicorn_args)
188
215
  except KeyboardInterrupt:
@@ -12,7 +12,7 @@ import pandas as pd
12
12
  from tt_perf_report import perf_report
13
13
 
14
14
  from ttnn_visualizer.exceptions import DataFormatError
15
- from ttnn_visualizer.models import TabSession
15
+ from ttnn_visualizer.models import Instance
16
16
  from ttnn_visualizer.ssh_client import get_client
17
17
 
18
18
 
@@ -278,7 +278,7 @@ class DeviceLogProfilerQueries:
278
278
  "source file",
279
279
  ]
280
280
 
281
- def __init__(self, session: TabSession):
281
+ def __init__(self, session: Instance):
282
282
  """
283
283
  Initialize the profiler with a session object.
284
284
  The session determines whether to use a local or remote runner.
@@ -366,7 +366,7 @@ class DeviceLogProfilerQueries:
366
366
  )
367
367
 
368
368
  @staticmethod
369
- def get_raw_csv(session: TabSession):
369
+ def get_raw_csv(session: Instance):
370
370
  from ttnn_visualizer.sftp_operations import read_remote_file
371
371
 
372
372
  if (
@@ -455,7 +455,7 @@ class OpsPerformanceQueries:
455
455
  "HWCommandQueue_write_buffer_TT_HOST_FUNC [ns]",
456
456
  ]
457
457
 
458
- def __init__(self, session: TabSession):
458
+ def __init__(self, session: Instance):
459
459
  """
460
460
  Initialize the performance profiler with a session object.
461
461
  """
@@ -571,6 +571,10 @@ class OpsPerformanceReportQueries:
571
571
  "raw_op_code"
572
572
  ]
573
573
 
574
+ PASSTHROUGH_COLUMNS = {
575
+ "pm_ideal_ns": "PM IDEAL [ns]",
576
+ }
577
+
574
578
  DEFAULT_SIGNPOST = None
575
579
  DEFAULT_IGNORE_SIGNPOSTS = None
576
580
  DEFAULT_MIN_PERCENTAGE = 0.5
@@ -596,6 +600,12 @@ class OpsPerformanceReportQueries:
596
600
  True,
597
601
  )
598
602
 
603
+ ops_perf_results = []
604
+ ops_perf_results_reader = csv.DictReader(StringIO(raw_csv))
605
+
606
+ for row in ops_perf_results_reader:
607
+ ops_perf_results.append(row)
608
+
599
609
  report = []
600
610
 
601
611
  try:
@@ -610,6 +620,12 @@ class OpsPerformanceReportQueries:
610
620
  processed_row["advice"] = processed_row["advice"].split(" • ")
611
621
  else:
612
622
  processed_row["advice"] = []
623
+
624
+ for key, value in cls.PASSTHROUGH_COLUMNS.items():
625
+ op_id = int(row[0])
626
+ idx = op_id - 2 # IDs in result column one correspond to row numbers in ops perf results csv
627
+ processed_row[key] = ops_perf_results[idx][value]
628
+
613
629
  report.append(processed_row)
614
630
  except csv.Error as e:
615
631
  raise DataFormatError() from e
@@ -19,7 +19,7 @@ from ttnn_visualizer.exceptions import (
19
19
  NoProjectsException,
20
20
  RemoteSqliteException,
21
21
  )
22
- from ttnn_visualizer.sessions import get_or_create_tab_session
22
+ from ttnn_visualizer.sessions import get_or_create_instance
23
23
 
24
24
 
25
25
  def with_session(func):
@@ -27,18 +27,18 @@ def with_session(func):
27
27
  def wrapper(*args, **kwargs):
28
28
  from flask import current_app
29
29
 
30
- tab_id = request.args.get("tabId")
30
+ instance_id = request.args.get("instanceId")
31
31
 
32
- if not tab_id:
33
- current_app.logger.error("No tabId present on request, returning 404")
32
+ if not instance_id:
33
+ current_app.logger.error("No instanceId present on request, returning 404")
34
34
  abort(404)
35
35
 
36
- session_query_data = get_or_create_tab_session(tab_id=tab_id)
36
+ session_query_data = get_or_create_instance(instance_id=instance_id)
37
37
  session = session_query_data.to_pydantic()
38
38
 
39
39
  if not session.active_report:
40
40
  current_app.logger.error(
41
- f"No active report exists for tabId {tab_id}, returning 404"
41
+ f"No active report exists for instanceId {instance_id}, returning 404"
42
42
  )
43
43
  # Raise 404 if report_path is missing or does not exist
44
44
  abort(404)
@@ -38,3 +38,10 @@ class DatabaseFileNotFoundException(Exception):
38
38
 
39
39
  class DataFormatError(Exception):
40
40
  pass
41
+
42
+
43
+ class InvalidReportPath(Exception):
44
+ pass
45
+
46
+ class InvalidProfilerPath(Exception):
47
+ pass
@@ -43,6 +43,13 @@ def extract_report_name(files):
43
43
  unsplit_report_name = str(files[0].filename)
44
44
  return unsplit_report_name.split("/")[0]
45
45
 
46
+ def extract_npe_name(files):
47
+ if not files:
48
+ return None
49
+
50
+ file_path = Path(files[0].filename)
51
+ return file_path.stem
52
+
46
53
 
47
54
  def save_uploaded_files(
48
55
  files,
ttnn_visualizer/models.py CHANGED
@@ -182,6 +182,7 @@ class StatusMessage(SerializeableModel):
182
182
  class ActiveReport(SerializeableModel):
183
183
  report_name: Optional[str] = None
184
184
  profile_name: Optional[str] = None
185
+ npe_name: Optional[str] = None
185
186
 
186
187
 
187
188
  class RemoteReportFolder(SerializeableModel):
@@ -191,23 +192,25 @@ class RemoteReportFolder(SerializeableModel):
191
192
  lastSynced: Optional[int] = None
192
193
 
193
194
 
194
- class TabSession(BaseModel):
195
- tab_id: str
195
+ class Instance(BaseModel):
196
+ instance_id: str
196
197
  report_path: Optional[str] = None
197
198
  profiler_path: Optional[str] = None
199
+ npe_path: Optional[str] = None
198
200
  active_report: Optional[ActiveReport] = None
199
201
  remote_connection: Optional[RemoteConnection] = None
200
202
  remote_folder: Optional[RemoteReportFolder] = None
201
203
  remote_profile_folder: Optional[RemoteReportFolder] = None
202
204
 
203
205
 
204
- class TabSessionTable(db.Model):
205
- __tablename__ = "tab_sessions"
206
+ class InstanceTable(db.Model):
207
+ __tablename__ = "instances"
206
208
 
207
209
  id = Column(Integer, primary_key=True)
208
- tab_id = Column(String, unique=True, nullable=False)
210
+ instance_id = Column(String, unique=True, nullable=False)
209
211
  report_path = Column(String)
210
212
  profiler_path = Column(String, nullable=True)
213
+ npe_path = Column(String, nullable=True)
211
214
  active_report = db.Column(MutableDict.as_mutable(JSON), nullable=False, default={})
212
215
  remote_connection = Column(JSON, nullable=True)
213
216
  remote_folder = Column(JSON, nullable=True)
@@ -215,17 +218,19 @@ class TabSessionTable(db.Model):
215
218
 
216
219
  def __init__(
217
220
  self,
218
- tab_id,
221
+ instance_id,
219
222
  active_report,
220
223
  remote_connection=None,
221
224
  remote_folder=None,
225
+ remote_profile_folder=None,
222
226
  report_path=None,
223
227
  profiler_path=None,
224
- remote_profile_folder=None,
228
+ npe_path=None,
225
229
  ):
226
- self.tab_id = tab_id
230
+ self.instance_id = instance_id
227
231
  self.active_report = active_report
228
232
  self.report_path = report_path
233
+ self.npe_path = npe_path
229
234
  self.remote_connection = remote_connection
230
235
  self.remote_folder = remote_folder
231
236
  self.profiler_path = profiler_path
@@ -234,22 +239,26 @@ class TabSessionTable(db.Model):
234
239
  def to_dict(self):
235
240
  return {
236
241
  "id": self.id,
237
- "tab_id": self.tab_id,
242
+ "instance_id": self.instance_id,
238
243
  "active_report": self.active_report,
239
244
  "remote_connection": self.remote_connection,
240
245
  "remote_folder": self.remote_folder,
241
246
  "remote_profile_folder": self.remote_profile_folder,
242
247
  "report_path": self.report_path,
243
248
  "profiler_path": self.profiler_path,
249
+ "npe_path": self.npe_path
244
250
  }
245
251
 
246
- def to_pydantic(self) -> TabSession:
247
- return TabSession(
248
- tab_id=str(self.tab_id),
252
+ def to_pydantic(self) -> Instance:
253
+ return Instance(
254
+ instance_id=str(self.instance_id),
249
255
  report_path=str(self.report_path) if self.report_path is not None else None,
250
256
  profiler_path=(
251
257
  str(self.profiler_path) if self.profiler_path is not None else None
252
258
  ),
259
+ npe_path=(
260
+ str(self.npe_path) if self.npe_path is not None else None
261
+ ),
253
262
  active_report=(
254
263
  (ActiveReport(**self.active_report) if self.active_report else None)
255
264
  if isinstance(self.active_report, dict)
@@ -13,7 +13,7 @@ from ttnn_visualizer.models import (
13
13
  DeviceOperation,
14
14
  Buffer,
15
15
  BufferPage,
16
- TabSession,
16
+ Instance,
17
17
  Tensor,
18
18
  OperationArgument,
19
19
  StackTrace,
@@ -31,7 +31,7 @@ import paramiko
31
31
 
32
32
 
33
33
  class LocalQueryRunner:
34
- def __init__(self, session: Optional[TabSession] = None, connection=None):
34
+ def __init__(self, session: Optional[Instance] = None, connection=None):
35
35
 
36
36
  if connection:
37
37
  self.connection = connection
@@ -66,7 +66,7 @@ class LocalQueryRunner:
66
66
  class RemoteQueryRunner:
67
67
  column_delimiter = "|||"
68
68
 
69
- def __init__(self, session: TabSession):
69
+ def __init__(self, session: Instance):
70
70
  self.session = session
71
71
  self._validate_session()
72
72
  self.ssh_client = self._get_ssh_client(self.session.remote_connection)
@@ -165,11 +165,11 @@ class RemoteQueryRunner:
165
165
 
166
166
  class DatabaseQueries:
167
167
 
168
- session: Optional[TabSession] = None
168
+ session: Optional[Instance] = None
169
169
  ssh_client = None
170
170
  query_runner: LocalQueryRunner | RemoteQueryRunner
171
171
 
172
- def __init__(self, session: Optional[TabSession] = None, connection=None):
172
+ def __init__(self, session: Optional[Instance] = None, connection=None):
173
173
  self.session = session
174
174
 
175
175
  if connection:
@@ -2,13 +2,18 @@
2
2
  #
3
3
  # SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
4
4
 
5
+ import json
6
+ import random
7
+ import string
5
8
  from logging import getLogger
9
+ from pathlib import Path
6
10
 
7
11
  from flask import request
8
12
 
9
- from ttnn_visualizer.utils import get_report_path, get_profiler_path
13
+ from ttnn_visualizer.exceptions import InvalidReportPath, InvalidProfilerPath
14
+ from ttnn_visualizer.utils import get_report_path, get_profiler_path, get_npe_path
10
15
  from ttnn_visualizer.models import (
11
- TabSessionTable,
16
+ InstanceTable,
12
17
  )
13
18
  from ttnn_visualizer.extensions import db
14
19
 
@@ -16,13 +21,13 @@ logger = getLogger(__name__)
16
21
 
17
22
  from flask import jsonify, current_app
18
23
  from sqlalchemy.exc import SQLAlchemyError
19
- import json
20
24
 
21
25
 
22
- def update_existing_tab_session(
26
+ def update_existing_instance(
23
27
  session_data,
24
28
  report_name,
25
29
  profile_name,
30
+ npe_name,
26
31
  remote_connection,
27
32
  remote_folder,
28
33
  remote_profile_folder,
@@ -34,6 +39,8 @@ def update_existing_tab_session(
34
39
  active_report["report_name"] = report_name
35
40
  if profile_name:
36
41
  active_report["profile_name"] = profile_name
42
+ if npe_name:
43
+ active_report["npe_name"] = npe_name
37
44
 
38
45
  session_data.active_report = active_report
39
46
 
@@ -48,7 +55,7 @@ def update_existing_tab_session(
48
55
  clear_remote_data(session_data)
49
56
 
50
57
  update_paths(
51
- session_data, active_report, remote_connection, report_name, profile_name
58
+ session_data, active_report, remote_connection
52
59
  )
53
60
 
54
61
 
@@ -63,17 +70,17 @@ def handle_sqlalchemy_error(error):
63
70
  db.session.rollback()
64
71
 
65
72
 
66
- def commit_and_log_session(session_data, tab_id):
73
+ def commit_and_log_session(session_data, instance_id):
67
74
  db.session.commit()
68
75
 
69
- session_data = TabSessionTable.query.filter_by(tab_id=tab_id).first()
76
+ session_data = InstanceTable.query.filter_by(instance_id=instance_id).first()
70
77
  current_app.logger.info(
71
- f"Session data for tab {tab_id}: {json.dumps(session_data.to_dict(), indent=4)}"
78
+ f"Session data for instance {instance_id}: {json.dumps(session_data.to_dict(), indent=4)}"
72
79
  )
73
80
 
74
81
 
75
82
  def update_paths(
76
- session_data, active_report, remote_connection, report_name, profile_name
83
+ session_data, active_report, remote_connection
77
84
  ):
78
85
  if active_report.get("profile_name"):
79
86
  session_data.profiler_path = get_profiler_path(
@@ -89,11 +96,18 @@ def update_paths(
89
96
  remote_connection=remote_connection,
90
97
  )
91
98
 
99
+ if active_report.get("npe_name"):
100
+ session_data.npe_path = get_npe_path(
101
+ npe_name=active_report["npe_name"],
102
+ current_app=current_app
103
+ )
104
+
92
105
 
93
- def create_new_tab_session(
94
- tab_id,
106
+ def create_new_instance(
107
+ instance_id,
95
108
  report_name,
96
109
  profile_name,
110
+ npe_name,
97
111
  remote_connection,
98
112
  remote_folder,
99
113
  remote_profile_folder,
@@ -104,14 +118,16 @@ def create_new_tab_session(
104
118
  active_report["report_name"] = report_name
105
119
  if profile_name:
106
120
  active_report["profile_name"] = profile_name
121
+ if npe_name:
122
+ active_report["npe_name"] = npe_name
107
123
 
108
124
  if clear_remote:
109
125
  remote_connection = None
110
126
  remote_folder = None
111
127
  remote_profile_folder = None
112
128
 
113
- session_data = TabSessionTable(
114
- tab_id=tab_id,
129
+ session_data = InstanceTable(
130
+ instance_id=instance_id,
115
131
  active_report=active_report,
116
132
  report_path=get_report_path(
117
133
  active_report,
@@ -130,40 +146,43 @@ def create_new_tab_session(
130
146
  return session_data
131
147
 
132
148
 
133
- def update_tab_session(
134
- tab_id,
149
+ def update_instance(
150
+ instance_id,
135
151
  report_name=None,
136
152
  profile_name=None,
153
+ npe_name=None,
137
154
  remote_connection=None,
138
155
  remote_folder=None,
139
156
  remote_profile_folder=None,
140
157
  clear_remote=False,
141
158
  ):
142
159
  try:
143
- session_data = get_or_create_tab_session(tab_id)
160
+ session_data = get_or_create_instance(instance_id)
144
161
 
145
162
  if session_data:
146
- update_existing_tab_session(
163
+ update_existing_instance(
147
164
  session_data,
148
165
  report_name,
149
166
  profile_name,
167
+ npe_name,
150
168
  remote_connection,
151
169
  remote_folder,
152
170
  remote_profile_folder,
153
171
  clear_remote,
154
172
  )
155
173
  else:
156
- session_data = create_new_tab_session(
157
- tab_id,
174
+ session_data = create_new_instance(
175
+ instance_id,
158
176
  report_name,
159
177
  profile_name,
178
+ npe_name,
160
179
  remote_connection,
161
180
  remote_folder,
162
181
  remote_profile_folder,
163
182
  clear_remote,
164
183
  )
165
184
 
166
- commit_and_log_session(session_data, tab_id)
185
+ commit_and_log_session(session_data, instance_id)
167
186
  return jsonify({"message": "Tab session updated successfully"}), 200
168
187
 
169
188
  except SQLAlchemyError as e:
@@ -171,25 +190,26 @@ def update_tab_session(
171
190
  return jsonify({"error": "Failed to update tab session"}), 500
172
191
 
173
192
 
174
- def get_or_create_tab_session(
175
- tab_id,
193
+ def get_or_create_instance(
194
+ instance_id,
176
195
  report_name=None,
177
196
  profile_name=None,
197
+ npe_name=None,
178
198
  remote_connection=None,
179
199
  remote_folder=None,
180
200
  ):
181
201
  """
182
202
  Retrieve an existing tab session or create a new one if it doesn't exist.
183
- Uses the TabSession model to manage session data and supports conditional updates.
203
+ Uses the Instance model to manage session data and supports conditional updates.
184
204
  """
185
205
  try:
186
206
  # Query the database for the tab session
187
- session_data = TabSessionTable.query.filter_by(tab_id=tab_id).first()
207
+ session_data = InstanceTable.query.filter_by(instance_id=instance_id).first()
188
208
 
189
209
  # If session doesn't exist, initialize it
190
210
  if not session_data:
191
- session_data = TabSessionTable(
192
- tab_id=tab_id,
211
+ session_data = InstanceTable(
212
+ instance_id=instance_id,
193
213
  active_report={},
194
214
  remote_connection=None,
195
215
  remote_folder=None,
@@ -198,17 +218,18 @@ def get_or_create_tab_session(
198
218
  db.session.commit()
199
219
 
200
220
  # Update the session if any new data is provided
201
- if report_name or profile_name or remote_connection or remote_folder:
202
- update_tab_session(
203
- tab_id=tab_id,
221
+ if report_name or profile_name or npe_name or remote_connection or remote_folder:
222
+ update_instance(
223
+ instance_id=instance_id,
204
224
  report_name=report_name,
205
225
  profile_name=profile_name,
226
+ npe_name=npe_name,
206
227
  remote_connection=remote_connection,
207
228
  remote_folder=remote_folder,
208
229
  )
209
230
 
210
231
  # Query again to get the updated session data
211
- session_data = TabSessionTable.query.filter_by(tab_id=tab_id).first()
232
+ session_data = InstanceTable.query.filter_by(instance_id=instance_id).first()
212
233
 
213
234
  return session_data
214
235
 
@@ -218,20 +239,20 @@ def get_or_create_tab_session(
218
239
  return None
219
240
 
220
241
 
221
- def get_tab_session():
242
+ def get_instance():
222
243
  """
223
- Middleware to retrieve or create a tab session based on the tab_id.
244
+ Middleware to retrieve or create a tab session based on the instance_id.
224
245
  """
225
- tab_id = request.args.get("tabId", None)
246
+ instance_id = request.args.get("instanceId", None)
226
247
 
227
- current_app.logger.info(f"get_tab_session: Received tab_id: {tab_id}")
228
- if not tab_id:
229
- current_app.logger.error("get_tab_session: No tab_id found")
230
- return jsonify({"error": "tabId is required"}), 400
248
+ current_app.logger.info(f"get_instance: Received instance_id: {instance_id}")
249
+ if not instance_id:
250
+ current_app.logger.error("get_instance: No instance_id found")
251
+ return jsonify({"error": "instanceId is required"}), 400
231
252
 
232
- active_report = get_or_create_tab_session(tab_id)
253
+ active_report = get_or_create_instance(instance_id)
233
254
  current_app.logger.info(
234
- f"get_tab_session: Session retrieved: {active_report.active_report}"
255
+ f"get_instance: Session retrieved: {active_report.active_report}"
235
256
  )
236
257
 
237
258
  return jsonify({"active_report": active_report.active_report}), 200
@@ -241,5 +262,39 @@ def init_sessions(app):
241
262
  """
242
263
  Initializes session middleware and hooks it into Flask.
243
264
  """
244
- app.before_request(get_tab_session)
265
+ app.before_request(get_instance)
245
266
  app.logger.info("Sessions middleware initialized.")
267
+
268
+
269
+ def create_random_instance_id():
270
+ return ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
271
+
272
+
273
+ def create_instance_from_local_paths(report_path, profiler_path):
274
+ _report_path = Path(report_path)
275
+ _profiler_path = Path(profiler_path)
276
+
277
+ if not _report_path.exists():
278
+ raise InvalidReportPath()
279
+
280
+ if not _profiler_path.exists():
281
+ raise InvalidProfilerPath()
282
+
283
+ report_name = _report_path.parts[-2] if len(_report_path.parts) > 2 else ""
284
+ profile_name = _profiler_path.parts[-1] if len(_profiler_path.parts) > 2 else ""
285
+ session_data = InstanceTable(
286
+ instance_id=create_random_instance_id(),
287
+ active_report={
288
+ "report_name": report_name,
289
+ "profile_name": profile_name,
290
+ "npe_name": None,
291
+ },
292
+ report_path=report_path,
293
+ profiler_path=profiler_path,
294
+ remote_connection=None,
295
+ remote_folder=None,
296
+ remote_profile_folder=None,
297
+ )
298
+ db.session.add(session_data)
299
+ db.session.commit()
300
+ return session_data
@@ -19,7 +19,7 @@ class DefaultConfig(object):
19
19
 
20
20
  # Path Settings
21
21
  REPORT_DATA_DIRECTORY = Path(__file__).parent.absolute().joinpath("data")
22
- VERSION = "0.14.2"
22
+ VERSION = "0.26.0"
23
23
  LOCAL_DATA_DIRECTORY = Path(REPORT_DATA_DIRECTORY).joinpath("local")
24
24
  REMOTE_DATA_DIRECTORY = Path(REPORT_DATA_DIRECTORY).joinpath("remote")
25
25
  APPLICATION_DIR = os.path.abspath(os.path.join(__file__, "..", os.pardir))