Coverage for mindsdb / integrations / handlers / eventbrite_handler / eventbrite_tables.py: 0%

241 statements  

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

1import pandas as pd 

2import collections 

3from mindsdb.integrations.libs.api_handler import APITable 

4from mindsdb_sql_parser import ast 

5from mindsdb.integrations.utilities.sql_utils import extract_comparison_conditions 

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

7 SELECTQueryParser, 

8 SELECTQueryExecutor, 

9) 

10 

11 

12def flatten(d, parent_key="", sep="_"): 

13 items = [] 

14 for k, v in d.items(): 

15 new_key = parent_key + sep + k if parent_key else k 

16 if isinstance(v, collections.MutableMapping): 

17 items.extend(flatten(v, new_key, sep=sep).items()) 

18 else: 

19 items.append((new_key, v)) 

20 return dict(items) 

21 

22 

23class EventbriteUserTable(APITable): 

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

25 self.handler.connect() 

26 

27 if query.limit is not None: 

28 raise NotImplementedError("Limit is not supported for user info table") 

29 

30 user_info = self.handler.api.get_current_user() 

31 

32 # Normalize email field 

33 if "emails" in user_info and isinstance(user_info["emails"], list): 

34 user_info["email"] = ( 

35 user_info["emails"][0]["email"] if user_info["emails"] else None 

36 ) 

37 user_info["email_verified"] = ( 

38 user_info["emails"][0]["verified"] if user_info["emails"] else None 

39 ) 

40 user_info["email_primary"] = ( 

41 user_info["emails"][0]["primary"] if user_info["emails"] else None 

42 ) 

43 del user_info["emails"] 

44 else: 

45 user_info["email"] = None 

46 user_info["email_verified"] = None 

47 user_info["email_primary"] = None 

48 

49 data = pd.DataFrame([user_info]) 

50 

51 # Select columns based on query 

52 columns = self.get_columns() 

53 selected_columns = [ 

54 target.parts[-1] 

55 for target in query.targets 

56 if isinstance(target, ast.Identifier) 

57 ] 

58 if selected_columns: 

59 columns = [col for col in columns if col in selected_columns] 

60 data = data[columns] 

61 

62 return data 

63 

64 def get_columns(self): 

65 return [ 

66 "email", 

67 "email_verified", 

68 "email_primary", 

69 "id", 

70 "name", 

71 "first_name", 

72 "last_name", 

73 "is_public", 

74 "image_id", 

75 ] 

76 

77 

78class EventbriteOrganizationTable(APITable): 

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

80 self.handler.connect() 

81 

82 organization_info = self.handler.api.get_user_organizations() 

83 

84 # Normalize organization data 

85 organizations = organization_info.get("organizations", []) 

86 result = pd.DataFrame(organizations) 

87 

88 # filter targets 

89 columns = [] 

90 for target in query.targets: 

91 if isinstance(target, ast.Star): 

92 columns = [] 

93 break 

94 elif isinstance(target, ast.Identifier): 

95 columns.append(target.parts[-1]) 

96 else: 

97 raise NotImplementedError 

98 

99 if len(columns) == 0: 

100 columns = self.get_columns() 

101 

102 # columns to lower case 

103 columns = [name.lower() for name in columns] 

104 

105 if len(result) == 0: 

106 result = pd.DataFrame([], columns=columns) 

107 else: 

108 # add absent columns 

109 for col in set(columns) & set(result.columns) ^ set(columns): 

110 result[col] = None 

111 

112 # filter by columns 

113 result = result[columns] 

114 

115 return result 

116 

117 def get_columns(self): 

118 return [ 

119 "_type", 

120 "name", 

121 "vertical", 

122 "parent_id", 

123 "locale", 

124 "created", 

125 "image_id", 

126 "id", 

127 ] 

128 

129 

130class EventbriteCategoryTable(APITable): 

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

132 self.handler.connect() 

133 

134 category_info = self.handler.api.list_categories() 

135 categories = category_info.get("categories", []) 

136 result = pd.DataFrame(categories) 

137 

138 select_statement_parser = SELECTQueryParser( 

139 query, "categoryInfoTable", self.get_columns() 

140 ) 

141 

142 ( 

143 selected_columns, 

144 where_conditions, 

145 order_by_conditions, 

146 result_limit, 

147 ) = select_statement_parser.parse_query() 

148 

149 total_results = result_limit if result_limit else 100 

150 

151 # filter targets 

152 columns = [] 

153 for target in query.targets: 

154 if isinstance(target, ast.Star): 

155 columns = [] 

156 break 

157 elif isinstance(target, ast.Identifier): 

158 columns.append(target.parts[-1]) 

159 else: 

160 raise NotImplementedError 

161 

162 if len(columns) == 0: 

163 columns = self.get_columns() 

164 

165 # columns to lower case 

166 columns = [name.lower() for name in columns] 

167 

168 if len(result) == 0: 

169 result = pd.DataFrame([], columns=columns) 

170 else: 

171 # add absent columns 

172 for col in set(columns) & set(result.columns) ^ set(columns): 

173 result[col] = None 

174 

175 # filter by columns 

176 result = result[columns] 

177 

178 select_statement_executor = SELECTQueryExecutor( 

179 result, selected_columns, where_conditions, order_by_conditions 

180 ) 

181 

182 result = select_statement_executor.execute_query() 

183 

184 return result.head(total_results) 

185 

186 def get_columns(self): 

187 return [ 

188 "resource_uri", 

189 "id", 

190 "name", 

191 "name_localized", 

192 "short_name", 

193 "short_name_localized", 

194 ] 

195 

196 

197class EventbriteSubcategoryTable(APITable): 

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

199 self.handler.connect() 

200 

201 # Dummy function to simulate API call and response 

202 category_info = self.handler.api.list_subcategories() 

203 

204 # Normalizing the category data 

205 categories = category_info.get("subcategories", []) 

206 result = pd.DataFrame(categories) 

207 

208 select_statement_parser = SELECTQueryParser( 

209 query, "subcategoryInfoTable", self.get_columns() 

210 ) 

211 

212 ( 

213 selected_columns, 

214 where_conditions, 

215 order_by_conditions, 

216 result_limit, 

217 ) = select_statement_parser.parse_query() 

218 

219 total_results = result_limit if result_limit else 100 

220 

221 # Normalize nested fields 

222 parent_category = result["parent_category"].apply(pd.Series) 

223 parent_category.columns = [f"parent_{col}" for col in parent_category.columns] 

224 result = pd.concat([result, parent_category], axis=1).drop( 

225 "parent_category", axis=1 

226 ) 

227 

228 # filter targets 

229 columns = [] 

230 for target in query.targets: 

231 if isinstance(target, ast.Star): 

232 columns = [] 

233 break 

234 elif isinstance(target, ast.Identifier): 

235 columns.append(target.parts[-1]) 

236 else: 

237 raise NotImplementedError 

238 

239 if len(columns) == 0: 

240 columns = self.get_columns() 

241 

242 # columns to lower case 

243 columns = [name.lower() for name in columns] 

244 

245 if len(result) == 0: 

246 result = pd.DataFrame([], columns=columns) 

247 else: 

248 # add absent columns 

249 for col in set(columns) & set(result.columns) ^ set(columns): 

250 result[col] = None 

251 

252 # filter by columns 

253 result = result[columns] 

254 

255 select_statement_executor = SELECTQueryExecutor( 

256 result, selected_columns, where_conditions, order_by_conditions 

257 ) 

258 

259 result = select_statement_executor.execute_query() 

260 

261 return result.head(total_results) 

262 

263 def get_columns(self): 

264 return [ 

265 "resource_uri", 

266 "id", 

267 "name", 

268 "name_localized", 

269 "parent_category", 

270 "parent_resource_uri", 

271 "parent_id", 

272 "parent_name", 

273 "parent_name_localized", 

274 "parent_short_name", 

275 "parent_short_name_localized", 

276 ] 

277 

278 

279class EventbriteFormatTable(APITable): 

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

281 self.handler.connect() 

282 

283 # Simulate API call and get the response 

284 format_info = self.handler.api.list_formats() 

285 

286 # Normalize format data 

287 formats = format_info.get("formats", []) 

288 result = pd.DataFrame(formats) 

289 

290 select_statement_parser = SELECTQueryParser( 

291 query, "formatInfoTable", self.get_columns() 

292 ) 

293 

294 ( 

295 selected_columns, 

296 where_conditions, 

297 order_by_conditions, 

298 result_limit, 

299 ) = select_statement_parser.parse_query() 

300 

301 total_results = result_limit if result_limit else 100 

302 

303 # filter targets 

304 columns = [] 

305 for target in query.targets: 

306 if isinstance(target, ast.Star): 

307 columns = [] 

308 break 

309 elif isinstance(target, ast.Identifier): 

310 columns.append(target.parts[-1]) 

311 else: 

312 raise NotImplementedError 

313 

314 if len(columns) == 0: 

315 columns = self.get_columns() 

316 

317 # columns to lower case 

318 columns = [name.lower() for name in columns] 

319 

320 if len(result) == 0: 

321 result = pd.DataFrame([], columns=columns) 

322 else: 

323 # add absent columns 

324 for col in set(columns) & set(result.columns) ^ set(columns): 

325 result[col] = None 

326 

327 # filter by columns 

328 result = result[columns] 

329 

330 select_statement_executor = SELECTQueryExecutor( 

331 result, selected_columns, where_conditions, order_by_conditions 

332 ) 

333 

334 result = select_statement_executor.execute_query() 

335 

336 return result.head(total_results) 

337 

338 def get_columns(self): 

339 return [ 

340 "resource_uri", 

341 "id", 

342 "name", 

343 "name_localized", 

344 "short_name", 

345 "short_name_localized", 

346 ] 

347 

348 

349class EventbriteEventDetailsTable(APITable): 

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

351 self.handler.connect() 

352 

353 conditions = extract_comparison_conditions(query.where) 

354 allowed_keys = set(["event_id"]) 

355 

356 params = {} 

357 filters = [] 

358 for op, arg1, arg2 in conditions: 

359 if op == "or": 

360 raise NotImplementedError("OR is not supported") 

361 elif op == "=" and arg1 in allowed_keys: 

362 params[arg1] = arg2 

363 elif op != "=": 

364 raise NotImplementedError(f"Unknown op: {op}") 

365 else: 

366 filters.append([op, arg1, arg2]) 

367 

368 if query.limit is not None: 

369 params["max_results"] = query.limit.value 

370 

371 if "event_id" not in params: 

372 # search not works without searchQuery, use 'London' 

373 params["event_id"] = "717926867587" 

374 

375 event_details = self.handler.api.get_event(params["event_id"]) 

376 

377 # Normalize event data 

378 flat_event_details = flatten(event_details) 

379 result = pd.DataFrame([flat_event_details]) 

380 

381 for col in ["name", "description", "start", "end", "logo"]: 

382 if col in result.columns: 

383 result = pd.concat( 

384 [result, result[col].apply(pd.Series).add_prefix(f"{col}_")], 

385 axis=1, 

386 ) 

387 result = result.drop([col], axis=1) 

388 

389 # filter targets 

390 columns = [] 

391 for target in query.targets: 

392 if isinstance(target, ast.Star): 

393 columns = [] 

394 break 

395 elif isinstance(target, ast.Identifier): 

396 columns.append(target.parts[-1]) 

397 else: 

398 raise NotImplementedError 

399 

400 if len(columns) == 0: 

401 columns = self.get_columns() 

402 

403 # columns to lower case 

404 columns = [name.lower() for name in columns] 

405 

406 if len(result) == 0: 

407 result = pd.DataFrame([], columns=columns) 

408 else: 

409 # add absent columns 

410 for col in set(columns) & set(result.columns) ^ set(columns): 

411 result[col] = None 

412 

413 # filter by columns 

414 result = result[columns] 

415 return result 

416 

417 def get_columns(self): 

418 return [ 

419 "name_text", 

420 "name_html", 

421 "description_text", 

422 "description_html", 

423 "url", 

424 "start_timezone", 

425 "start_local", 

426 "start_utc", 

427 "end_timezone", 

428 "end_local", 

429 "end_utc", 

430 "organization_id", 

431 "created", 

432 "changed", 

433 "published", 

434 "capacity", 

435 "capacity_is_custom", 

436 "status", 

437 "currency", 

438 "listed", 

439 "shareable", 

440 "online_event", 

441 "tx_time_limit", 

442 "hide_start_date", 

443 "hide_end_date", 

444 "locale", 

445 "is_locked", 

446 "privacy_setting", 

447 "is_series", 

448 "is_series_parent", 

449 "inventory_type", 

450 "is_reserved_seating", 

451 "show_pick_a_seat", 

452 "show_seatmap_thumbnail", 

453 "show_colors_in_seatmap_thumbnail", 

454 "source", 

455 "is_free", 

456 "version", 

457 "summary", 

458 "facebook_event_id", 

459 "logo_id", 

460 "organizer_id", 

461 "venue_id", 

462 "category_id", 

463 "subcategory_id", 

464 "format_id", 

465 "id", 

466 "resource_uri", 

467 "is_externally_ticketed", 

468 "logo_crop_mask", 

469 "logo_original", 

470 "logo_id", 

471 "logo_url", 

472 "logo_aspect_ratio", 

473 "logo_edge_color", 

474 "logo_edge_color_set", 

475 ] 

476 

477 

478class EventbriteEventsTable(APITable): 

479 def __init__(self, handler): 

480 self.handler = handler 

481 

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

483 self.handler.connect() 

484 

485 conditions = extract_comparison_conditions(query.where) 

486 allowed_keys = set(["organization_id"]) 

487 

488 params = {} 

489 for op, arg1, arg2 in conditions: 

490 if op == "=" and arg1 in allowed_keys: 

491 params[arg1] = arg2 

492 else: 

493 raise NotImplementedError( 

494 f"Unsupported operation or field: {op} {arg1}" 

495 ) 

496 

497 if "organization_id" not in params: 

498 raise ValueError("Organization ID must be provided") 

499 

500 event_list = self.handler.api.list_events(params["organization_id"]) 

501 result = pd.DataFrame(event_list["events"]) 

502 

503 # Normalize event data and split nested dictionaries into separate columns 

504 result = pd.concat( 

505 [ 

506 result.drop(["name", "description", "start", "end", "logo"], axis=1), 

507 result["name"].apply(pd.Series).add_prefix("name_"), 

508 result["description"].apply(pd.Series).add_prefix("description_"), 

509 result["start"].apply(pd.Series).add_prefix("start_"), 

510 result["end"].apply(pd.Series).add_prefix("end_"), 

511 result["logo"].apply(pd.Series).add_prefix("logo_"), 

512 ], 

513 axis=1, 

514 ) 

515 

516 select_statement_parser = SELECTQueryParser( 

517 query, "subcategoryInfoTable", self.get_columns() 

518 ) 

519 

520 ( 

521 selected_columns, 

522 where_conditions, 

523 order_by_conditions, 

524 result_limit, 

525 ) = select_statement_parser.parse_query() 

526 

527 total_results = result_limit if result_limit else 100 

528 

529 # filter targets 

530 columns = [] 

531 for target in query.targets: 

532 if isinstance(target, ast.Star): 

533 columns = [] 

534 break 

535 elif isinstance(target, ast.Identifier): 

536 columns.append(target.parts[-1]) 

537 else: 

538 raise NotImplementedError 

539 

540 if len(columns) == 0: 

541 columns = self.get_columns() 

542 

543 # columns to lower case 

544 columns = [name.lower() for name in columns] 

545 

546 if len(result) == 0: 

547 result = pd.DataFrame([], columns=columns) 

548 else: 

549 # add absent columns 

550 for col in set(columns) & set(result.columns) ^ set(columns): 

551 result[col] = None 

552 

553 # filter by columns 

554 result = result[columns] 

555 

556 select_statement_executor = SELECTQueryExecutor( 

557 result, selected_columns, where_conditions, order_by_conditions 

558 ) 

559 

560 result = select_statement_executor.execute_query() 

561 

562 return result.head(total_results) 

563 

564 def get_columns(self): 

565 return [ 

566 "name_text", 

567 "name_html", 

568 "description_text", 

569 "description_html", 

570 "url", 

571 "start_timezone", 

572 "start_local", 

573 "start_utc", 

574 "end_timezone", 

575 "end_local", 

576 "end_utc", 

577 "organization_id", 

578 "created", 

579 "changed", 

580 "published", 

581 "capacity", 

582 "capacity_is_custom", 

583 "status", 

584 "currency", 

585 "listed", 

586 "shareable", 

587 "online_event", 

588 "tx_time_limit", 

589 "hide_start_date", 

590 "hide_end_date", 

591 "locale", 

592 "is_locked", 

593 "privacy_setting", 

594 "is_series", 

595 "is_series_parent", 

596 "inventory_type", 

597 "is_reserved_seating", 

598 "show_pick_a_seat", 

599 "show_seatmap_thumbnail", 

600 "show_colors_in_seatmap_thumbnail", 

601 "source", 

602 "is_free", 

603 "summary", 

604 "organizer_id", 

605 "venue_id", 

606 "category_id", 

607 "subcategory_id", 

608 "format_id", 

609 "id", 

610 "resource_uri", 

611 "is_externally_ticketed", 

612 ]