universal-mcp-applications 0.1.32__py3-none-any.whl → 0.1.36rc2__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 (105) hide show
  1. universal_mcp/applications/ahrefs/app.py +52 -198
  2. universal_mcp/applications/airtable/app.py +23 -122
  3. universal_mcp/applications/apollo/app.py +111 -464
  4. universal_mcp/applications/asana/app.py +417 -1567
  5. universal_mcp/applications/aws_s3/app.py +36 -103
  6. universal_mcp/applications/bill/app.py +546 -1957
  7. universal_mcp/applications/box/app.py +1068 -3981
  8. universal_mcp/applications/braze/app.py +364 -1430
  9. universal_mcp/applications/browser_use/app.py +2 -8
  10. universal_mcp/applications/cal_com_v2/app.py +207 -625
  11. universal_mcp/applications/calendly/app.py +61 -200
  12. universal_mcp/applications/canva/app.py +45 -110
  13. universal_mcp/applications/clickup/app.py +207 -674
  14. universal_mcp/applications/coda/app.py +146 -426
  15. universal_mcp/applications/confluence/app.py +310 -1098
  16. universal_mcp/applications/contentful/app.py +36 -151
  17. universal_mcp/applications/crustdata/app.py +28 -107
  18. universal_mcp/applications/dialpad/app.py +283 -756
  19. universal_mcp/applications/digitalocean/app.py +1766 -5777
  20. universal_mcp/applications/domain_checker/app.py +3 -54
  21. universal_mcp/applications/e2b/app.py +14 -64
  22. universal_mcp/applications/elevenlabs/app.py +9 -47
  23. universal_mcp/applications/exa/app.py +6 -17
  24. universal_mcp/applications/falai/app.py +24 -101
  25. universal_mcp/applications/figma/app.py +53 -137
  26. universal_mcp/applications/file_system/app.py +2 -13
  27. universal_mcp/applications/firecrawl/app.py +51 -152
  28. universal_mcp/applications/fireflies/app.py +59 -281
  29. universal_mcp/applications/fpl/app.py +91 -528
  30. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  31. universal_mcp/applications/fpl/utils/helper.py +25 -89
  32. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  33. universal_mcp/applications/ghost_content/app.py +52 -161
  34. universal_mcp/applications/github/app.py +19 -56
  35. universal_mcp/applications/gong/app.py +88 -248
  36. universal_mcp/applications/google_calendar/app.py +16 -68
  37. universal_mcp/applications/google_docs/app.py +85 -189
  38. universal_mcp/applications/google_drive/app.py +141 -463
  39. universal_mcp/applications/google_gemini/app.py +12 -64
  40. universal_mcp/applications/google_mail/app.py +28 -157
  41. universal_mcp/applications/google_searchconsole/app.py +15 -48
  42. universal_mcp/applications/google_sheet/app.py +100 -581
  43. universal_mcp/applications/google_sheet/helper.py +10 -37
  44. universal_mcp/applications/hashnode/app.py +57 -269
  45. universal_mcp/applications/heygen/app.py +44 -122
  46. universal_mcp/applications/http_tools/app.py +10 -32
  47. universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
  48. universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
  49. universal_mcp/applications/hubspot/app.py +23 -87
  50. universal_mcp/applications/jira/app.py +2071 -7986
  51. universal_mcp/applications/klaviyo/app.py +494 -1376
  52. universal_mcp/applications/linkedin/README.md +9 -2
  53. universal_mcp/applications/linkedin/app.py +240 -181
  54. universal_mcp/applications/mailchimp/app.py +450 -1605
  55. universal_mcp/applications/markitdown/app.py +8 -20
  56. universal_mcp/applications/miro/app.py +217 -699
  57. universal_mcp/applications/ms_teams/app.py +64 -186
  58. universal_mcp/applications/neon/app.py +86 -192
  59. universal_mcp/applications/notion/app.py +21 -36
  60. universal_mcp/applications/onedrive/app.py +16 -38
  61. universal_mcp/applications/openai/app.py +42 -165
  62. universal_mcp/applications/outlook/app.py +24 -84
  63. universal_mcp/applications/perplexity/app.py +4 -19
  64. universal_mcp/applications/pipedrive/app.py +832 -3142
  65. universal_mcp/applications/posthog/app.py +163 -432
  66. universal_mcp/applications/reddit/app.py +40 -139
  67. universal_mcp/applications/resend/app.py +41 -107
  68. universal_mcp/applications/retell/app.py +14 -41
  69. universal_mcp/applications/rocketlane/app.py +221 -934
  70. universal_mcp/applications/scraper/README.md +7 -4
  71. universal_mcp/applications/scraper/app.py +50 -109
  72. universal_mcp/applications/semanticscholar/app.py +22 -64
  73. universal_mcp/applications/semrush/app.py +43 -77
  74. universal_mcp/applications/sendgrid/app.py +512 -1262
  75. universal_mcp/applications/sentry/app.py +271 -906
  76. universal_mcp/applications/serpapi/app.py +40 -143
  77. universal_mcp/applications/sharepoint/app.py +17 -39
  78. universal_mcp/applications/shopify/app.py +1551 -4287
  79. universal_mcp/applications/shortcut/app.py +155 -417
  80. universal_mcp/applications/slack/app.py +33 -115
  81. universal_mcp/applications/spotify/app.py +126 -325
  82. universal_mcp/applications/supabase/app.py +104 -213
  83. universal_mcp/applications/tavily/app.py +1 -1
  84. universal_mcp/applications/trello/app.py +693 -2656
  85. universal_mcp/applications/twilio/app.py +14 -50
  86. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  87. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  88. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  89. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  90. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  91. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  92. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  93. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  94. universal_mcp/applications/whatsapp/app.py +35 -186
  95. universal_mcp/applications/whatsapp/audio.py +2 -6
  96. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  97. universal_mcp/applications/whatsapp_business/app.py +70 -283
  98. universal_mcp/applications/wrike/app.py +45 -118
  99. universal_mcp/applications/yahoo_finance/app.py +19 -65
  100. universal_mcp/applications/youtube/app.py +75 -261
  101. universal_mcp/applications/zenquotes/app.py +2 -2
  102. {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/METADATA +2 -2
  103. {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/RECORD +105 -105
  104. {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/WHEEL +0 -0
  105. {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/licenses/LICENSE +0 -0
@@ -9,9 +9,7 @@ from .api import api
9
9
  logger = logging.getLogger("fpl-mcp-server.fixtures")
10
10
 
11
11
 
12
- def get_fixtures_resource(
13
- gameweek_id: int | None = None, team_name: str | None = None
14
- ) -> list[dict[str, Any]]:
12
+ def get_fixtures_resource(gameweek_id: int | None = None, team_name: str | None = None) -> list[dict[str, Any]]:
15
13
  """Get fixtures from the FPL API with optional filtering by gameweek or team
16
14
 
17
15
  Args:
@@ -68,9 +66,7 @@ def get_fixtures_resource(
68
66
 
69
67
  # Apply gameweek filter if provided
70
68
  if gameweek_id is not None:
71
- formatted_fixtures = [
72
- f for f in formatted_fixtures if f["gameweek"] == gameweek_id
73
- ]
69
+ formatted_fixtures = [f for f in formatted_fixtures if f["gameweek"] == gameweek_id]
74
70
 
75
71
  # Apply team filter if provided
76
72
  if team_name is not None:
@@ -109,9 +105,7 @@ def get_player_fixtures(player_id: int, num_fixtures: int = 5) -> list[dict[str,
109
105
  Returns:
110
106
  List of upcoming fixtures for the player
111
107
  """
112
- logger.info(
113
- f"Getting player fixtures (player_id={player_id}, num_fixtures={num_fixtures})"
114
- )
108
+ logger.info(f"Getting player fixtures (player_id={player_id}, num_fixtures={num_fixtures})")
115
109
 
116
110
  # Get player data to find their team
117
111
  players_data = api.get_players()
@@ -190,9 +184,7 @@ def get_player_fixtures(player_id: int, num_fixtures: int = 5) -> list[dict[str,
190
184
  opponent_team = team_map.get(opponent_id, {})
191
185
 
192
186
  # Determine difficulty - higher is more difficult
193
- difficulty = fixture.get(
194
- "team_h_difficulty" if is_home else "team_a_difficulty", 3
195
- )
187
+ difficulty = fixture.get("team_h_difficulty" if is_home else "team_a_difficulty", 3)
196
188
 
197
189
  formatted_fixture = {
198
190
  "gameweek": fixture.get("event"),
@@ -218,9 +210,7 @@ def analyze_player_fixtures(player_id: int, num_fixtures: int = 5) -> dict[str,
218
210
  Returns:
219
211
  Analysis of player's upcoming fixtures with difficulty ratings
220
212
  """
221
- logger.info(
222
- f"Analyzing player fixtures (player_id={player_id}, num_fixtures={num_fixtures})"
223
- )
213
+ logger.info(f"Analyzing player fixtures (player_id={player_id}, num_fixtures={num_fixtures})")
224
214
 
225
215
  # Get player data
226
216
  players_data = api.get_players()
@@ -258,9 +248,7 @@ def analyze_player_fixtures(player_id: int, num_fixtures: int = 5) -> dict[str,
258
248
  position_info = position_map.get(position_id, {})
259
249
  position_code = position_info.get("singular_name_short", "Unknown position")
260
250
 
261
- logger.info(
262
- "Player %s plays as %s for %s", player.get("web_name"), position_code, team_name
263
- )
251
+ logger.info("Player %s plays as %s for %s", player.get("web_name"), position_code, team_name)
264
252
 
265
253
  # Make sure position is one of GK, DEF, MID, FWD
266
254
  position_mapping = {"GKP": "GK", "DEF": "DEF", "MID": "MID", "FWD": "FWD"}
@@ -360,11 +348,7 @@ def get_blank_gameweeks(num_gameweeks: int = 5) -> list[dict[str, Any]]:
360
348
  current_gw_id = current_gw["id"]
361
349
 
362
350
  # Limit to specified number of upcoming gameweeks
363
- upcoming_gameweeks = [
364
- gw
365
- for gw in all_gameweeks
366
- if gw["id"] >= current_gw_id and gw["id"] < current_gw_id + num_gameweeks
367
- ]
351
+ upcoming_gameweeks = [gw for gw in all_gameweeks if gw["id"] >= current_gw_id and gw["id"] < current_gw_id + num_gameweeks]
368
352
 
369
353
  # Map team IDs to names
370
354
  team_map = {t["id"]: t for t in team_data}
@@ -439,11 +423,7 @@ def get_double_gameweeks(num_gameweeks: int = 5) -> list[dict[str, Any]]:
439
423
  current_gw_id = current_gw["id"]
440
424
 
441
425
  # Limit to specified number of upcoming gameweeks
442
- upcoming_gameweeks = [
443
- gw
444
- for gw in all_gameweeks
445
- if gw["id"] >= current_gw_id and gw["id"] < current_gw_id + num_gameweeks
446
- ]
426
+ upcoming_gameweeks = [gw for gw in all_gameweeks if gw["id"] >= current_gw_id and gw["id"] < current_gw_id + num_gameweeks]
447
427
 
448
428
  # Map team IDs to names
449
429
  team_map = {t["id"]: t for t in team_data}
@@ -495,9 +475,7 @@ def get_double_gameweeks(num_gameweeks: int = 5) -> list[dict[str, Any]]:
495
475
  return double_gameweeks
496
476
 
497
477
 
498
- def get_player_gameweek_history(
499
- player_ids: list[int], num_gameweeks: int = 5
500
- ) -> dict[str, Any]:
478
+ def get_player_gameweek_history(player_ids: list[int], num_gameweeks: int = 5) -> dict[str, Any]:
501
479
  """Get recent gameweek history for multiple players.
502
480
 
503
481
  Args:
@@ -508,9 +486,7 @@ def get_player_gameweek_history(
508
486
  Dictionary mapping player IDs to their gameweek histories
509
487
  """
510
488
  logger = logging.getLogger(__name__)
511
- logger.info(
512
- f"Getting gameweek history for {len(player_ids)} players, {num_gameweeks} gameweeks"
513
- )
489
+ logger.info(f"Getting gameweek history for {len(player_ids)} players, {num_gameweeks} gameweeks")
514
490
 
515
491
  # Get current gameweek to determine range
516
492
  gameweeks = api.get_gameweeks()
@@ -571,28 +547,18 @@ def get_player_gameweek_history(
571
547
  # Added additional stats as requested
572
548
  "expected_goals": entry.get("expected_goals", 0),
573
549
  "expected_assists": entry.get("expected_assists", 0),
574
- "expected_goal_involvements": entry.get(
575
- "expected_goal_involvements", 0
576
- ),
577
- "expected_goals_conceded": entry.get(
578
- "expected_goals_conceded", 0
579
- ),
550
+ "expected_goal_involvements": entry.get("expected_goal_involvements", 0),
551
+ "expected_goals_conceded": entry.get("expected_goals_conceded", 0),
580
552
  "transfers_in": entry.get("transfers_in", 0),
581
553
  "transfers_out": entry.get("transfers_out", 0),
582
554
  "selected": entry.get("selected", 0),
583
- "value": entry.get("value", 0) / 10.0
584
- if "value" in entry
585
- else 0,
555
+ "value": entry.get("value", 0) / 10.0 if "value" in entry else 0,
586
556
  "team_score": entry.get(
587
- "team_h_score"
588
- if entry.get("was_home")
589
- else "team_a_score",
557
+ "team_h_score" if entry.get("was_home") else "team_a_score",
590
558
  0,
591
559
  ),
592
560
  "opponent_score": entry.get(
593
- "team_a_score"
594
- if entry.get("was_home")
595
- else "team_h_score",
561
+ "team_a_score" if entry.get("was_home") else "team_h_score",
596
562
  0,
597
563
  ),
598
564
  }
@@ -8,9 +8,7 @@ from .api import api
8
8
  # Resources
9
9
 
10
10
 
11
- def get_players_resource(
12
- name_filter: str | None = None, team_filter: str | None = None
13
- ) -> list[dict[str, Any]]:
11
+ def get_players_resource(name_filter: str | None = None, team_filter: str | None = None) -> list[dict[str, Any]]:
14
12
  """
15
13
  Format player data for the MCP resource.
16
14
 
@@ -83,9 +81,7 @@ def get_players_resource(
83
81
  # Expected stats (if available)
84
82
  "expected_goals": player.get("expected_goals", "N/A"),
85
83
  "expected_assists": player.get("expected_assists", "N/A"),
86
- "expected_goal_involvements": player.get(
87
- "expected_goal_involvements", "N/A"
88
- ),
84
+ "expected_goal_involvements": player.get("expected_goal_involvements", "N/A"),
89
85
  "expected_goals_conceded": player.get("expected_goals_conceded", "N/A"),
90
86
  # Ownership & transfers
91
87
  "selected_by_percent": player["selected_by_percent"],
@@ -281,17 +277,10 @@ def find_players_by_name(name: str, limit: int = 5) -> list[dict[str, Any]]:
281
277
  scored_players.append((total_score, player))
282
278
 
283
279
  # Sort by score (highest first)
284
- sorted_players = [
285
- player for _, player in sorted(scored_players, key=lambda x: x[0], reverse=True)
286
- ]
280
+ sorted_players = [player for _, player in sorted(scored_players, key=lambda x: x[0], reverse=True)]
287
281
  # If no matches with good confidence, fall back to simple contains match
288
282
  if not sorted_players or (sorted_players and scored_players[0][0] < 30):
289
- fallback_players = [
290
- p
291
- for p in all_players
292
- if search_term in p["name"].lower()
293
- or search_term in p.get("web_name", "").lower()
294
- ]
283
+ fallback_players = [p for p in all_players if search_term in p["name"].lower() or search_term in p.get("web_name", "").lower()]
295
284
  # Sort fallback by points
296
285
  fallback_players.sort(key=lambda p: float(p["points"]), reverse=True)
297
286
 
@@ -338,9 +327,7 @@ def get_current_gameweek_resource() -> dict[str, Any]:
338
327
 
339
328
  # Format deadline time to be more readable
340
329
  try:
341
- deadline = datetime.datetime.strptime(
342
- current_gw["deadline_time"], "%Y-%m-%dT%H:%M:%SZ"
343
- )
330
+ deadline = datetime.datetime.strptime(current_gw["deadline_time"], "%Y-%m-%dT%H:%M:%SZ")
344
331
  gw_data["deadline_formatted"] = deadline.strftime("%A, %d %B %Y at %H:%M UTC")
345
332
 
346
333
  # Calculate time until deadline
@@ -419,9 +406,7 @@ def get_player_fixtures(player_id: int, num_fixtures: int = 5) -> list[dict[str,
419
406
  Returns:
420
407
  List of upcoming fixtures for the player
421
408
  """
422
- logger.info(
423
- f"Getting player fixtures (player_id={player_id}, num_fixtures={num_fixtures})"
424
- )
409
+ logger.info(f"Getting player fixtures (player_id={player_id}, num_fixtures={num_fixtures})")
425
410
 
426
411
  # Get player data to find their team
427
412
  players_data = api.get_players()
@@ -500,9 +485,7 @@ def get_player_fixtures(player_id: int, num_fixtures: int = 5) -> list[dict[str,
500
485
  opponent_team = team_map.get(opponent_id, {})
501
486
 
502
487
  # Determine difficulty - higher is more difficult
503
- difficulty = fixture.get(
504
- "team_h_difficulty" if is_home else "team_a_difficulty", 3
505
- )
488
+ difficulty = fixture.get("team_h_difficulty" if is_home else "team_a_difficulty", 3)
506
489
 
507
490
  formatted_fixture = {
508
491
  "gameweek": fixture.get("event"),
@@ -518,9 +501,7 @@ def get_player_fixtures(player_id: int, num_fixtures: int = 5) -> list[dict[str,
518
501
  return formatted_fixtures
519
502
 
520
503
 
521
- def get_player_gameweek_history(
522
- player_ids: list[int], num_gameweeks: int = 5
523
- ) -> dict[str, Any]:
504
+ def get_player_gameweek_history(player_ids: list[int], num_gameweeks: int = 5) -> dict[str, Any]:
524
505
  """Get recent gameweek history for multiple players.
525
506
 
526
507
  Args:
@@ -531,9 +512,7 @@ def get_player_gameweek_history(
531
512
  Dictionary mapping player IDs to their gameweek histories
532
513
  """
533
514
  logger = logging.getLogger(__name__)
534
- logger.info(
535
- f"Getting gameweek history for {len(player_ids)} players, {num_gameweeks} gameweeks"
536
- )
515
+ logger.info(f"Getting gameweek history for {len(player_ids)} players, {num_gameweeks} gameweeks")
537
516
 
538
517
  # Get current gameweek to determine range
539
518
  gameweeks = api.get_gameweeks()
@@ -594,28 +573,18 @@ def get_player_gameweek_history(
594
573
  # Added additional stats as requested
595
574
  "expected_goals": entry.get("expected_goals", 0),
596
575
  "expected_assists": entry.get("expected_assists", 0),
597
- "expected_goal_involvements": entry.get(
598
- "expected_goal_involvements", 0
599
- ),
600
- "expected_goals_conceded": entry.get(
601
- "expected_goals_conceded", 0
602
- ),
576
+ "expected_goal_involvements": entry.get("expected_goal_involvements", 0),
577
+ "expected_goals_conceded": entry.get("expected_goals_conceded", 0),
603
578
  "transfers_in": entry.get("transfers_in", 0),
604
579
  "transfers_out": entry.get("transfers_out", 0),
605
580
  "selected": entry.get("selected", 0),
606
- "value": entry.get("value", 0) / 10.0
607
- if "value" in entry
608
- else 0,
581
+ "value": entry.get("value", 0) / 10.0 if "value" in entry else 0,
609
582
  "team_score": entry.get(
610
- "team_h_score"
611
- if entry.get("was_home")
612
- else "team_a_score",
583
+ "team_h_score" if entry.get("was_home") else "team_a_score",
613
584
  0,
614
585
  ),
615
586
  "opponent_score": entry.get(
616
- "team_a_score"
617
- if entry.get("was_home")
618
- else "team_h_score",
587
+ "team_a_score" if entry.get("was_home") else "team_h_score",
619
588
  0,
620
589
  ),
621
590
  }
@@ -738,11 +707,7 @@ def get_player_info(
738
707
  # Include gameweek history if requested
739
708
  if include_history and "history" in player:
740
709
  # Filter history by gameweek range
741
- filtered_history = [
742
- gw
743
- for gw in player.get("history", [])
744
- if start_gameweek <= gw.get("round", 0) <= end_gameweek
745
- ]
710
+ filtered_history = [gw for gw in player.get("history", []) if start_gameweek <= gw.get("round", 0) <= end_gameweek]
746
711
 
747
712
  # Get detailed gameweek history
748
713
  player_id_value = player.get("id")
@@ -765,11 +730,7 @@ def get_player_info(
765
730
  gw_num = gw_data.get("round")
766
731
  # Find matching detailed gameweek
767
732
  matching_detailed = next(
768
- (
769
- gw
770
- for gw in detailed_history
771
- if gw.get("round") == gw_num or gw.get("gameweek") == gw_num
772
- ),
733
+ (gw for gw in detailed_history if gw.get("round") == gw_num or gw.get("gameweek") == gw_num),
773
734
  None,
774
735
  )
775
736
 
@@ -825,11 +786,7 @@ def get_player_info(
825
786
 
826
787
  # Calculate average fixture difficulty
827
788
  difficulty_values = [f.get("difficulty", 3) for f in fixtures_data]
828
- avg_difficulty = (
829
- sum(difficulty_values) / len(difficulty_values)
830
- if difficulty_values
831
- else 3
832
- )
789
+ avg_difficulty = sum(difficulty_values) / len(difficulty_values) if difficulty_values else 3
833
790
 
834
791
  # Convert to a 1-10 scale where 10 is best (easiest fixtures)
835
792
  fixture_score = (6 - avg_difficulty) * 2
@@ -837,18 +794,12 @@ def get_player_info(
837
794
  result["fixture_analysis"] = {
838
795
  "difficulty_score": round(fixture_score, 1),
839
796
  "fixtures_analyzed": len(fixtures_data),
840
- "home_matches": sum(
841
- 1 for f in fixtures_data if f.get("location") == "home"
842
- ),
843
- "away_matches": sum(
844
- 1 for f in fixtures_data if f.get("location") == "away"
845
- ),
797
+ "home_matches": sum(1 for f in fixtures_data if f.get("location") == "home"),
798
+ "away_matches": sum(1 for f in fixtures_data if f.get("location") == "away"),
846
799
  }
847
800
 
848
801
  # Add fixture difficulty assessment
849
- if "fixture_analysis" in result and isinstance(
850
- result["fixture_analysis"], dict
851
- ):
802
+ if "fixture_analysis" in result and isinstance(result["fixture_analysis"], dict):
852
803
  fixture_analysis = result["fixture_analysis"]
853
804
  if fixture_score >= 8:
854
805
  fixture_analysis["assessment"] = "Excellent fixtures"
@@ -862,9 +813,7 @@ def get_player_info(
862
813
  return result
863
814
 
864
815
 
865
- def search_players(
866
- query: str, position: str | None = None, team: str | None = None, limit: int = 5
867
- ) -> dict[str, Any]:
816
+ def search_players(query: str, position: str | None = None, team: str | None = None, limit: int = 5) -> dict[str, Any]:
868
817
  """
869
818
  Search for players by name with optional filtering by position and team.
870
819
 
@@ -881,9 +830,7 @@ def search_players(
881
830
  logger.info(f"Searching players: query={query}, position={position}, team={team}")
882
831
 
883
832
  # Find players by name
884
- matches = find_players_by_name(
885
- query, limit=limit * 2
886
- ) # Get more than needed for filtering
833
+ matches = find_players_by_name(query, limit=limit * 2) # Get more than needed for filtering
887
834
 
888
835
  # Apply position filter if specified
889
836
  if position and matches:
@@ -891,12 +838,7 @@ def search_players(
891
838
 
892
839
  # Apply team filter if specified
893
840
  if team and matches:
894
- matches = [
895
- p
896
- for p in matches
897
- if team.lower() in p.get("team", "").lower()
898
- or team.lower() in p.get("team_short", "").lower()
899
- ]
841
+ matches = [p for p in matches if team.lower() in p.get("team", "").lower() or team.lower() in p.get("team_short", "").lower()]
900
842
 
901
843
  # Limit results
902
844
  matches = matches[:limit]
@@ -965,18 +907,12 @@ def get_team_by_name(name: str) -> dict[str, Any] | None:
965
907
 
966
908
  # Try exact match first
967
909
  for team in teams:
968
- if (
969
- team["name"].lower() == name_lower
970
- or team["short_name"].lower() == name_lower
971
- ):
910
+ if team["name"].lower() == name_lower or team["short_name"].lower() == name_lower:
972
911
  return team
973
912
 
974
913
  # Then try partial match
975
914
  for team in teams:
976
- if (
977
- name_lower in team["name"].lower()
978
- or name_lower in team["short_name"].lower()
979
- ):
915
+ if name_lower in team["name"].lower() or name_lower in team["short_name"].lower():
980
916
  return team
981
917
 
982
918
  return None
@@ -20,12 +20,8 @@ def parse_league_standings(data: dict[str, Any]) -> dict[str, Any]:
20
20
  "id": data.get("league", {}).get("id"),
21
21
  "name": data.get("league", {}).get("name"),
22
22
  "created": data.get("league", {}).get("created"),
23
- "type": "Public"
24
- if data.get("league", {}).get("league_type") == "s"
25
- else "Private",
26
- "scoring": "Classic"
27
- if data.get("league", {}).get("scoring") == "c"
28
- else "Head-to-Head",
23
+ "type": "Public" if data.get("league", {}).get("league_type") == "s" else "Private",
24
+ "scoring": "Classic" if data.get("league", {}).get("scoring") == "c" else "Head-to-Head",
29
25
  "admin_entry": data.get("league", {}).get("admin_entry"),
30
26
  "start_event": data.get("league", {}).get("start_event"),
31
27
  }
@@ -66,9 +62,7 @@ def parse_league_standings(data: dict[str, Any]) -> dict[str, Any]:
66
62
  return response
67
63
 
68
64
 
69
- def get_teams_historical_data(
70
- team_ids: list[int], api, start_gw: int | None = None, end_gw: int | None = None
71
- ) -> dict[str, Any]:
65
+ def get_teams_historical_data(team_ids: list[int], api, start_gw: int | None = None, end_gw: int | None = None) -> dict[str, Any]:
72
66
  """Get historical data for multiple teams"""
73
67
  results = {}
74
68
  errors = {}
@@ -114,11 +108,7 @@ def get_teams_historical_data(
114
108
  history_data = api._make_request(f"entry/{team_id}/history/")
115
109
 
116
110
  if "current" in history_data:
117
- current = [
118
- gw
119
- for gw in history_data["current"]
120
- if start_gw <= gw.get("event", 0) <= end_gw
121
- ]
111
+ current = [gw for gw in history_data["current"] if start_gw <= gw.get("event", 0) <= end_gw]
122
112
 
123
113
  filtered_data = {
124
114
  "current": current,
@@ -158,12 +148,8 @@ def _get_league_standings(league_id: int, api) -> dict[str, Any]:
158
148
  "id": data.get("league", {}).get("id"),
159
149
  "name": data.get("league", {}).get("name"),
160
150
  "created": data.get("league", {}).get("created"),
161
- "type": "Public"
162
- if data.get("league", {}).get("league_type") == "s"
163
- else "Private",
164
- "scoring": "Classic"
165
- if data.get("league", {}).get("scoring") == "c"
166
- else "Head-to-Head",
151
+ "type": "Public" if data.get("league", {}).get("league_type") == "s" else "Private",
152
+ "scoring": "Classic" if data.get("league", {}).get("scoring") == "c" else "Head-to-Head",
167
153
  "admin_entry": data.get("league", {}).get("admin_entry"),
168
154
  "start_event": data.get("league", {}).get("start_event"),
169
155
  }
@@ -247,9 +233,7 @@ def _get_league_historical_performance(
247
233
  if gw_data:
248
234
  points_series.append(gw_data.get("points", 0))
249
235
  rank_series.append(gw_data.get("overall_rank", 0))
250
- value_series.append(
251
- gw_data.get("value", 0) / 10.0 if gw_data.get("value") else 0
252
- )
236
+ value_series.append(gw_data.get("value", 0) / 10.0 if gw_data.get("value") else 0)
253
237
  else:
254
238
  points_series.append(0)
255
239
  rank_series.append(0)
@@ -292,9 +276,7 @@ def _get_league_historical_performance(
292
276
 
293
277
  if valid_ranks:
294
278
  mean_rank = sum(valid_ranks) / len(valid_ranks)
295
- rank_variance = sum((r - mean_rank) ** 2 for r in valid_ranks) / len(
296
- valid_ranks
297
- )
279
+ rank_variance = sum((r - mean_rank) ** 2 for r in valid_ranks) / len(valid_ranks)
298
280
  consistency_score = 10.0 - min(10.0, (rank_variance / 1000000) * 10)
299
281
  team["consistency_score"] = round(consistency_score, 1)
300
282
  else:
@@ -381,9 +363,7 @@ def _get_league_team_composition(
381
363
  team_values = {}
382
364
 
383
365
  for team_id, team_data in teams_data.items():
384
- team_info = next(
385
- (t for t in league_data["standings"] if t["team_id"] == team_id), None
386
- )
366
+ team_info = next((t for t in league_data["standings"] if t["team_id"] == team_id), None)
387
367
  if not team_info:
388
368
  continue
389
369
 
@@ -415,16 +395,10 @@ def _get_league_team_composition(
415
395
  "full_name": full_name.strip() or "Unknown",
416
396
  "position": position,
417
397
  "team": player_data.get("team_short", "UNK"),
418
- "price": player_data.get("now_cost", 0) / 10.0
419
- if player_data.get("now_cost")
420
- else 0,
421
- "form": float(player_data.get("form", "0.0"))
422
- if player_data.get("form")
423
- else 0,
398
+ "price": player_data.get("now_cost", 0) / 10.0 if player_data.get("now_cost") else 0,
399
+ "form": float(player_data.get("form", "0.0")) if player_data.get("form") else 0,
424
400
  "total_points": player_data.get("total_points", 0),
425
- "points_per_game": float(player_data.get("points_per_game", "0.0"))
426
- if player_data.get("points_per_game")
427
- else 0,
401
+ "points_per_game": float(player_data.get("points_per_game", "0.0")) if player_data.get("points_per_game") else 0,
428
402
  "ownership_count": 0,
429
403
  "ownership_percent": 0,
430
404
  "captain_count": 0,
@@ -465,9 +439,7 @@ def _get_league_team_composition(
465
439
 
466
440
  team_count = len(teams_data)
467
441
  for player_id, player in player_ownership.items():
468
- player["ownership_percent"] = round(
469
- (player["ownership_count"] / team_count) * 100, 1
470
- )
442
+ player["ownership_percent"] = round((player["ownership_count"] / team_count) * 100, 1)
471
443
 
472
444
  players_by_ownership = sorted(
473
445
  player_ownership.values(),
@@ -478,27 +450,15 @@ def _get_league_team_composition(
478
450
  template_threshold = 30.0
479
451
  differential_threshold = 10.0
480
452
 
481
- template_players = [
482
- p for p in players_by_ownership if p["ownership_percent"] > template_threshold
483
- ]
484
- differential_players = [
485
- p
486
- for p in players_by_ownership
487
- if 0 < p["ownership_percent"] < differential_threshold
488
- ]
453
+ template_players = [p for p in players_by_ownership if p["ownership_percent"] > template_threshold]
454
+ differential_players = [p for p in players_by_ownership if 0 < p["ownership_percent"] < differential_threshold]
489
455
 
490
456
  position_order = {"GKP": 1, "DEF": 2, "MID": 3, "FWD": 4, "UNK": 5}
491
- template_players.sort(
492
- key=lambda p: (position_order.get(p["position"], 5), -p["ownership_percent"])
493
- )
494
- differential_players.sort(
495
- key=lambda p: (position_order.get(p["position"], 5), -p["total_points"])
496
- )
457
+ template_players.sort(key=lambda p: (position_order.get(p["position"], 5), -p["ownership_percent"]))
458
+ differential_players.sort(key=lambda p: (position_order.get(p["position"], 5), -p["total_points"]))
497
459
 
498
460
  captain_data = []
499
- for player_id, count in sorted(
500
- captain_picks.items(), key=lambda x: x[1], reverse=True
501
- ):
461
+ for player_id, count in sorted(captain_picks.items(), key=lambda x: x[1], reverse=True):
502
462
  if player_id in player_ownership:
503
463
  player = player_ownership[player_id]
504
464
  captain_data.append(
@@ -522,9 +482,7 @@ def _get_league_team_composition(
522
482
  "team_value": value_data["value"],
523
483
  }
524
484
 
525
- sorted_value_stats = sorted(
526
- value_stats.values(), key=lambda v: v["team_value"], reverse=True
527
- )
485
+ sorted_value_stats = sorted(value_stats.values(), key=lambda v: v["team_value"], reverse=True)
528
486
 
529
487
  PLAYER_LIMIT = 25
530
488
 
@@ -536,9 +494,7 @@ def _get_league_team_composition(
536
494
  "all_players": players_by_ownership[:PLAYER_LIMIT],
537
495
  "template_players": template_players[:PLAYER_LIMIT],
538
496
  "differential_players": differential_players[:PLAYER_LIMIT],
539
- "captain_picks": captain_data[:PLAYER_LIMIT]
540
- if len(captain_data) > PLAYER_LIMIT
541
- else captain_data,
497
+ "captain_picks": captain_data[:PLAYER_LIMIT] if len(captain_data) > PLAYER_LIMIT else captain_data,
542
498
  },
543
499
  "team_values": sorted_value_stats,
544
500
  "errors": errors,