Coverage for mindsdb / integrations / handlers / lightdash_handler / lightdash_tables.py: 0%

329 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-21 00:36 +0000

1from typing import List 

2 

3import pandas as pd 

4from mindsdb_sql_parser import ast 

5 

6from mindsdb.integrations.utilities.handlers.query_utilities import ( 

7 SELECTQueryExecutor, 

8 SELECTQueryParser, 

9) 

10from mindsdb.integrations.libs.api_handler import APIHandler, APITable 

11from mindsdb.integrations.utilities.sql_utils import conditions_to_filter 

12 

13 

14def val_to_string(d, k): 

15 if k in d: 

16 d[k] = str(d[k]) 

17 

18 

19def move_under(d, key_contents_to_move, key_to_move_under=None): 

20 if key_contents_to_move not in d: 

21 return 

22 for k, v in d[key_contents_to_move].items(): 

23 if key_to_move_under: 

24 d[key_to_move_under][k] = v 

25 else: 

26 d[k] = v 

27 del d[key_contents_to_move] 

28 

29 

30def select_keys(d, keys): 

31 new_d = {} 

32 for key in keys: 

33 new_d[key] = d.get(key, "") 

34 return new_d 

35 

36 

37class CustomAPITable(APITable): 

38 

39 def __init__(self, handler: APIHandler): 

40 super().__init__(handler) 

41 self.handler.connect() 

42 

43 def get_columns(self, ignore: List[str] = []) -> List[str]: 

44 return [item for item in self.columns if item not in ignore] 

45 

46 def select(self, query: ast.Select) -> pd.DataFrame: 

47 raise NotImplementedError() 

48 

49 def parse_select(self, query: ast.Select, table_name: str): 

50 select_statement_parser = SELECTQueryParser(query, table_name, self.get_columns()) 

51 self.selected_columns, self.where_conditions, self.order_by_conditions, self.result_limit = select_statement_parser.parse_query() 

52 

53 def get_where_param(self, query: ast.Select, param: str): 

54 params = conditions_to_filter(query.where) 

55 if param not in params: 

56 raise Exception(f"WHERE condition does not have '{param}' selector") 

57 return params[param] 

58 

59 def apply_query_params(self, df, query): 

60 select_statement_parser = SELECTQueryParser(query, self.name, self.get_columns()) 

61 selected_columns, _, order_by_conditions, result_limit = select_statement_parser.parse_query() 

62 select_statement_executor = SELECTQueryExecutor(df, selected_columns, [], order_by_conditions, result_limit) 

63 return select_statement_executor.execute_query() 

64 

65 

66class UserTable(CustomAPITable): 

67 name: str = "user" 

68 columns: List[str] = [ 

69 'userUuid', 

70 'email', 

71 'firstName', 

72 'lastName', 

73 'organizationUuid', 

74 'organizationName', 

75 'organizationCreatedAt', 

76 'isTrackingAnonymized', 

77 'isMarketingOptedIn', 

78 'isSetupComplete', 

79 'role', 

80 'isActive', 

81 ] 

82 

83 def __init__(self, handler: APIHandler): 

84 super().__init__(handler) 

85 self.connection = self.handler.connect() 

86 

87 def select(self, query: ast.Select) -> pd.DataFrame: 

88 data = select_keys(self.connection.get_user(), self.columns) 

89 df = pd.DataFrame.from_records([data]) 

90 return self.apply_query_params(df, query) 

91 

92 

93class UserAbilityTable(CustomAPITable): 

94 name: str = "user_ability" 

95 columns: List[str] = [ 

96 'action', 

97 'subject', 

98 'conditions' 

99 ] 

100 

101 def __init__(self, handler: APIHandler): 

102 super().__init__(handler) 

103 self.connection = self.handler.connect() 

104 

105 def select(self, query: ast.Select) -> pd.DataFrame: 

106 data = select_keys(self.connection.get_user(), ["abilityRules"]) 

107 for d in data: 

108 val_to_string(d, "condition") 

109 df = pd.DataFrame.from_records(data, columns=self.columns) 

110 return self.apply_query_params(df, query) 

111 

112 

113class OrgTable(CustomAPITable): 

114 name: str = "org" 

115 columns: List[str] = [ 

116 'organizationUuid', 

117 'defaultProjectUuid' 

118 'name', 

119 'chartColors', 

120 'needsProject', 

121 ] 

122 

123 def __init__(self, handler: APIHandler): 

124 super().__init__(handler) 

125 self.connection = self.handler.connect() 

126 

127 def select(self, query: ast.Select) -> pd.DataFrame: 

128 data = select_keys(self.connection.get_org(), self.columns) 

129 val_to_string(data, "chartColors") 

130 df = pd.DataFrame.from_records([data]) 

131 return self.apply_query_params(df, query) 

132 

133 

134class OrgProjectsTable(CustomAPITable): 

135 name: str = "org_projects" 

136 columns: List[str] = [ 

137 'name', 

138 'projectUuid', 

139 'type', 

140 ] 

141 

142 def __init__(self, handler: APIHandler): 

143 super().__init__(handler) 

144 self.connection = self.handler.connect() 

145 

146 def select(self, query: ast.Select) -> pd.DataFrame: 

147 data = self.connection.get_projects() 

148 df = pd.DataFrame.from_records(data, columns=self.columns) 

149 return self.apply_query_params(df, query) 

150 

151 

152class OrgMembersTable(CustomAPITable): 

153 name: str = "org_members" 

154 columns: List[str] = [ 

155 'userUuid', 

156 'firstName', 

157 'lastName', 

158 'email', 

159 'organizationUuid', 

160 'role', 

161 'isActive', 

162 'isInviteExpired', 

163 ] 

164 

165 def __init__(self, handler: APIHandler): 

166 super().__init__(handler) 

167 self.connection = self.handler.connect() 

168 

169 def select(self, query: ast.Select) -> pd.DataFrame: 

170 data = self.connection.get_org_members() 

171 df = pd.DataFrame.from_records(data, columns=self.columns) 

172 return self.apply_query_params(df, query) 

173 

174 

175class ProjectTable(CustomAPITable): 

176 name: str = "project_table" 

177 columns: List[str] = [ 

178 'organizationUuid', 

179 'projectUuid', 

180 'name', 

181 'type', 

182 'pinnedListUuid', 

183 'copiedFromProjectUuid', 

184 'dbtVersion', 

185 ] 

186 

187 def __init__(self, handler: APIHandler): 

188 super().__init__(handler) 

189 self.connection = self.handler.connect() 

190 

191 def select(self, query: ast.Select) -> pd.DataFrame: 

192 project_uuid = self.get_where_param(query, 'project_uuid') 

193 data = select_keys(self.connection.get_project(project_uuid), self.columns) 

194 df = pd.DataFrame.from_records([data]) 

195 return self.apply_query_params(df, query) 

196 

197 

198class WarehouseConnectionTable(CustomAPITable): 

199 name: str = "warehouse_connection" 

200 columns: List[str] = [ 

201 "role", 

202 "type", 

203 "account", 

204 "database", 

205 "warehouse", 

206 "schema", 

207 "threads", 

208 "clientSessionKeepAlive", 

209 "queryTag", 

210 "accessUrl", 

211 "startOfWeek", 

212 ] 

213 

214 def __init__(self, handler: APIHandler): 

215 super().__init__(handler) 

216 self.connection = self.handler.connect() 

217 

218 def select(self, query: ast.Select) -> pd.DataFrame: 

219 project_uuid = self.get_where_param(query, 'project_uuid') 

220 data = select_keys(self.connection.get_project(project_uuid).get("warehouseConnection", {}), self.columns) 

221 df = pd.DataFrame.from_records([data]) 

222 return self.apply_query_params(df, query) 

223 

224 

225class DBTConnectionTable(CustomAPITable): 

226 name: str = "dbt_connection" 

227 columns: List[str] = [ 

228 "type", 

229 "target", 

230 "profiles_dir", 

231 "project_dir", 

232 ] 

233 

234 def __init__(self, handler: APIHandler): 

235 super().__init__(handler) 

236 self.connection = self.handler.connect() 

237 

238 def select(self, query: ast.Select) -> pd.DataFrame: 

239 project_uuid = self.get_where_param(query, "project_uuid") 

240 data = select_keys(self.connection.get_project(project_uuid).get("dbtConnection", {}), self.columns) 

241 df = pd.DataFrame.from_records([data]) 

242 return self.apply_query_params(df, query) 

243 

244 

245class DBTEnvironmentVarsTable(CustomAPITable): 

246 name: str = "dbt_env_vars" 

247 columns: List[str] = [ 

248 "value", 

249 "key", 

250 ] 

251 

252 def __init__(self, handler: APIHandler): 

253 super().__init__(handler) 

254 self.connection = self.handler.connect() 

255 

256 def select(self, query: ast.Select) -> pd.DataFrame: 

257 project_uuid = self.get_where_param(query, "project_uuid") 

258 data = self.connection.get_project(project_uuid).get("dbtConnection", {}).get("environment", []) 

259 df = pd.DataFrame.from_records(data, columns=self.columns) 

260 return self.apply_query_params(df, query) 

261 

262 

263class ChartsTable(CustomAPITable): 

264 name: str = "charts" 

265 columns: List[str] = [ 

266 "name", 

267 "organizationUuid", 

268 "uuid", 

269 "description", 

270 "projectUuid", 

271 "spaceUuid", 

272 "pinnedListUuid", 

273 "spaceName", 

274 "dashboardUuid", 

275 "dashboardName", 

276 "chartType", 

277 ] 

278 

279 def __init__(self, handler: APIHandler): 

280 super().__init__(handler) 

281 self.connection = self.handler.connect() 

282 

283 def select(self, query: ast.Select) -> pd.DataFrame: 

284 project_uuid = self.get_where_param(query, "project_uuid") 

285 data = self.connection.get_charts_in_project(project_uuid) 

286 df = pd.DataFrame.from_records(data, columns=self.columns) 

287 return self.apply_query_params(df, query) 

288 

289 

290class SpacesTable(CustomAPITable): 

291 name: str = "spaces" 

292 columns: List[str] = [ 

293 "name", 

294 "organizationUuid", 

295 "uuid", 

296 "projectUuid", 

297 "pinnedListUuid", 

298 "pinnedListOrder", 

299 "isPrivate", 

300 "dashboardCount", 

301 "chartCount", 

302 "access", 

303 ] 

304 

305 def __init__(self, handler: APIHandler): 

306 super().__init__(handler) 

307 self.connection = self.handler.connect() 

308 

309 def select(self, query: ast.Select) -> pd.DataFrame: 

310 project_uuid = self.get_where_param(query, "project_uuid") 

311 data = self.connection.get_spaces_in_project(project_uuid) 

312 for d in data: 

313 val_to_string(d, "access") 

314 df = pd.DataFrame.from_records(data, columns=self.columns) 

315 return self.apply_query_params(df, query) 

316 

317 

318class AccessTable(CustomAPITable): 

319 name: str = "access" 

320 columns: List[str] = [ 

321 "lastName", 

322 "firstName", 

323 "email", 

324 "role", 

325 "projectUuid", 

326 "userUuid", 

327 ] 

328 

329 def __init__(self, handler: APIHandler): 

330 super().__init__(handler) 

331 self.connection = self.handler.connect() 

332 

333 def select(self, query: ast.Select) -> pd.DataFrame: 

334 project_uuid = self.get_where_param(query, "project_uuid") 

335 data = self.connection.get_project_access_list(project_uuid) 

336 df = pd.DataFrame.from_records(data, columns=self.columns) 

337 return self.apply_query_params(df, query) 

338 

339 

340class ValidationTable(CustomAPITable): 

341 name: str = "validation" 

342 columns: List[str] = [ 

343 "source", 

344 "spaceUuid", 

345 "projectUuid", 

346 "errorType", 

347 "error", 

348 "name", 

349 "createdAt", 

350 "validationId", 

351 "chartName", 

352 "chartViews", 

353 "lastUpdatedAt", 

354 "lastUpdatedBy", 

355 "fieldName", 

356 "chartType", 

357 "chartUuid", 

358 ] 

359 

360 def __init__(self, handler: APIHandler): 

361 super().__init__(handler) 

362 self.connection = self.handler.connect() 

363 

364 def select(self, query: ast.Select) -> pd.DataFrame: 

365 project_uuid = self.get_where_param(query, "project_uuid") 

366 data = self.connection.get_validation_results(project_uuid) 

367 df = pd.DataFrame.from_records(data, columns=self.columns) 

368 return self.apply_query_params(df, query) 

369 

370 

371class DashboardsTable(CustomAPITable): 

372 name: str = "dashboards" 

373 columns: List[str] = [ 

374 "name", 

375 "organizationUuid", 

376 "uuid", 

377 "description", 

378 "updatedAt", 

379 "projectUuid", 

380 "spaceUuid", 

381 "views", 

382 "firstViewedAt", 

383 "pinnedListUuid", 

384 "pinnedListOrder", 

385 ] 

386 

387 def __init__(self, handler: APIHandler): 

388 super().__init__(handler) 

389 self.connection = self.handler.connect() 

390 

391 def select(self, query: ast.Select) -> pd.DataFrame: 

392 project_uuid = self.get_where_param(query, "project_uuid") 

393 space_uuid = self.get_where_param(query, "space_uuid") 

394 data = [] 

395 for row in self.connection.get_space(project_uuid, space_uuid).get("dashboards", []): 

396 data.append(select_keys(row, self.columns)) 

397 df = pd.DataFrame.from_records(data, columns=self.columns) 

398 return self.apply_query_params(df, query) 

399 

400 

401class QueriesTable(CustomAPITable): 

402 name: str = "queries" 

403 columns: List[str] = [ 

404 "name", 

405 "uuid", 

406 "description", 

407 "updatedAt", 

408 "spaceUuid", 

409 "pinnedListUuid", 

410 "pinnedListOrder", 

411 "firstViewedAt", 

412 "views", 

413 "chartType", 

414 ] 

415 

416 def __init__(self, handler: APIHandler): 

417 super().__init__(handler) 

418 self.connection = self.handler.connect() 

419 

420 def select(self, query: ast.Select) -> pd.DataFrame: 

421 project_uuid = self.get_where_param(query, "project_uuid") 

422 space_uuid = self.get_where_param(query, "space_uuid") 

423 data = [] 

424 for row in self.connection.get_space(project_uuid, space_uuid).get("queries", []): 

425 data.append(select_keys(row, self.columns)) 

426 df = pd.DataFrame.from_records(data, columns=self.columns) 

427 return self.apply_query_params(df, query) 

428 

429 

430class ChartHistoryTable(CustomAPITable): 

431 name: str = "chart_history" 

432 columns: List[str] = [ 

433 "createdAt", 

434 "chartUuid", 

435 "versionUuid", 

436 "createdBy", 

437 ] 

438 

439 def __init__(self, handler: APIHandler): 

440 super().__init__(handler) 

441 self.connection = self.handler.connect() 

442 

443 def select(self, query: ast.Select) -> pd.DataFrame: 

444 chart_uuid = self.get_where_param(query, "chart_uuid") 

445 data = [] 

446 for row in self.connection.get_chart_version_history(chart_uuid): 

447 d = select_keys(row, self.columns) 

448 val_to_string(d, "createdBy") 

449 data.append(d) 

450 df = pd.DataFrame.from_records(data, columns=self.columns) 

451 return self.apply_query_params(df, query) 

452 

453 

454class ChartConfigTable(CustomAPITable): 

455 name: str = "chart_config" 

456 columns: List[str] = [ 

457 "legendPosition", 

458 "showLegend", 

459 "groupSortOverrides", 

460 "groupValueOptionOverrides", 

461 "groupColorOverrides", 

462 "groupLabelOverrides", 

463 "showPercentage", 

464 "showValue", 

465 "valueLabel", 

466 "isDonut", 

467 "metricId", 

468 "groupFieldIds" 

469 ] 

470 

471 def __init__(self, handler: APIHandler): 

472 super().__init__(handler) 

473 self.connection = self.handler.connect() 

474 

475 def select(self, query: ast.Select) -> pd.DataFrame: 

476 chart_uuid = self.get_where_param(query, "chart_uuid") 

477 version_uuid = self.get_where_param(query, "version_uuid") 

478 raw_data = self.connection.get_chart(chart_uuid, version_uuid).get("chart", {}).get("chart_config", {}) 

479 config_data = raw_data.get("config", {}) 

480 val_to_string(config_data, "groupSortOverrides") 

481 val_to_string(config_data, "groupValueOptionOverrides") 

482 val_to_string(config_data, "groupColorOverrides") 

483 val_to_string(config_data, "groupLabelOverrides") 

484 val_to_string(config_data, "groupFieldIds") 

485 config_data = select_keys(config_data, self.columns) 

486 df = pd.DataFrame.from_records([{**config_data, "type": raw_data.get("type", "")}], columns=self.columns) 

487 return self.apply_query_params(df, query) 

488 

489 

490class ChartAdditionalMetricsTable(CustomAPITable): 

491 name: str = "chart_additional_metrics" 

492 columns: List[str] = [ 

493 "label", 

494 "type", 

495 "description", 

496 "sql", 

497 "hidden", 

498 "round", 

499 "compact", 

500 "format", 

501 "table", 

502 "name", 

503 "index", 

504 "filters", 

505 "baseDimensionName", 

506 "uuid", 

507 "percentile", 

508 ] 

509 

510 def __init__(self, handler: APIHandler): 

511 super().__init__(handler) 

512 self.connection = self.handler.connect() 

513 

514 def select(self, query: ast.Select) -> pd.DataFrame: 

515 chart_uuid = self.get_where_param(query, "chart_uuid") 

516 version_uuid = self.get_where_param(query, "version_uuid") 

517 data = self.connection.get_chart(chart_uuid, version_uuid).get("metricQuery", {}).get("additionalMetrics", []) 

518 for d in data: 

519 val_to_string(data, "filters") 

520 d = select_keys(d, self.columns) 

521 df = pd.DataFrame.from_records(data, columns=self.columns) 

522 return self.apply_query_params(df, query) 

523 

524 

525class ChartTableCalculationsTable(CustomAPITable): 

526 name: str = "chart_table_calculations" 

527 columns: List[str] = [ 

528 "suffix", 

529 "prefix", 

530 "compact", 

531 "currency", 

532 "separator", 

533 "round", 

534 "type", 

535 "sql", 

536 "displayName", 

537 "name", 

538 "index", 

539 ] 

540 

541 def __init__(self, handler: APIHandler): 

542 super().__init__(handler) 

543 self.connection = self.handler.connect() 

544 

545 def select(self, query: ast.Select) -> pd.DataFrame: 

546 chart_uuid = self.get_where_param(query, "chart_uuid") 

547 version_uuid = self.get_where_param(query, "version_uuid") 

548 data = self.connection.get_chart(chart_uuid, version_uuid).get("metricQuery", {}).get("tableCalculations", []) 

549 for d in data: 

550 move_under(d, "format") 

551 d = select_keys(d, self.columns) 

552 df = pd.DataFrame.from_records(data, columns=self.columns) 

553 return self.apply_query_params(df, query) 

554 

555 

556class SchedulerLogsTable(CustomAPITable): 

557 name: str = "scheduler_logs" 

558 columns: List[str] = [ 

559 "details", 

560 "targetType", 

561 "target", 

562 "status", 

563 "createdAt", 

564 "scheduledTime", 

565 "jobGroup", 

566 "jobId", 

567 "schedulerUuid", 

568 "task", 

569 ] 

570 

571 def __init__(self, handler: APIHandler): 

572 super().__init__(handler) 

573 self.connection = self.handler.connect() 

574 

575 def select(self, query: ast.Select) -> pd.DataFrame: 

576 project_uuid = self.get_where_param(query, "project_uuid") 

577 data = self.connection.get_scheduler_logs(project_uuid).get("logs", []) 

578 for d in data: 

579 val_to_string(d, "details") 

580 d = select_keys(d, self.columns) 

581 df = pd.DataFrame.from_records(data, columns=self.columns) 

582 return self.apply_query_params(df, query) 

583 

584 

585class SchedulerTable(CustomAPITable): 

586 name: str = "scheduler" 

587 columns: List[str] = [ 

588 "options", 

589 "dashboardUuid", 

590 "savedChartUuid", 

591 "cron", 

592 "format", 

593 "createdBy", 

594 "updatedAt", 

595 "createdAt", 

596 "message", 

597 "name", 

598 "schedulerUuid", 

599 ] 

600 

601 def __init__(self, handler: APIHandler): 

602 super().__init__(handler) 

603 self.connection = self.handler.connect() 

604 

605 def select(self, query: ast.Select) -> pd.DataFrame: 

606 scheduler_uuid = self.get_where_param(query, "scheduler_uuid") 

607 data = select_keys(self.connection.get_scheduler(scheduler_uuid), self.columns) 

608 val_to_string(data, "options") 

609 df = pd.DataFrame.from_records([data], columns=self.columns) 

610 return self.apply_query_params(df, query) 

611 

612 

613class SchedulerJobsTable(CustomAPITable): 

614 name: str = "scheduler_jobs" 

615 columns: List[str] = [ 

616 "id", 

617 "date", 

618 ] 

619 

620 def __init__(self, handler: APIHandler): 

621 super().__init__(handler) 

622 self.connection = self.handler.connect() 

623 

624 def select(self, query: ast.Select) -> pd.DataFrame: 

625 scheduler_uuid = self.get_where_param(query, "scheduler_uuid") 

626 data = self.connection.get_scheduler_jobs(scheduler_uuid) 

627 for d in data: 

628 d = select_keys(d, self.columns) 

629 df = pd.DataFrame.from_records(data, columns=self.columns) 

630 return self.apply_query_params(df, query) 

631 

632 

633class SchedulerJobStatus(CustomAPITable): 

634 name: str = "scheduler_job_status" 

635 columns: List[str] = [ 

636 "status", 

637 ] 

638 

639 def __init__(self, handler: APIHandler): 

640 super().__init__(handler) 

641 self.connection = self.handler.connect() 

642 

643 def select(self, query: ast.Select) -> pd.DataFrame: 

644 job_id = self.get_where_param(query, "job_id") 

645 data = self.connection.get_scheduler_jobs(job_id) 

646 data = select_keys(data, self.columns) 

647 df = pd.DataFrame.from_records([data], columns=self.columns) 

648 return self.apply_query_params(df, query)