Merge development branch into main - resolved conflict in ncaa_fb_managers.py
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 11 KiB |
BIN
assets/broadcast_logos/paramount-plus.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/broadcast_logos/prime.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 4.3 KiB |
@@ -1,404 +1,53 @@
|
|||||||
NCAAF
|
NCAAF
|
||||||
AAMU => Alabama A&M Bulldogs
|
AMH => Amherst Mammoths
|
||||||
ACU => Abilene Christian Wildcats
|
|
||||||
ADA => Adams State Grizzlies
|
|
||||||
ADR => Adrian Bulldogs
|
|
||||||
AFA => Air Force Falcons
|
|
||||||
AIC => American International Yellow Jackets
|
|
||||||
AKR => Akron Zips
|
|
||||||
ALA => Alabama Crimson Tide
|
|
||||||
ALB => Albright Lions
|
|
||||||
ALBS => Albany State (GA) Golden Rams
|
|
||||||
ALCN => Alcorn State Braves
|
|
||||||
ALD => Alderson Broaddus Battlers
|
|
||||||
ALF => Alfred Saxons
|
|
||||||
ALL => Allegheny Gators
|
|
||||||
ALST => Alabama State Hornets
|
|
||||||
AMH => Amherst College Mammoths
|
|
||||||
AND => Anderson (IN) Ravens
|
|
||||||
ANG => Angelo State Rams
|
|
||||||
ANN => Anna Maria College Amcats
|
ANN => Anna Maria College Amcats
|
||||||
APP => Appalachian State Mountaineers
|
|
||||||
APSU => Austin Peay Governors
|
|
||||||
ARIZ => Arizona Wildcats
|
ARIZ => Arizona Wildcats
|
||||||
ARK => Arkansas-Monticello Boll Weevils
|
ARK => Arkansas Razorbacks
|
||||||
ARMY => Army Black Knights
|
|
||||||
ARST => Arkansas State Red Wolves
|
|
||||||
ASH => Ashland Eagles
|
|
||||||
ASP => Assumption Greyhounds
|
|
||||||
ASU => Arizona State Sun Devils
|
ASU => Arizona State Sun Devils
|
||||||
AUB => Auburn Tigers
|
AUB => Auburn Tigers
|
||||||
AUG => St. Augustine's Falcons
|
BOIS => Boise State Broncos
|
||||||
AUR => Aurora Spartans
|
BRST => Bridgewater State Bears
|
||||||
AUS => Austin College 'Roos
|
BUENA => Buena Vista Beavers
|
||||||
AVE => Averett Cougars
|
CAL => California Golden Bears
|
||||||
AVI => Avila College Eagles
|
CAR => Carroll University (WI) Pioneers
|
||||||
AZU => Azusa Pacific Cougars
|
CLA => Claremont-Mudd-Scripps College Stags
|
||||||
BAK => Baker University Wildcats
|
COLBY => Colby College White Mules
|
||||||
BAL => Baldwin Wallace Yellow Jackets
|
|
||||||
BALL => Ball State Cardinals
|
|
||||||
BAT => Bates College Bobcats
|
|
||||||
BAY => Baylor Bears
|
|
||||||
BC => Boston College Eagles
|
|
||||||
BEC => Becker College Hawks
|
|
||||||
BEL => Beloit College Buccaneers
|
|
||||||
BEN => Benedictine University (IL) Eagles
|
|
||||||
BENT => Bentley Falcons
|
|
||||||
BET => Bethel (TN) Wildcats
|
|
||||||
BGSU => Bowling Green Falcons
|
|
||||||
BHS => Black Hills State Yellow Jackets
|
|
||||||
BIR => Birmingham-Southern Panthers
|
|
||||||
BKN => Bacone College Warriors
|
|
||||||
BLA => Blackburn Beavers
|
|
||||||
BLOM => Bloomsburg Huskies
|
|
||||||
BLU => Bluffton Beavers
|
|
||||||
BOW => Bowdoin Polar Bears
|
|
||||||
BRI => British Columbia Thunderbirds
|
|
||||||
BRWN => Brown Bears
|
|
||||||
BST => Bemidji State Beavers
|
|
||||||
BSU => Bowie State Bulldogs
|
|
||||||
BUCK => Bucknell Bison
|
|
||||||
BUE => Buena Vista Beavers
|
|
||||||
BUF => Buffalo State Bengals
|
|
||||||
BUFF => Buffalo Bulls
|
|
||||||
BUT => Butler Bulldogs
|
|
||||||
BYU => BYU Cougars
|
|
||||||
CAL => California Lutheran Kingsmen
|
|
||||||
CAM => Campbell Fighting Camels
|
|
||||||
CAP => Capital University Crusaders
|
|
||||||
CAR => Carthage College Red Men
|
|
||||||
CARK => Central Arkansas Bears
|
|
||||||
CAS => Castleton Spartans
|
|
||||||
CAT => Catholic University Cardinals
|
|
||||||
CCSU => Central Connecticut Blue Devils
|
|
||||||
CCU => Coastal Carolina Chanticleers
|
|
||||||
CEN => Centre College Colonels
|
|
||||||
CHA => Chapman University Panthers
|
|
||||||
CHI => Chicago Maroons
|
|
||||||
CHSO => Charleston Southern Buccaneers
|
|
||||||
CIN => Cincinnati Bearcats
|
|
||||||
CLA => Clarion Golden Eagles
|
|
||||||
CLEM => Clemson Tigers
|
|
||||||
CLMB => Columbia Lions
|
|
||||||
CLT => Charlotte 49ers
|
|
||||||
CMU => Central Michigan Chippewas
|
|
||||||
COE => Coe College Kohawks
|
|
||||||
COL => Colorado School of Mines Orediggers
|
|
||||||
COLC => Colorado College Tigers
|
|
||||||
COLG => Colgate Raiders
|
|
||||||
COLO => Colorado Buffaloes
|
COLO => Colorado Buffaloes
|
||||||
CON => Concordia-Minnesota Cobbers
|
CONN => UConn Huskies
|
||||||
COR => Cornell College (IA) Rams
|
|
||||||
CP => Cal Poly Mustangs
|
CP => Cal Poly Mustangs
|
||||||
CRO => Crown Storm
|
|
||||||
CSU => Colorado State Rams
|
CSU => Colorado State Rams
|
||||||
CUL => Culver-Stockton Wildcats
|
|
||||||
CUM => Cumberland College Indians
|
|
||||||
CUR => Curry College Colonels
|
CUR => Curry College Colonels
|
||||||
DAK => Dakota Wesleyan Tigers
|
DEL => Delaware Blue Hens
|
||||||
DART => Dartmouth Big Green
|
|
||||||
DAV => Davidson Wildcats
|
|
||||||
DAY => Dayton Flyers
|
|
||||||
DEF => Defiance Yellow Jackets
|
|
||||||
DEL => Delta State Statesmen
|
|
||||||
DEN => Denison Big Red
|
|
||||||
DEP => DePauw Tigers
|
|
||||||
DIC => Dickinson State Blue Hawks
|
|
||||||
DRKE => Drake Bulldogs
|
|
||||||
DSU => Delaware State Hornets
|
|
||||||
DUB => Dubuque Spartans
|
DUB => Dubuque Spartans
|
||||||
DUKE => Duke Blue Devils
|
ELM => Elmhurst Bluejays
|
||||||
DUQ => Duquesne Dukes
|
FAMU => Florida A&M Rattlers
|
||||||
EAS => Eastern New Mexico Greyhounds
|
|
||||||
ECU => East Carolina Pirates
|
|
||||||
EDI => Edinboro Fighting Scots
|
|
||||||
EIU => Eastern Illinois Panthers
|
|
||||||
EKU => Eastern Kentucky Colonels
|
|
||||||
ELI => Elizabeth City State Vikings
|
|
||||||
ELM => Elmhurst Blue Jays
|
|
||||||
ELON => Elon Phoenix
|
|
||||||
EMO => Emory & Henry Wasps
|
|
||||||
EMP => Emporia State Hornets
|
|
||||||
EMU => Eastern Michigan Eagles
|
|
||||||
END => Endicott College Gulls
|
|
||||||
EOR => Eastern Oregon Mountaineers
|
|
||||||
ETSU => East Tennessee State Buccaneers
|
|
||||||
EUR => Eureka College Red Devils
|
|
||||||
EWU => Eastern Washington Eagles
|
|
||||||
FAU => Florida Atlantic Owls
|
|
||||||
FAY => Fayetteville State Broncos
|
|
||||||
FDU => FDU-Florham Devils
|
|
||||||
FER => Ferrum Panthers
|
|
||||||
FIN => Findlay Oilers
|
|
||||||
FIT => Fitchburg State Falcons
|
|
||||||
FIU => Florida International Panthers
|
|
||||||
FLA => Florida Gators
|
FLA => Florida Gators
|
||||||
FOR => Fort Valley State Wildcats
|
|
||||||
FRA => Franklin Grizzlies
|
|
||||||
FRES => Fresno State Bulldogs
|
|
||||||
FRO => Frostburg State Bobcats
|
|
||||||
FRST => Ferris State Bulldogs
|
|
||||||
FSU => Florida State Seminoles
|
FSU => Florida State Seminoles
|
||||||
FTLW => Fort Lewis Skyhawks
|
|
||||||
FUR => Furman Paladins
|
|
||||||
GAL => Gallaudet Bison
|
|
||||||
GAN => Gannon Golden Knights
|
|
||||||
GASO => Georgia Southern Eagles
|
|
||||||
GAST => Georgia State Panthers
|
|
||||||
GEN => Geneva College Golden Tornadoes
|
|
||||||
GEO => George Fox University Bruins
|
|
||||||
GET => Gettysburg Bullets
|
|
||||||
GLE => Glenville State Pioneers
|
|
||||||
GMU => George Mason Patriots
|
|
||||||
GRA => Grand Valley State Lakers
|
|
||||||
GRE => Greenville Panthers
|
|
||||||
GRI => Grinnell Pioneers
|
GRI => Grinnell Pioneers
|
||||||
GRO => Grove City College Wolverines
|
|
||||||
GT => Georgia Tech Yellow Jackets
|
GT => Georgia Tech Yellow Jackets
|
||||||
GUI => Guilford Quakers
|
GTWN => Georgetown Hoyas
|
||||||
GWEB => Gardner-Webb Bulldogs
|
|
||||||
HAM => Hampden-Sydney Tigers
|
|
||||||
HAMP => Hampton Pirates
|
|
||||||
HAN => Hanover Panthers
|
|
||||||
HAR => Hartwick Hawks
|
|
||||||
HARV => Harvard Crimson
|
|
||||||
HAS => Haskell Indian Nations Jayhawks
|
|
||||||
HAW => Hawai'i Rainbow Warriors
|
HAW => Hawai'i Rainbow Warriors
|
||||||
HBU => Houston Baptist Huskies
|
|
||||||
HC => Holy Cross Crusaders
|
|
||||||
HEI => Heidelberg Student Princes
|
|
||||||
HEN => Hendrix College Warriors
|
|
||||||
HIL => Hillsdale Chargers
|
|
||||||
HIR => Hiram College Terriers
|
|
||||||
HOB => Hobart Statesmen
|
|
||||||
HOU => Houston Cougars
|
|
||||||
HOW => Howard Bison
|
HOW => Howard Bison
|
||||||
HUS => Husson Eagles
|
|
||||||
IDHO => Idaho Vandals
|
IDHO => Idaho Vandals
|
||||||
IDST => Idaho State Bengals
|
|
||||||
ILL => Illinois Fighting Illini
|
|
||||||
ILST => Illinois State Redbirds
|
|
||||||
ILW => Illinois Wesleyan Titans
|
|
||||||
IND => Indianapolis
|
|
||||||
INST => Indiana State Sycamores
|
|
||||||
IOW => Iowa Wesleyan Tigers
|
|
||||||
IOWA => Iowa Hawkeyes
|
|
||||||
ISU => Iowa State Cyclones
|
ISU => Iowa State Cyclones
|
||||||
ITH => Ithaca Bombers
|
JXST => Jacksonville State Gamecocks
|
||||||
IU => Indiana Hoosiers
|
|
||||||
JKST => Jackson State Tigers
|
|
||||||
JMU => James Madison Dukes
|
|
||||||
JOH => Johnson C Smith Golden Bulls
|
|
||||||
JUN => Juniata Eagles
|
|
||||||
JVST => Jacksonville State Gamecocks
|
|
||||||
KAL => Kalamazoo Hornets
|
|
||||||
KAN => Kansas Wesleyan University Coyotes
|
|
||||||
KEN => Kenyon Lords
|
|
||||||
KENN => Kennesaw State Owls
|
|
||||||
KENT => Kent State Golden Flashes
|
|
||||||
KIN => King's College (PA) Monarchs
|
|
||||||
KNO => Knox College Prairie Fire
|
|
||||||
KSU => Kansas State Wildcats
|
|
||||||
KU => Kansas Jayhawks
|
|
||||||
KUT => Kutztown Golden Bears
|
|
||||||
KYST => Kentucky State Thorobreds
|
|
||||||
KYW => Kentucky Wesleyan Panthers
|
|
||||||
LA => La Verne Leopards
|
|
||||||
LAC => Lane Dragons
|
|
||||||
LAF => Lafayette Leopards
|
|
||||||
LAG => LaGrange College Panthers
|
|
||||||
LAK => Lake Forest Foresters
|
|
||||||
LAM => Lambuth Eagles
|
|
||||||
LAN => Langston Lions
|
|
||||||
LAW => Lawrence Vikings
|
|
||||||
LEB => Lebanon Valley Flying Dutchmen
|
|
||||||
LEH => Lehigh Mountain Hawks
|
|
||||||
LEN => Lenoir-Rhyne Bears
|
|
||||||
LEW => Lewis & Clark Pioneers
|
|
||||||
LIB => Liberty Flames
|
|
||||||
LIM => Limestone Saints
|
|
||||||
LIN => Linfield Wildcats
|
|
||||||
LOC => Lock Haven Bald Eagles
|
|
||||||
LOR => Loras College Duhawks
|
|
||||||
LOU => Louisville Cardinals
|
|
||||||
LSU => LSU Tigers
|
|
||||||
LT => Louisiana Tech Bulldogs
|
|
||||||
LUT => Luther Norse
|
LUT => Luther Norse
|
||||||
LYC => Lycoming Warriors
|
MESA => Colorado Mesa Mavericks
|
||||||
M-OH => Miami (OH) RedHawks
|
MIL => Millikin Big Blue
|
||||||
MAC => Macalester Scots
|
MOR => Morehouse College Maroon Tigers
|
||||||
MAI => Maine Maritime Mariners
|
NOR => North Park Vikings
|
||||||
MAN => Mansfield Mountaineers
|
|
||||||
MAR => Maryville College Fighting Scots
|
|
||||||
MAS => Mass Maritime Buccaneers
|
|
||||||
MASS => UMass Minutemen
|
|
||||||
MAY => Mayville State Comets
|
|
||||||
MCM => McMurry War Hawks
|
|
||||||
MCN => McNeese Cowboys
|
|
||||||
MD => Maryland Terrapins
|
|
||||||
MEM => Memphis Tigers
|
|
||||||
MEN => Menlo College Oaks
|
|
||||||
MER => Merchant Marine Mariners
|
|
||||||
MERC => Mercyhurst Lakers
|
|
||||||
MES => Colorado Mesa Mavericks
|
|
||||||
MET => Methodist Monarchs
|
|
||||||
MH => Mars Hill Mountain Lions
|
|
||||||
MIAMI => Miami Hurricanes
|
|
||||||
MICH => Michigan Wolverines
|
|
||||||
MID => Midwestern State Mustangs
|
|
||||||
MIL => Millsaps Majors
|
|
||||||
MIN => Minot State Beavers
|
|
||||||
MINN => Minnesota Golden Gophers
|
|
||||||
MIS => Missouri Western Griffons
|
|
||||||
MISS => Ole Miss Rebels
|
|
||||||
MIZ => Missouri Tigers
|
|
||||||
MNST => Minnesota State Mavericks
|
|
||||||
MONM => Monmouth Hawks
|
|
||||||
MONT => Montana Grizzlies
|
|
||||||
MOR => Morningside Chiefs
|
|
||||||
MORE => Morehead State Eagles
|
|
||||||
MORG => Morgan State Bears
|
|
||||||
MOU => Mount Union Raiders
|
|
||||||
MRSH => Marshall Thundering Herd
|
|
||||||
MRST => Marist Red Foxes
|
|
||||||
MSST => Mississippi State Bulldogs
|
|
||||||
MSU => Michigan State Spartans
|
|
||||||
MTST => Montana State Bobcats
|
|
||||||
MTSU => Middle Tennessee Blue Raiders
|
|
||||||
MTU => Michigan Tech Huskies
|
|
||||||
MUH => Muhlenberg Mules
|
|
||||||
MUR => Murray State Racers
|
|
||||||
MUS => Muskingum Fighting Muskies
|
|
||||||
MVSU => Mississippi Valley State Delta Devils
|
|
||||||
NAU => Northern Arizona Lumberjacks
|
|
||||||
NAVY => Navy Midshipmen
|
|
||||||
NBY => Newberry Wolves
|
|
||||||
NCAT => North Carolina A&T Aggies
|
|
||||||
NCCU => North Carolina Central Eagles
|
|
||||||
NCST => NC State Wolfpack
|
|
||||||
ND => Notre Dame Fighting Irish
|
|
||||||
NDOH => Notre Dame College Falcons
|
|
||||||
NDSU => North Dakota State Bison
|
|
||||||
NEB => Nebraska-Kearney Lopers
|
|
||||||
NEV => Nevada Wolf Pack
|
|
||||||
NH => New Haven Chargers
|
|
||||||
NICH => Nicholls Colonels
|
|
||||||
NIU => Northern Illinois Huskies
|
|
||||||
NMH => New Mexico Highlands Cowboys
|
|
||||||
NMI => Northern Michigan Wildcats
|
|
||||||
NMSU => New Mexico State Aggies
|
|
||||||
NOR => Univ. of Northwestern-St. Paul Eagles
|
|
||||||
NORF => Norfolk State Spartans
|
|
||||||
NW => Northwestern Wildcats
|
|
||||||
OBE => Oberlin Yeomen
|
|
||||||
ODU => Old Dominion Monarchs
|
|
||||||
OHI => Ohio Northern Polar Bears
|
|
||||||
OHIO => Ohio Bobcats
|
|
||||||
OKL => Oklahoma Baptist Bison
|
|
||||||
OKST => Oklahoma State Cowboys
|
|
||||||
OLI => Olivet College Comets
|
|
||||||
OMA => Omaha Mavericks
|
|
||||||
ORST => Oregon State Beavers
|
|
||||||
OSU => Ohio State Buckeyes
|
|
||||||
OTT => Otterbein Cardinals
|
|
||||||
OU => Oklahoma Sooners
|
|
||||||
PAC => Pacific (OR) Boxers
|
|
||||||
PENN => Pennsylvania Quakers
|
|
||||||
PIKE => Pikeville Bears
|
|
||||||
PITT => Pittsburgh Panthers
|
|
||||||
PRE => Presentation College Saints
|
|
||||||
PRI => Principia College Panthers
|
|
||||||
PRIN => Princeton Tigers
|
|
||||||
PST => Pittsburg State Gorillas
|
|
||||||
PSU => Penn State Nittany Lions
|
|
||||||
RED => Redlands Bulldogs
|
RED => Redlands Bulldogs
|
||||||
RICE => Rice Owls
|
|
||||||
RICH => Richmond Spiders
|
|
||||||
RIT => Rochester Yellow Jackets
|
|
||||||
ROB => Robert Morris (IL) Eagles
|
|
||||||
ROS => Rose-Hulman Engineers
|
|
||||||
RUTG => Rutgers Scarlet Knights
|
|
||||||
SAC => Sacramento State Hornets
|
SAC => Sacramento State Hornets
|
||||||
SAG => Saginaw Valley Cardinals
|
|
||||||
SDAK => South Dakota Coyotes
|
|
||||||
SDSU => San Diego State Aztecs
|
SDSU => San Diego State Aztecs
|
||||||
SET => Seton Hill Griffins
|
|
||||||
SIU => Southern Illinois Salukis
|
|
||||||
SJSU => San José State Spartans
|
SJSU => San José State Spartans
|
||||||
SLI => Slippery Rock The Rock
|
|
||||||
SOU => Southwestern College Moundbuilders
|
|
||||||
SPR => Springfield College Pride
|
|
||||||
ST => St. Scholastica Saints
|
|
||||||
STAN => Stanford Cardinal
|
STAN => Stanford Cardinal
|
||||||
STE => Stevenson University Mustangs
|
|
||||||
STET => Stetson Hatters
|
STET => Stetson Hatters
|
||||||
STO => Stonehill College Skyhawks
|
|
||||||
SUS => Susquehanna University River Hawks
|
|
||||||
SUU => Southern Utah Thunderbirds
|
|
||||||
SYR => Syracuse Orange
|
|
||||||
TA&M => Texas A&M Aggies
|
|
||||||
TAY => Taylor Trojans
|
|
||||||
TEM => Temple Owls
|
|
||||||
TEX => Texas Longhorns
|
|
||||||
TIF => Tiffin University Dragons
|
|
||||||
TLSA => Tulsa Golden Hurricane
|
|
||||||
TRI => Trinity University (TX) Tigers
|
|
||||||
TUF => Tufts University Jumbos
|
|
||||||
TXST => Texas State Bobcats
|
|
||||||
UAB => UAB Blazers
|
UAB => UAB Blazers
|
||||||
UAPB => Arkansas-Pine Bluff Golden Lions
|
|
||||||
UCD => UC Davis Aggies
|
|
||||||
UCF => UCF Knights
|
|
||||||
UCLA => UCLA Bruins
|
UCLA => UCLA Bruins
|
||||||
UCONN => UConn Huskies
|
|
||||||
UGA => Georgia Bulldogs
|
UGA => Georgia Bulldogs
|
||||||
UK => Kentucky Wildcats
|
|
||||||
UL => Louisiana Ragin' Cajuns
|
|
||||||
ULM => UL Monroe Warhawks
|
|
||||||
UMD => Minnesota-Duluth Bulldogs
|
|
||||||
UMDA => UMASS Dartmouth Corsairs
|
|
||||||
UML => UMass Lowell River Hawks
|
|
||||||
UNA => North Alabama Lions
|
|
||||||
UNC => North Carolina Tar Heels
|
|
||||||
UNCO => Northern Colorado Bears
|
|
||||||
UND => North Dakota Fighting Hawks
|
|
||||||
UNH => New Hampshire Wildcats
|
|
||||||
UNI => University of Mary Marauders
|
|
||||||
UNLV => UNLV Rebels
|
|
||||||
UNM => New Mexico Lobos
|
|
||||||
UNNY => Union Dutchmen
|
|
||||||
UNT => North Texas Mean Green
|
|
||||||
UPP => Upper Iowa Peacocks
|
|
||||||
URI => Rhode Island Rams
|
|
||||||
USA => South Alabama Jaguars
|
USA => South Alabama Jaguars
|
||||||
USC => USC Trojans
|
USC => USC Trojans
|
||||||
USD => San Diego Toreros
|
|
||||||
USF => South Florida Bulls
|
USF => South Florida Bulls
|
||||||
USU => Utah State Aggies
|
|
||||||
UTAH => Utah Utes
|
|
||||||
UTC => Chattanooga Mocs
|
|
||||||
UTI => Utica College Pioneers
|
|
||||||
UVA => Virginia Cavaliers
|
|
||||||
VAL => Valley City State Vikings
|
|
||||||
VAN => Vanderbilt Commodores
|
|
||||||
VILL => Villanova Wildcats
|
|
||||||
VIR => Virginia State Trojans
|
|
||||||
VT => Virginia Tech Hokies
|
|
||||||
WAB => Wabash College Little Giants
|
|
||||||
WAKE => Wake Forest Demon Deacons
|
|
||||||
WAS => Washington-Missouri Bears
|
|
||||||
WASH => Washington Huskies
|
|
||||||
WAY => Wayne State (MI) Warriors
|
|
||||||
WES => Westminster College (MO) Blue Jays
|
|
||||||
WHE => Wheaton College Illinois Thunder
|
|
||||||
WIL => Wilkes University Colonels
|
|
||||||
WIN => Wingate Bulldogs
|
|
||||||
WIS => Wisconsin-Platteville Pioneers
|
|
||||||
WISC => Wisconsin Badgers
|
|
||||||
WKU => Western Kentucky Hilltoppers
|
|
||||||
WOR => Worcester State College Lancers
|
|
||||||
WSU => Washington State Cougars
|
|
||||||
WVU => West Virginia Mountaineers
|
|
||||||
YALE => Yale Bulldogs
|
YALE => Yale Bulldogs
|
||||||
|
|
||||||
NBA
|
NBA
|
||||||
@@ -1106,6 +755,149 @@ MLB Conferences/Divisions
|
|||||||
OAK => Oakland Athletics
|
OAK => Oakland Athletics
|
||||||
SEA => Seattle Mariners
|
SEA => Seattle Mariners
|
||||||
TEX => Texas Rangers
|
TEX => Texas Rangers
|
||||||
|
Soccer - Premier League (England)
|
||||||
|
ARS => Arsenal
|
||||||
|
AVL => Aston Villa
|
||||||
|
BHA => Brighton & Hove Albion
|
||||||
|
BOU => AFC Bournemouth
|
||||||
|
BRE => Brentford
|
||||||
|
BUR => Burnley
|
||||||
|
CHE => Chelsea
|
||||||
|
CRY => Crystal Palace
|
||||||
|
EVE => Everton
|
||||||
|
FUL => Fulham
|
||||||
|
LIV => Liverpool
|
||||||
|
LUT => Luton Town
|
||||||
|
MCI => Manchester City
|
||||||
|
MUN => Manchester United
|
||||||
|
NEW => Newcastle United
|
||||||
|
NFO => Nottingham Forest
|
||||||
|
SHU => Sheffield United
|
||||||
|
TOT => Tottenham Hotspur
|
||||||
|
WHU => West Ham United
|
||||||
|
WOL => Wolverhampton Wanderers
|
||||||
|
|
||||||
|
Soccer - La Liga (Spain)
|
||||||
|
ALA => Alavés
|
||||||
|
ATH => Athletic Bilbao
|
||||||
|
ATM => Atlético Madrid
|
||||||
|
BAR => Barcelona
|
||||||
|
BET => Real Betis
|
||||||
|
CAG => Cagliari
|
||||||
|
CEL => Celta Vigo
|
||||||
|
ESP => Espanyol
|
||||||
|
GET => Getafe
|
||||||
|
GIR => Girona
|
||||||
|
LAZ => Lazio
|
||||||
|
LEG => Leganés
|
||||||
|
RAY => Rayo Vallecano
|
||||||
|
RMA => Real Madrid
|
||||||
|
SEV => Sevilla
|
||||||
|
VAL => Valencia
|
||||||
|
VLD => Valladolid
|
||||||
|
|
||||||
|
Soccer - Bundesliga (Germany)
|
||||||
|
BOC => VfL Bochum
|
||||||
|
BOL => VfL Bochum
|
||||||
|
DOR => Borussia Dortmund
|
||||||
|
FCA => FC Augsburg
|
||||||
|
FCB => Bayern Munich
|
||||||
|
FCU => FC Union Berlin
|
||||||
|
HAC => Hannover 96
|
||||||
|
HDH => Hertha BSC
|
||||||
|
KOL => 1. FC Köln
|
||||||
|
LEV => Bayer Leverkusen
|
||||||
|
M05 => Mainz 05
|
||||||
|
RBL => RB Leipzig
|
||||||
|
SCF => SC Freiburg
|
||||||
|
SGE => Eintracht Frankfurt
|
||||||
|
STU => VfB Stuttgart
|
||||||
|
SVW => Werder Bremen
|
||||||
|
TSG => TSG Hoffenheim
|
||||||
|
WOB => VfL Wolfsburg
|
||||||
|
|
||||||
|
Soccer - Serie A (Italy)
|
||||||
|
ATA => Atalanta
|
||||||
|
CAG => Cagliari
|
||||||
|
EMP => Empoli
|
||||||
|
FIO => Fiorentina
|
||||||
|
INT => Inter Milan
|
||||||
|
JUV => Juventus
|
||||||
|
LAZ => Lazio
|
||||||
|
MIL => AC Milan
|
||||||
|
MON => Monza
|
||||||
|
NAP => Napoli
|
||||||
|
ROM => Roma
|
||||||
|
TOR => Torino
|
||||||
|
UDI => Udinese
|
||||||
|
VER => Hellas Verona
|
||||||
|
|
||||||
|
Soccer - Ligue 1 (France)
|
||||||
|
LIL => Lille
|
||||||
|
LPM => Lille
|
||||||
|
LYON => Lyon
|
||||||
|
MAR => Marseille
|
||||||
|
MON => Monaco
|
||||||
|
NAN => Nantes
|
||||||
|
NICE => Nice
|
||||||
|
OL => Olympique Lyonnais
|
||||||
|
OM => Olympique de Marseille
|
||||||
|
PAR => Paris Saint-Germain
|
||||||
|
PSG => Paris Saint-Germain
|
||||||
|
REN => Rennes
|
||||||
|
STR => Strasbourg
|
||||||
|
|
||||||
|
Soccer - Champions League
|
||||||
|
AJA => Ajax
|
||||||
|
ASM => AS Monaco
|
||||||
|
ASS => AS Saint-Étienne
|
||||||
|
BOC => VfL Bochum
|
||||||
|
CEL => Celtic
|
||||||
|
COM => Club Brugge
|
||||||
|
FCA => FC Augsburg
|
||||||
|
FCB => Bayern Munich
|
||||||
|
FCU => FC Union Berlin
|
||||||
|
FIO => Fiorentina
|
||||||
|
GEN => Genoa
|
||||||
|
HAC => Hannover 96
|
||||||
|
IPS => Ipswich Town
|
||||||
|
KSV => Kaiserslautern
|
||||||
|
LEC => Lecce
|
||||||
|
LIL => Lille
|
||||||
|
LIV => Liverpool
|
||||||
|
M05 => Mainz 05
|
||||||
|
MCI => Manchester City
|
||||||
|
MUN => Manchester United
|
||||||
|
NAN => Nantes
|
||||||
|
OSA => Osasuna
|
||||||
|
RBL => RB Leipzig
|
||||||
|
RCL => RC Lens
|
||||||
|
RMA => Real Madrid
|
||||||
|
SCF => SC Freiburg
|
||||||
|
SGE => Eintracht Frankfurt
|
||||||
|
SR => Sporting CP
|
||||||
|
STP => St. Pauli
|
||||||
|
SVW => Werder Bremen
|
||||||
|
TFC => Toulouse FC
|
||||||
|
TOT => Tottenham Hotspur
|
||||||
|
TSG => TSG Hoffenheim
|
||||||
|
UDI => Udinese
|
||||||
|
VEN => Venezia
|
||||||
|
VFB => VfB Stuttgart
|
||||||
|
VIL => Villarreal
|
||||||
|
|
||||||
|
Soccer - Other Teams
|
||||||
|
austin => Austin FC
|
||||||
|
cf_montral => CF Montréal
|
||||||
|
charlotte => Charlotte FC
|
||||||
|
dortmund => Borussia Dortmund
|
||||||
|
gladbach => Borussia Mönchengladbach
|
||||||
|
lafc => Los Angeles FC
|
||||||
|
leverkusen => Bayer Leverkusen
|
||||||
|
nycfc => New York City FC
|
||||||
|
paris_sg => Paris Saint-Germain
|
||||||
|
st_louis => St. Louis City SC
|
||||||
|
|
||||||
MLS Conferences/Divisions
|
MLS Conferences/Divisions
|
||||||
Conferences currently unsupported
|
Conferences currently unsupported
|
||||||
|
|
||||||
|
|||||||
BIN
assets/sports/mlb_logos/mlb.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 20 KiB |
BIN
assets/sports/nba_logos/nba.png
Normal file
|
After Width: | Height: | Size: 490 B |
BIN
assets/sports/ncaa_fbs_logos/AANDM.png
Normal file
|
After Width: | Height: | Size: 409 B |
BIN
assets/sports/ncaa_fbs_logos/AMH.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/sports/ncaa_fbs_logos/ANN.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 44 KiB |
BIN
assets/sports/ncaa_fbs_logos/ASU.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
BIN
assets/sports/ncaa_fbs_logos/BOIS.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
assets/sports/ncaa_fbs_logos/BRST.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/sports/ncaa_fbs_logos/BUENA.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
BIN
assets/sports/ncaa_fbs_logos/CAR.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/sports/ncaa_fbs_logos/CLA.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
assets/sports/ncaa_fbs_logos/COLBY.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 53 KiB |
BIN
assets/sports/ncaa_fbs_logos/CP.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
assets/sports/ncaa_fbs_logos/CSU.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
assets/sports/ncaa_fbs_logos/CUR.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/sports/ncaa_fbs_logos/DEL.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
assets/sports/ncaa_fbs_logos/DUB.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/sports/ncaa_fbs_logos/ELM.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
assets/sports/ncaa_fbs_logos/FAMU.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/sports/ncaa_fbs_logos/FLA.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 56 KiB |
BIN
assets/sports/ncaa_fbs_logos/GRI.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 23 KiB |
BIN
assets/sports/ncaa_fbs_logos/GTWN.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/sports/ncaa_fbs_logos/HAW.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/sports/ncaa_fbs_logos/HOW.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
assets/sports/ncaa_fbs_logos/IDHO.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 61 KiB |
BIN
assets/sports/ncaa_fbs_logos/JXST.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/sports/ncaa_fbs_logos/LUT.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 24 KiB |
BIN
assets/sports/ncaa_fbs_logos/MESA.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
BIN
assets/sports/ncaa_fbs_logos/MIL.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/sports/ncaa_fbs_logos/MOR.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/sports/ncaa_fbs_logos/NOR.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
assets/sports/ncaa_fbs_logos/RED.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
assets/sports/ncaa_fbs_logos/SAC.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 34 KiB |
BIN
assets/sports/ncaa_fbs_logos/STET.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
assets/sports/ncaa_fbs_logos/TAANDM.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/sports/ncaa_fbs_logos/TAMU.png
Normal file
|
After Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 31 KiB |
BIN
assets/sports/ncaa_fbs_logos/UNT.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/sports/ncaa_fbs_logos/USA.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
BIN
assets/sports/ncaa_fbs_logos/YALE.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/sports/ncaa_fbs_logos/ncaa_fb.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/sports/ncaa_fbs_logos/ncaam.png
Normal file
|
After Width: | Height: | Size: 651 B |
BIN
assets/sports/nfl_logos/nfl.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/sports/nhl_logos/nhl.png
Normal file
|
After Width: | Height: | Size: 430 B |
1
check_team_images.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -5,10 +5,10 @@
|
|||||||
"start_time": "07:00",
|
"start_time": "07:00",
|
||||||
"end_time": "23:00"
|
"end_time": "23:00"
|
||||||
},
|
},
|
||||||
"timezone": "America/Chicago",
|
"timezone": "America/New_York",
|
||||||
"location": {
|
"location": {
|
||||||
"city": "Dallas",
|
"city": "Tampa",
|
||||||
"state": "Texas",
|
"state": "Florida",
|
||||||
"country": "US"
|
"country": "US"
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
"daily_forecast": 30,
|
"daily_forecast": 30,
|
||||||
"stock_news": 20,
|
"stock_news": 20,
|
||||||
"odds_ticker": 60,
|
"odds_ticker": 60,
|
||||||
|
"leaderboard": 60,
|
||||||
"nhl_live": 30,
|
"nhl_live": 30,
|
||||||
"nhl_recent": 30,
|
"nhl_recent": 30,
|
||||||
"nhl_upcoming": 30,
|
"nhl_upcoming": 30,
|
||||||
@@ -81,7 +82,7 @@
|
|||||||
"update_interval": 1
|
"update_interval": 1
|
||||||
},
|
},
|
||||||
"weather": {
|
"weather": {
|
||||||
"enabled": false,
|
"enabled": true,
|
||||||
"update_interval": 1800,
|
"update_interval": 1800,
|
||||||
"units": "imperial",
|
"units": "imperial",
|
||||||
"display_format": "{temp}\u00b0F\n{condition}"
|
"display_format": "{temp}\u00b0F\n{condition}"
|
||||||
@@ -129,11 +130,12 @@
|
|||||||
"duration_buffer": 0.1
|
"duration_buffer": 0.1
|
||||||
},
|
},
|
||||||
"odds_ticker": {
|
"odds_ticker": {
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"show_favorite_teams_only": true,
|
"show_favorite_teams_only": true,
|
||||||
"games_per_favorite_team": 1,
|
"games_per_favorite_team": 1,
|
||||||
"max_games_per_league": 5,
|
"max_games_per_league": 5,
|
||||||
"show_odds_only": false,
|
"show_odds_only": false,
|
||||||
|
"fetch_odds": true,
|
||||||
"sort_order": "soonest",
|
"sort_order": "soonest",
|
||||||
"enabled_leagues": [
|
"enabled_leagues": [
|
||||||
"nfl",
|
"nfl",
|
||||||
@@ -150,6 +152,46 @@
|
|||||||
"dynamic_duration": true,
|
"dynamic_duration": true,
|
||||||
"min_duration": 30,
|
"min_duration": 30,
|
||||||
"max_duration": 300,
|
"max_duration": 300,
|
||||||
|
"duration_buffer": 0.05
|
||||||
|
},
|
||||||
|
"leaderboard": {
|
||||||
|
"enabled": false,
|
||||||
|
"enabled_sports": {
|
||||||
|
"nfl": {
|
||||||
|
"enabled": true,
|
||||||
|
"top_teams": 10
|
||||||
|
},
|
||||||
|
"nba": {
|
||||||
|
"enabled": false,
|
||||||
|
"top_teams": 10
|
||||||
|
},
|
||||||
|
"mlb": {
|
||||||
|
"enabled": false,
|
||||||
|
"top_teams": 10
|
||||||
|
},
|
||||||
|
"ncaa_fb": {
|
||||||
|
"enabled": true,
|
||||||
|
"top_teams": 25,
|
||||||
|
"show_ranking": true
|
||||||
|
},
|
||||||
|
"nhl": {
|
||||||
|
"enabled": false,
|
||||||
|
"top_teams": 10
|
||||||
|
},
|
||||||
|
"ncaam_basketball": {
|
||||||
|
"enabled": false,
|
||||||
|
"top_teams": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"update_interval": 3600,
|
||||||
|
"scroll_speed": 1,
|
||||||
|
"scroll_delay": 0.01,
|
||||||
|
"display_duration": 60,
|
||||||
|
"loop": false,
|
||||||
|
"request_timeout": 30,
|
||||||
|
"dynamic_duration": true,
|
||||||
|
"min_duration": 45,
|
||||||
|
"max_duration": 600,
|
||||||
"duration_buffer": 0.1
|
"duration_buffer": 0.1
|
||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
@@ -257,6 +299,7 @@
|
|||||||
],
|
],
|
||||||
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
|
"show_ranking": true,
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"ncaa_fb_live": true,
|
"ncaa_fb_live": true,
|
||||||
"ncaa_fb_recent": true ,
|
"ncaa_fb_recent": true ,
|
||||||
@@ -366,7 +409,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"text_display": {
|
"text_display": {
|
||||||
"enabled": false,
|
"enabled": true,
|
||||||
"text": "Subscribe to ChuckBuilds",
|
"text": "Subscribe to ChuckBuilds",
|
||||||
"font_path": "assets/fonts/press-start-2p.ttf",
|
"font_path": "assets/fonts/press-start-2p.ttf",
|
||||||
"font_size": 8,
|
"font_size": 8,
|
||||||
|
|||||||
1
create_league_logos.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
1
create_ncaa_logos.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
107
debug_of_the_day.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Debug script for OfTheDayManager issues
|
||||||
|
Run this on the Raspberry Pi to diagnose the problem
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
1. Copy this file to your Raspberry Pi
|
||||||
|
2. Run: python3 debug_of_the_day.py
|
||||||
|
3. Check the output for any errors or issues
|
||||||
|
|
||||||
|
This script will help identify why the OfTheDayManager is not loading data files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
def debug_of_the_day():
|
||||||
|
print("=== OfTheDayManager Debug Script ===")
|
||||||
|
print(f"Current working directory: {os.getcwd()}")
|
||||||
|
print(f"Python path: {sys.path}")
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if not os.path.exists('config/config.json'):
|
||||||
|
print("ERROR: config/config.json not found. Make sure you're running from the LEDMatrix root directory.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Load the actual config
|
||||||
|
try:
|
||||||
|
with open('config/config.json', 'r') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
print("✓ Successfully loaded config.json")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"ERROR loading config.json: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check of_the_day configuration
|
||||||
|
of_the_day_config = config.get('of_the_day', {})
|
||||||
|
print(f"OfTheDay enabled: {of_the_day_config.get('enabled', False)}")
|
||||||
|
|
||||||
|
if not of_the_day_config.get('enabled', False):
|
||||||
|
print("OfTheDay is disabled in config!")
|
||||||
|
return
|
||||||
|
|
||||||
|
categories = of_the_day_config.get('categories', {})
|
||||||
|
print(f"Categories configured: {list(categories.keys())}")
|
||||||
|
|
||||||
|
# Test each category
|
||||||
|
today = date.today()
|
||||||
|
day_of_year = today.timetuple().tm_yday
|
||||||
|
print(f"Today is day {day_of_year} of the year")
|
||||||
|
|
||||||
|
for category_name, category_config in categories.items():
|
||||||
|
print(f"\n--- Testing category: {category_name} ---")
|
||||||
|
print(f"Category enabled: {category_config.get('enabled', True)}")
|
||||||
|
|
||||||
|
if not category_config.get('enabled', True):
|
||||||
|
print("Category is disabled, skipping...")
|
||||||
|
continue
|
||||||
|
|
||||||
|
data_file = category_config.get('data_file')
|
||||||
|
print(f"Data file: {data_file}")
|
||||||
|
|
||||||
|
# Test path resolution
|
||||||
|
if not os.path.isabs(data_file):
|
||||||
|
if data_file.startswith('of_the_day/'):
|
||||||
|
file_path = os.path.join(os.getcwd(), data_file)
|
||||||
|
else:
|
||||||
|
file_path = os.path.join(os.getcwd(), 'of_the_day', data_file)
|
||||||
|
else:
|
||||||
|
file_path = data_file
|
||||||
|
|
||||||
|
file_path = os.path.abspath(file_path)
|
||||||
|
print(f"Resolved path: {file_path}")
|
||||||
|
print(f"File exists: {os.path.exists(file_path)}")
|
||||||
|
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
print(f"ERROR: Data file not found at {file_path}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Test JSON loading
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
print(f"✓ Successfully loaded JSON with {len(data)} items")
|
||||||
|
|
||||||
|
# Check for today's entry
|
||||||
|
day_key = str(day_of_year)
|
||||||
|
if day_key in data:
|
||||||
|
item = data[day_key]
|
||||||
|
print(f"✓ Found entry for day {day_of_year}: {item.get('title', 'No title')}")
|
||||||
|
else:
|
||||||
|
print(f"✗ No entry found for day {day_of_year}")
|
||||||
|
# Show some nearby entries
|
||||||
|
nearby_days = [k for k in data.keys() if k.isdigit() and abs(int(k) - day_of_year) <= 5]
|
||||||
|
print(f"Nearby days with entries: {sorted(nearby_days)}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"ERROR loading JSON: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
print("\n=== Debug complete ===")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
debug_of_the_day()
|
||||||
1
list_missing_teams.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
1
list_soccer_abbreviations.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
657
missing_team_logos.txt
Normal file
@@ -0,0 +1,657 @@
|
|||||||
|
================================================================================
|
||||||
|
MISSING TEAM LOGOS - COMPLETE LIST
|
||||||
|
================================================================================
|
||||||
|
Total missing teams: 309
|
||||||
|
|
||||||
|
|
||||||
|
MLB:
|
||||||
|
---
|
||||||
|
OAK => Oakland Athletics
|
||||||
|
|
||||||
|
NCAAF:
|
||||||
|
-----
|
||||||
|
AAMU => Alabama A&M Bulldogs
|
||||||
|
ACU => Abilene Christian Wildcats
|
||||||
|
ADA => Adams State Grizzlies
|
||||||
|
ADR => Adrian Bulldogs
|
||||||
|
AIC => American International Yellow Jackets
|
||||||
|
ALB => Albright Lions
|
||||||
|
ALBS => Albany State (GA) Golden Rams
|
||||||
|
ALCN => Alcorn State Braves
|
||||||
|
ALD => Alderson Broaddus Battlers
|
||||||
|
ALF => Alfred Saxons
|
||||||
|
ALL => Allegheny Gators
|
||||||
|
ALST => Alabama State Hornets
|
||||||
|
AMH => Amherst College Mammoths
|
||||||
|
AND => Anderson (IN) Ravens
|
||||||
|
ANG => Angelo State Rams
|
||||||
|
ANN => Anna Maria College Amcats
|
||||||
|
APSU => Austin Peay Governors
|
||||||
|
ASH => Ashland Eagles
|
||||||
|
ASP => Assumption Greyhounds
|
||||||
|
ASU => Arizona State Sun Devils
|
||||||
|
AUG => St. Augustine's Falcons
|
||||||
|
AUR => Aurora Spartans
|
||||||
|
AUS => Austin College 'Roos
|
||||||
|
AVE => Averett Cougars
|
||||||
|
AVI => Avila College Eagles
|
||||||
|
AZU => Azusa Pacific Cougars
|
||||||
|
BAK => Baker University Wildcats
|
||||||
|
BAL => Baldwin Wallace Yellow Jackets
|
||||||
|
BAT => Bates College Bobcats
|
||||||
|
BEC => Becker College Hawks
|
||||||
|
BEL => Beloit College Buccaneers
|
||||||
|
BEN => Benedictine University (IL) Eagles
|
||||||
|
BENT => Bentley Falcons
|
||||||
|
BET => Bethel (TN) Wildcats
|
||||||
|
BHS => Black Hills State Yellow Jackets
|
||||||
|
BIR => Birmingham-Southern Panthers
|
||||||
|
BKN => Bacone College Warriors
|
||||||
|
BLA => Blackburn Beavers
|
||||||
|
BLOM => Bloomsburg Huskies
|
||||||
|
BLU => Bluffton Beavers
|
||||||
|
BOW => Bowdoin Polar Bears
|
||||||
|
BRI => British Columbia Thunderbirds
|
||||||
|
BRWN => Brown Bears
|
||||||
|
BST => Bemidji State Beavers
|
||||||
|
BUCK => Bucknell Bison
|
||||||
|
BUE => Buena Vista Beavers
|
||||||
|
BUF => Buffalo State Bengals
|
||||||
|
BUT => Butler Bulldogs
|
||||||
|
CAM => Campbell Fighting Camels
|
||||||
|
CAP => Capital University Crusaders
|
||||||
|
CAR => Carthage College Red Men
|
||||||
|
CARK => Central Arkansas Bears
|
||||||
|
CAS => Castleton Spartans
|
||||||
|
CAT => Catholic University Cardinals
|
||||||
|
CCSU => Central Connecticut Blue Devils
|
||||||
|
CEN => Centre College Colonels
|
||||||
|
CHA => Chapman University Panthers
|
||||||
|
CHI => Chicago Maroons
|
||||||
|
CHSO => Charleston Southern Buccaneers
|
||||||
|
CLA => Clarion Golden Eagles
|
||||||
|
CLMB => Columbia Lions
|
||||||
|
COE => Coe College Kohawks
|
||||||
|
COL => Colorado School of Mines Orediggers
|
||||||
|
COLC => Colorado College Tigers
|
||||||
|
COLG => Colgate Raiders
|
||||||
|
CON => Concordia-Minnesota Cobbers
|
||||||
|
COR => Cornell College (IA) Rams
|
||||||
|
CP => Cal Poly Mustangs
|
||||||
|
CRO => Crown Storm
|
||||||
|
CSU => Colorado State Rams
|
||||||
|
CUL => Culver-Stockton Wildcats
|
||||||
|
CUM => Cumberland College Indians
|
||||||
|
CUR => Curry College Colonels
|
||||||
|
DAK => Dakota Wesleyan Tigers
|
||||||
|
DART => Dartmouth Big Green
|
||||||
|
DAV => Davidson Wildcats
|
||||||
|
DAY => Dayton Flyers
|
||||||
|
DEF => Defiance Yellow Jackets
|
||||||
|
DEL => Delta State Statesmen
|
||||||
|
DEN => Denison Big Red
|
||||||
|
DEP => DePauw Tigers
|
||||||
|
DIC => Dickinson State Blue Hawks
|
||||||
|
DRKE => Drake Bulldogs
|
||||||
|
DSU => Delaware State Hornets
|
||||||
|
DUB => Dubuque Spartans
|
||||||
|
DUQ => Duquesne Dukes
|
||||||
|
EAS => Eastern New Mexico Greyhounds
|
||||||
|
EDI => Edinboro Fighting Scots
|
||||||
|
EIU => Eastern Illinois Panthers
|
||||||
|
EKU => Eastern Kentucky Colonels
|
||||||
|
ELI => Elizabeth City State Vikings
|
||||||
|
ELM => Elmhurst Blue Jays
|
||||||
|
ELON => Elon Phoenix
|
||||||
|
EMO => Emory & Henry Wasps
|
||||||
|
EMP => Emporia State Hornets
|
||||||
|
END => Endicott College Gulls
|
||||||
|
EOR => Eastern Oregon Mountaineers
|
||||||
|
ETSU => East Tennessee State Buccaneers
|
||||||
|
EUR => Eureka College Red Devils
|
||||||
|
EWU => Eastern Washington Eagles
|
||||||
|
FAY => Fayetteville State Broncos
|
||||||
|
FDU => FDU-Florham Devils
|
||||||
|
FER => Ferrum Panthers
|
||||||
|
FIN => Findlay Oilers
|
||||||
|
FIT => Fitchburg State Falcons
|
||||||
|
FLA => Florida Gators
|
||||||
|
FOR => Fort Valley State Wildcats
|
||||||
|
FRA => Franklin Grizzlies
|
||||||
|
FRO => Frostburg State Bobcats
|
||||||
|
FRST => Ferris State Bulldogs
|
||||||
|
FTLW => Fort Lewis Skyhawks
|
||||||
|
FUR => Furman Paladins
|
||||||
|
GAL => Gallaudet Bison
|
||||||
|
GAN => Gannon Golden Knights
|
||||||
|
GEN => Geneva College Golden Tornadoes
|
||||||
|
GEO => George Fox University Bruins
|
||||||
|
GET => Gettysburg Bullets
|
||||||
|
GLE => Glenville State Pioneers
|
||||||
|
GMU => George Mason Patriots
|
||||||
|
GRA => Grand Valley State Lakers
|
||||||
|
GRE => Greenville Panthers
|
||||||
|
GRI => Grinnell Pioneers
|
||||||
|
GRO => Grove City College Wolverines
|
||||||
|
GUI => Guilford Quakers
|
||||||
|
GWEB => Gardner-Webb Bulldogs
|
||||||
|
HAM => Hampden-Sydney Tigers
|
||||||
|
HAMP => Hampton Pirates
|
||||||
|
HAN => Hanover Panthers
|
||||||
|
HAR => Hartwick Hawks
|
||||||
|
HARV => Harvard Crimson
|
||||||
|
HAS => Haskell Indian Nations Jayhawks
|
||||||
|
HAW => Hawai'i Rainbow Warriors
|
||||||
|
HBU => Houston Baptist Huskies
|
||||||
|
HC => Holy Cross Crusaders
|
||||||
|
HEI => Heidelberg Student Princes
|
||||||
|
HEN => Hendrix College Warriors
|
||||||
|
HIL => Hillsdale Chargers
|
||||||
|
HIR => Hiram College Terriers
|
||||||
|
HOB => Hobart Statesmen
|
||||||
|
HOW => Howard Bison
|
||||||
|
HUS => Husson Eagles
|
||||||
|
IDHO => Idaho Vandals
|
||||||
|
IDST => Idaho State Bengals
|
||||||
|
ILST => Illinois State Redbirds
|
||||||
|
ILW => Illinois Wesleyan Titans
|
||||||
|
IND => Indianapolis
|
||||||
|
INST => Indiana State Sycamores
|
||||||
|
IOW => Iowa Wesleyan Tigers
|
||||||
|
ITH => Ithaca Bombers
|
||||||
|
JKST => Jackson State Tigers
|
||||||
|
JOH => Johnson C Smith Golden Bulls
|
||||||
|
JUN => Juniata Eagles
|
||||||
|
KAL => Kalamazoo Hornets
|
||||||
|
KAN => Kansas Wesleyan University Coyotes
|
||||||
|
KEN => Kenyon Lords
|
||||||
|
KIN => King's College (PA) Monarchs
|
||||||
|
KNO => Knox College Prairie Fire
|
||||||
|
KUT => Kutztown Golden Bears
|
||||||
|
KYST => Kentucky State Thorobreds
|
||||||
|
KYW => Kentucky Wesleyan Panthers
|
||||||
|
LA => La Verne Leopards
|
||||||
|
LAG => LaGrange College Panthers
|
||||||
|
LAK => Lake Forest Foresters
|
||||||
|
LAM => Lambuth Eagles
|
||||||
|
LAN => Langston Lions
|
||||||
|
LAW => Lawrence Vikings
|
||||||
|
LEB => Lebanon Valley Flying Dutchmen
|
||||||
|
LEH => Lehigh Mountain Hawks
|
||||||
|
LEN => Lenoir-Rhyne Bears
|
||||||
|
LEW => Lewis & Clark Pioneers
|
||||||
|
LIM => Limestone Saints
|
||||||
|
LIN => Linfield Wildcats
|
||||||
|
LOC => Lock Haven Bald Eagles
|
||||||
|
LOR => Loras College Duhawks
|
||||||
|
LUT => Luther Norse
|
||||||
|
LYC => Lycoming Warriors
|
||||||
|
M-OH => Miami (OH) RedHawks
|
||||||
|
MAC => Macalester Scots
|
||||||
|
MAI => Maine Maritime Mariners
|
||||||
|
MAN => Mansfield Mountaineers
|
||||||
|
MAR => Maryville College Fighting Scots
|
||||||
|
MAS => Mass Maritime Buccaneers
|
||||||
|
MAY => Mayville State Comets
|
||||||
|
MCM => McMurry War Hawks
|
||||||
|
MCN => McNeese Cowboys
|
||||||
|
MEN => Menlo College Oaks
|
||||||
|
MER => Merchant Marine Mariners
|
||||||
|
MERC => Mercyhurst Lakers
|
||||||
|
MES => Colorado Mesa Mavericks
|
||||||
|
MET => Methodist Monarchs
|
||||||
|
MH => Mars Hill Mountain Lions
|
||||||
|
MID => Midwestern State Mustangs
|
||||||
|
MIL => Millsaps Majors
|
||||||
|
MIN => Minot State Beavers
|
||||||
|
MIS => Missouri Western Griffons
|
||||||
|
MNST => Minnesota State Mavericks
|
||||||
|
MONM => Monmouth Hawks
|
||||||
|
MONT => Montana Grizzlies
|
||||||
|
MOR => Morningside Chiefs
|
||||||
|
MORE => Morehead State Eagles
|
||||||
|
MORG => Morgan State Bears
|
||||||
|
MOU => Mount Union Raiders
|
||||||
|
MRST => Marist Red Foxes
|
||||||
|
MSU => Michigan State Spartans
|
||||||
|
MTST => Montana State Bobcats
|
||||||
|
MTU => Michigan Tech Huskies
|
||||||
|
MUH => Muhlenberg Mules
|
||||||
|
MUR => Murray State Racers
|
||||||
|
MUS => Muskingum Fighting Muskies
|
||||||
|
MVSU => Mississippi Valley State Delta Devils
|
||||||
|
NAU => Northern Arizona Lumberjacks
|
||||||
|
NBY => Newberry Wolves
|
||||||
|
NCAT => North Carolina A&T Aggies
|
||||||
|
NCCU => North Carolina Central Eagles
|
||||||
|
NCST => NC State Wolfpack
|
||||||
|
NDOH => Notre Dame College Falcons
|
||||||
|
NDSU => North Dakota State Bison
|
||||||
|
NH => New Haven Chargers
|
||||||
|
NICH => Nicholls Colonels
|
||||||
|
NMH => New Mexico Highlands Cowboys
|
||||||
|
NMI => Northern Michigan Wildcats
|
||||||
|
NOR => Univ. of Northwestern-St. Paul Eagles
|
||||||
|
NORF => Norfolk State Spartans
|
||||||
|
OBE => Oberlin Yeomen
|
||||||
|
OHI => Ohio Northern Polar Bears
|
||||||
|
OKL => Oklahoma Baptist Bison
|
||||||
|
OLI => Olivet College Comets
|
||||||
|
OMA => Omaha Mavericks
|
||||||
|
OTT => Otterbein Cardinals
|
||||||
|
PAC => Pacific (OR) Boxers
|
||||||
|
PENN => Pennsylvania Quakers
|
||||||
|
PIKE => Pikeville Bears
|
||||||
|
PRE => Presentation College Saints
|
||||||
|
PRI => Principia College Panthers
|
||||||
|
PRIN => Princeton Tigers
|
||||||
|
PST => Pittsburg State Gorillas
|
||||||
|
RED => Redlands Bulldogs
|
||||||
|
RICH => Richmond Spiders
|
||||||
|
RIT => Rochester Yellow Jackets
|
||||||
|
ROB => Robert Morris (IL) Eagles
|
||||||
|
ROS => Rose-Hulman Engineers
|
||||||
|
SAC => Sacramento State Hornets
|
||||||
|
SAG => Saginaw Valley Cardinals
|
||||||
|
SDAK => South Dakota Coyotes
|
||||||
|
SET => Seton Hill Griffins
|
||||||
|
SIU => Southern Illinois Salukis
|
||||||
|
SLI => Slippery Rock The Rock
|
||||||
|
SOU => Southwestern College Moundbuilders
|
||||||
|
SPR => Springfield College Pride
|
||||||
|
ST => St. Scholastica Saints
|
||||||
|
STE => Stevenson University Mustangs
|
||||||
|
STET => Stetson Hatters
|
||||||
|
STO => Stonehill College Skyhawks
|
||||||
|
SUS => Susquehanna University River Hawks
|
||||||
|
SUU => Southern Utah Thunderbirds
|
||||||
|
TA&M => Texas A&M Aggies
|
||||||
|
TAY => Taylor Trojans
|
||||||
|
TIF => Tiffin University Dragons
|
||||||
|
TRI => Trinity University (TX) Tigers
|
||||||
|
TUF => Tufts University Jumbos
|
||||||
|
TXST => Texas State Bobcats
|
||||||
|
UAPB => Arkansas-Pine Bluff Golden Lions
|
||||||
|
UCD => UC Davis Aggies
|
||||||
|
UCONN => UConn Huskies
|
||||||
|
ULM => UL Monroe Warhawks
|
||||||
|
UMD => Minnesota-Duluth Bulldogs
|
||||||
|
UMDA => UMASS Dartmouth Corsairs
|
||||||
|
UML => UMass Lowell River Hawks
|
||||||
|
UNA => North Alabama Lions
|
||||||
|
UNCO => Northern Colorado Bears
|
||||||
|
UND => North Dakota Fighting Hawks
|
||||||
|
UNH => New Hampshire Wildcats
|
||||||
|
UNI => University of Mary Marauders
|
||||||
|
UNNY => Union Dutchmen
|
||||||
|
UNT => North Texas Mean Green
|
||||||
|
UPP => Upper Iowa Peacocks
|
||||||
|
URI => Rhode Island Rams
|
||||||
|
USA => South Alabama Jaguars
|
||||||
|
USD => San Diego Toreros
|
||||||
|
UTC => Chattanooga Mocs
|
||||||
|
UTI => Utica College Pioneers
|
||||||
|
VAL => Valley City State Vikings
|
||||||
|
VILL => Villanova Wildcats
|
||||||
|
VIR => Virginia State Trojans
|
||||||
|
VT => Virginia Tech Hokies
|
||||||
|
WAB => Wabash College Little Giants
|
||||||
|
WAS => Washington-Missouri Bears
|
||||||
|
WAY => Wayne State (MI) Warriors
|
||||||
|
WES => Westminster College (MO) Blue Jays
|
||||||
|
WHE => Wheaton College Illinois Thunder
|
||||||
|
WIL => Wilkes University Colonels
|
||||||
|
WIN => Wingate Bulldogs
|
||||||
|
WIS => Wisconsin-Platteville Pioneers
|
||||||
|
WOR => Worcester State College Lancers
|
||||||
|
YALE => Yale Bulldogs
|
||||||
|
|
||||||
|
NHL:
|
||||||
|
---
|
||||||
|
ARI => Arizona Coyotes
|
||||||
|
VGS => Vegas Golden Knights
|
||||||
|
|
||||||
|
SOCCER - BUNDESLIGA (GERMANY):
|
||||||
|
-----------------------------
|
||||||
|
DOR => Borussia Dortmund
|
||||||
|
KOL => 1. FC Köln
|
||||||
|
LEV => Bayer Leverkusen
|
||||||
|
STU => VfB Stuttgart
|
||||||
|
|
||||||
|
SOCCER - LIGUE 1 (FRANCE):
|
||||||
|
-------------------------
|
||||||
|
LYON => Lyon
|
||||||
|
MAR => Marseille
|
||||||
|
NICE => Nice
|
||||||
|
PSG => Paris Saint-Germain
|
||||||
|
|
||||||
|
SOCCER - PREMIER LEAGUE (ENGLAND):
|
||||||
|
---------------------------------
|
||||||
|
BUR => Burnley
|
||||||
|
LUT => Luton Town
|
||||||
|
SHU => Sheffield United
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
SUMMARY BY SPORT:
|
||||||
|
================================================================================
|
||||||
|
MLB: 1 missing
|
||||||
|
NCAAF: 295 missing
|
||||||
|
NHL: 2 missing
|
||||||
|
Soccer - Bundesliga (Germany): 4 missing
|
||||||
|
Soccer - Ligue 1 (France): 4 missing
|
||||||
|
Soccer - Premier League (England): 3 missing
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
FILENAMES NEEDED:
|
||||||
|
================================================================================
|
||||||
|
Add these PNG files to their respective directories:
|
||||||
|
|
||||||
|
assets/sports/mlb_logos/OAK.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AAMU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ACU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ADA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ADR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AIC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ALB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ALBS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ALCN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ALD.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ALF.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ALL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ALST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AMH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AND.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ANG.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ANN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/APSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ASH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ASP.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ASU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AUG.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AUR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AUS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AVE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AVI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/AZU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BAK.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BAL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BAT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BEC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BEL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BENT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BET.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BHS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BIR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BKN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BLA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BLOM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BLU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BOW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BRI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BRWN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BUCK.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BUE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BUF.png
|
||||||
|
assets/sports/ncaa_fbs_logos/BUT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CAM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CAP.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CAR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CARK.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CAS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CAT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CCSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CHA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CHI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CHSO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CLA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CLMB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/COE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/COL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/COLC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/COLG.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CON.png
|
||||||
|
assets/sports/ncaa_fbs_logos/COR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CP.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CRO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CUL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CUM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/CUR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DAK.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DART.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DAV.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DAY.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DEF.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DEL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DEP.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DIC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DRKE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DUB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/DUQ.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EAS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EDI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EIU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EKU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ELI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ELM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ELON.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EMO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EMP.png
|
||||||
|
assets/sports/ncaa_fbs_logos/END.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EOR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ETSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EUR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/EWU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FAY.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FDU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FER.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FIN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FIT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FLA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FOR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FRA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FRO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FRST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FTLW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/FUR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GAL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GAN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GEO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GET.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GLE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GMU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GRA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GRE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GRI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GRO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GUI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/GWEB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HAM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HAMP.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HAN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HAR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HARV.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HAS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HAW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HBU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HEI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HIL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HIR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HOB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HOW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/HUS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/IDHO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/IDST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ILST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ILW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/IND.png
|
||||||
|
assets/sports/ncaa_fbs_logos/INST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/IOW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ITH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/JKST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/JOH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/JUN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KAL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KAN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KIN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KNO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KUT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KYST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/KYW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LAG.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LAK.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LAM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LAN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LAW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LEB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LEH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LEW.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LIM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LIN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LOC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LOR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LUT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/LYC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/M-OH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MAC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MAI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MAN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MAR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MAS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MAY.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MCM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MCN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MEN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MER.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MERC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MES.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MET.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MID.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MIL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MIN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MIS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MNST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MONM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MONT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MOR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MORE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MORG.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MOU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MRST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MTST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MTU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MUH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MUR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MUS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/MVSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NAU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NBY.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NCAT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NCCU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NCST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NDOH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NDSU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NICH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NMH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NMI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NOR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/NORF.png
|
||||||
|
assets/sports/ncaa_fbs_logos/OBE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/OHI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/OKL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/OLI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/OMA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/OTT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/PAC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/PENN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/PIKE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/PRE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/PRI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/PRIN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/PST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/RED.png
|
||||||
|
assets/sports/ncaa_fbs_logos/RICH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/RIT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ROB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ROS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SAC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SAG.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SDAK.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SET.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SIU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SLI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SOU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SPR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/STE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/STET.png
|
||||||
|
assets/sports/ncaa_fbs_logos/STO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SUS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/SUU.png
|
||||||
|
assets/sports/ncaa_fbs_logos/TA&M.png
|
||||||
|
assets/sports/ncaa_fbs_logos/TAY.png
|
||||||
|
assets/sports/ncaa_fbs_logos/TIF.png
|
||||||
|
assets/sports/ncaa_fbs_logos/TRI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/TUF.png
|
||||||
|
assets/sports/ncaa_fbs_logos/TXST.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UAPB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UCD.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UCONN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/ULM.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UMD.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UMDA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UML.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UNA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UNCO.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UND.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UNH.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UNI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UNNY.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UNT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UPP.png
|
||||||
|
assets/sports/ncaa_fbs_logos/URI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/USA.png
|
||||||
|
assets/sports/ncaa_fbs_logos/USD.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UTC.png
|
||||||
|
assets/sports/ncaa_fbs_logos/UTI.png
|
||||||
|
assets/sports/ncaa_fbs_logos/VAL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/VILL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/VIR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/VT.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WAB.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WAS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WAY.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WES.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WHE.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WIL.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WIN.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WIS.png
|
||||||
|
assets/sports/ncaa_fbs_logos/WOR.png
|
||||||
|
assets/sports/ncaa_fbs_logos/YALE.png
|
||||||
|
assets/sports/nhl_logos/ARI.png
|
||||||
|
assets/sports/nhl_logos/VGS.png
|
||||||
|
assets/sports/soccer_logos/DOR.png
|
||||||
|
assets/sports/soccer_logos/KOL.png
|
||||||
|
assets/sports/soccer_logos/LEV.png
|
||||||
|
assets/sports/soccer_logos/STU.png
|
||||||
|
assets/sports/soccer_logos/LYON.png
|
||||||
|
assets/sports/soccer_logos/MAR.png
|
||||||
|
assets/sports/soccer_logos/NICE.png
|
||||||
|
assets/sports/soccer_logos/PSG.png
|
||||||
|
assets/sports/soccer_logos/BUR.png
|
||||||
|
assets/sports/soccer_logos/LUT.png
|
||||||
|
assets/sports/soccer_logos/SHU.png
|
||||||
160
save_missing_teams.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script to save the missing teams list to a file for future reference.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def save_missing_teams():
|
||||||
|
"""Save the missing teams list to a file."""
|
||||||
|
|
||||||
|
# Define the sports directories and their corresponding sections in the abbreviations file
|
||||||
|
sports_dirs = {
|
||||||
|
'mlb_logos': 'MLB',
|
||||||
|
'nba_logos': 'NBA',
|
||||||
|
'nfl_logos': 'NFL',
|
||||||
|
'nhl_logos': 'NHL',
|
||||||
|
'ncaa_fbs_logos': ['NCAAF', 'NCAA Conferences/Divisions', 'NCAA_big10', 'NCAA_big12', 'NCAA_acc', 'NCAA_sec', 'NCAA_pac12', 'NCAA_american', 'NCAA_cusa', 'NCAA_mac', 'NCAA_mwc', 'NCAA_sunbelt', 'NCAA_ind', 'NCAA_ovc', 'NCAA_col', 'NCAA_usa', 'NCAA_bigw'],
|
||||||
|
'soccer_logos': ['Soccer - Premier League (England)', 'Soccer - La Liga (Spain)', 'Soccer - Bundesliga (Germany)', 'Soccer - Serie A (Italy)', 'Soccer - Ligue 1 (France)', 'Soccer - Champions League', 'Soccer - Other Teams'],
|
||||||
|
'milb_logos': 'MiLB'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read the abbreviations file
|
||||||
|
abbreviations_file = Path("assets/sports/all_team_abbreviations.txt")
|
||||||
|
if not abbreviations_file.exists():
|
||||||
|
print("Error: all_team_abbreviations.txt not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(abbreviations_file, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# Parse teams from the abbreviations file
|
||||||
|
teams_by_sport = {}
|
||||||
|
current_section = None
|
||||||
|
|
||||||
|
for line in content.split('\n'):
|
||||||
|
original_line = line
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Check if this is a section header (not indented and no arrow)
|
||||||
|
if line and not original_line.startswith(' ') and ' => ' not in line:
|
||||||
|
current_section = line
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if this is a team entry (indented and has arrow)
|
||||||
|
if original_line.startswith(' ') and ' => ' in line:
|
||||||
|
parts = line.split(' => ')
|
||||||
|
if len(parts) == 2:
|
||||||
|
abbr = parts[0].strip()
|
||||||
|
team_name = parts[1].strip()
|
||||||
|
|
||||||
|
if current_section not in teams_by_sport:
|
||||||
|
teams_by_sport[current_section] = []
|
||||||
|
teams_by_sport[current_section].append((abbr, team_name))
|
||||||
|
|
||||||
|
# Collect all missing teams
|
||||||
|
all_missing_teams = []
|
||||||
|
|
||||||
|
for logo_dir, sections in sports_dirs.items():
|
||||||
|
logo_path = Path(f"assets/sports/{logo_dir}")
|
||||||
|
|
||||||
|
if not logo_path.exists():
|
||||||
|
print(f"⚠️ Logo directory not found: {logo_path}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get all PNG files in the directory
|
||||||
|
logo_files = [f.stem for f in logo_path.glob("*.png")]
|
||||||
|
|
||||||
|
# Check teams for this sport
|
||||||
|
if isinstance(sections, str):
|
||||||
|
sections = [sections]
|
||||||
|
|
||||||
|
for section in sections:
|
||||||
|
if section not in teams_by_sport:
|
||||||
|
continue
|
||||||
|
|
||||||
|
missing_teams = []
|
||||||
|
|
||||||
|
for abbr, team_name in teams_by_sport[section]:
|
||||||
|
# Check if logo exists (case-insensitive)
|
||||||
|
logo_found = False
|
||||||
|
for logo_file in logo_files:
|
||||||
|
if logo_file.lower() == abbr.lower():
|
||||||
|
logo_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not logo_found:
|
||||||
|
missing_teams.append((abbr, team_name))
|
||||||
|
|
||||||
|
if missing_teams:
|
||||||
|
all_missing_teams.extend([(section, abbr, team_name) for abbr, team_name in missing_teams])
|
||||||
|
|
||||||
|
# Sort by sport and then by team abbreviation
|
||||||
|
all_missing_teams.sort(key=lambda x: (x[0], x[1]))
|
||||||
|
|
||||||
|
# Save to file
|
||||||
|
output_file = "missing_team_logos.txt"
|
||||||
|
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
|
f.write("=" * 80 + "\n")
|
||||||
|
f.write("MISSING TEAM LOGOS - COMPLETE LIST\n")
|
||||||
|
f.write("=" * 80 + "\n")
|
||||||
|
f.write(f"Total missing teams: {len(all_missing_teams)}\n")
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
|
current_sport = None
|
||||||
|
for section, abbr, team_name in all_missing_teams:
|
||||||
|
if section != current_sport:
|
||||||
|
current_sport = section
|
||||||
|
f.write(f"\n{section.upper()}:\n")
|
||||||
|
f.write("-" * len(section) + "\n")
|
||||||
|
|
||||||
|
f.write(f" {abbr:>8} => {team_name}\n")
|
||||||
|
|
||||||
|
f.write("\n" + "=" * 80 + "\n")
|
||||||
|
f.write("SUMMARY BY SPORT:\n")
|
||||||
|
f.write("=" * 80 + "\n")
|
||||||
|
|
||||||
|
# Count by sport
|
||||||
|
sport_counts = {}
|
||||||
|
for section, abbr, team_name in all_missing_teams:
|
||||||
|
if section not in sport_counts:
|
||||||
|
sport_counts[section] = 0
|
||||||
|
sport_counts[section] += 1
|
||||||
|
|
||||||
|
for sport, count in sorted(sport_counts.items()):
|
||||||
|
f.write(f"{sport:>30}: {count:>3} missing\n")
|
||||||
|
|
||||||
|
f.write("\n" + "=" * 80 + "\n")
|
||||||
|
f.write("FILENAMES NEEDED:\n")
|
||||||
|
f.write("=" * 80 + "\n")
|
||||||
|
f.write("Add these PNG files to their respective directories:\n")
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
|
for section, abbr, team_name in all_missing_teams:
|
||||||
|
# Determine the directory based on the section
|
||||||
|
if 'MLB' in section:
|
||||||
|
dir_name = 'mlb_logos'
|
||||||
|
elif 'NBA' in section:
|
||||||
|
dir_name = 'nba_logos'
|
||||||
|
elif 'NFL' in section:
|
||||||
|
dir_name = 'nfl_logos'
|
||||||
|
elif 'NHL' in section:
|
||||||
|
dir_name = 'nhl_logos'
|
||||||
|
elif 'NCAA' in section:
|
||||||
|
dir_name = 'ncaa_fbs_logos'
|
||||||
|
elif 'Soccer' in section:
|
||||||
|
dir_name = 'soccer_logos'
|
||||||
|
elif 'MiLB' in section:
|
||||||
|
dir_name = 'milb_logos'
|
||||||
|
else:
|
||||||
|
dir_name = 'unknown'
|
||||||
|
|
||||||
|
f.write(f"assets/sports/{dir_name}/{abbr}.png\n")
|
||||||
|
|
||||||
|
print(f"✅ Missing teams list saved to: {output_file}")
|
||||||
|
print(f"📊 Total missing teams: {len(all_missing_teams)}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
save_missing_teams()
|
||||||
@@ -581,6 +581,11 @@ class CacheManager:
|
|||||||
'memory_ttl': 172800,
|
'memory_ttl': 172800,
|
||||||
'force_refresh': False
|
'force_refresh': False
|
||||||
},
|
},
|
||||||
|
'leaderboard': {
|
||||||
|
'max_age': 604800, # 7 days (1 week) - football rankings updated weekly
|
||||||
|
'memory_ttl': 1209600, # 14 days in memory
|
||||||
|
'force_refresh': False
|
||||||
|
},
|
||||||
|
|
||||||
# News and odds
|
# News and odds
|
||||||
'news': {
|
'news': {
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ class CalendarManager:
|
|||||||
logger.info(f"Calendar configuration: enabled={self.enabled}, update_interval={self.update_interval}, max_events={self.max_events}, calendars={self.calendars}")
|
logger.info(f"Calendar configuration: enabled={self.enabled}, update_interval={self.update_interval}, max_events={self.max_events}, calendars={self.calendars}")
|
||||||
|
|
||||||
# Get timezone from config
|
# Get timezone from config
|
||||||
self.config_manager = ConfigManager()
|
timezone_str = self.config.get('timezone', 'UTC')
|
||||||
timezone_str = self.config_manager.get_timezone()
|
|
||||||
logger.info(f"Loading timezone from config: {timezone_str}")
|
logger.info(f"Loading timezone from config: {timezone_str}")
|
||||||
try:
|
try:
|
||||||
self.timezone = pytz.timezone(timezone_str)
|
self.timezone = pytz.timezone(timezone_str)
|
||||||
|
|||||||
10
src/clock.py
@@ -10,7 +10,13 @@ from src.display_manager import DisplayManager
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Clock:
|
class Clock:
|
||||||
def __init__(self, display_manager: DisplayManager = None):
|
def __init__(self, display_manager: DisplayManager = None, config: Dict[str, Any] = None):
|
||||||
|
if config is not None:
|
||||||
|
# Use provided config
|
||||||
|
self.config = config
|
||||||
|
self.config_manager = None # Not needed when config is provided
|
||||||
|
else:
|
||||||
|
# Fallback: create ConfigManager and load config (for standalone usage)
|
||||||
self.config_manager = ConfigManager()
|
self.config_manager = ConfigManager()
|
||||||
self.config = self.config_manager.load_config()
|
self.config = self.config_manager.load_config()
|
||||||
# Use the provided display_manager or create a new one if none provided
|
# Use the provided display_manager or create a new one if none provided
|
||||||
@@ -31,7 +37,7 @@ class Clock:
|
|||||||
|
|
||||||
def _get_timezone(self) -> pytz.timezone:
|
def _get_timezone(self) -> pytz.timezone:
|
||||||
"""Get timezone from the config file."""
|
"""Get timezone from the config file."""
|
||||||
config_timezone = self.config_manager.get_timezone()
|
config_timezone = self.config.get('timezone', 'UTC')
|
||||||
try:
|
try:
|
||||||
return pytz.timezone(config_timezone)
|
return pytz.timezone(config_timezone)
|
||||||
except pytz.exceptions.UnknownTimeZoneError:
|
except pytz.exceptions.UnknownTimeZoneError:
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from src.cache_manager import CacheManager
|
|||||||
from src.stock_manager import StockManager
|
from src.stock_manager import StockManager
|
||||||
from src.stock_news_manager import StockNewsManager
|
from src.stock_news_manager import StockNewsManager
|
||||||
from src.odds_ticker_manager import OddsTickerManager
|
from src.odds_ticker_manager import OddsTickerManager
|
||||||
|
from src.leaderboard_manager import LeaderboardManager
|
||||||
from src.nhl_managers import NHLLiveManager, NHLRecentManager, NHLUpcomingManager
|
from src.nhl_managers import NHLLiveManager, NHLRecentManager, NHLUpcomingManager
|
||||||
from src.nba_managers import NBALiveManager, NBARecentManager, NBAUpcomingManager
|
from src.nba_managers import NBALiveManager, NBARecentManager, NBAUpcomingManager
|
||||||
from src.mlb_manager import MLBLiveManager, MLBRecentManager, MLBUpcomingManager
|
from src.mlb_manager import MLBLiveManager, MLBRecentManager, MLBUpcomingManager
|
||||||
@@ -55,16 +56,17 @@ class DisplayController:
|
|||||||
|
|
||||||
# Initialize display modes
|
# Initialize display modes
|
||||||
init_time = time.time()
|
init_time = time.time()
|
||||||
self.clock = Clock(self.display_manager) if self.config.get('clock', {}).get('enabled', True) else None
|
self.clock = Clock(self.display_manager, self.config) if self.config.get('clock', {}).get('enabled', True) else None
|
||||||
self.weather = WeatherManager(self.config, self.display_manager) if self.config.get('weather', {}).get('enabled', False) else None
|
self.weather = WeatherManager(self.config, self.display_manager) if self.config.get('weather', {}).get('enabled', False) else None
|
||||||
self.stocks = StockManager(self.config, self.display_manager) if self.config.get('stocks', {}).get('enabled', False) else None
|
self.stocks = StockManager(self.config, self.display_manager) if self.config.get('stocks', {}).get('enabled', False) else None
|
||||||
self.news = StockNewsManager(self.config, self.display_manager) if self.config.get('stock_news', {}).get('enabled', False) else None
|
self.news = StockNewsManager(self.config, self.display_manager) if self.config.get('stock_news', {}).get('enabled', False) else None
|
||||||
self.odds_ticker = OddsTickerManager(self.config, self.display_manager) if self.config.get('odds_ticker', {}).get('enabled', False) else None
|
self.odds_ticker = OddsTickerManager(self.config, self.display_manager) if self.config.get('odds_ticker', {}).get('enabled', False) else None
|
||||||
|
self.leaderboard = LeaderboardManager(self.config, self.display_manager) if self.config.get('leaderboard', {}).get('enabled', False) else None
|
||||||
self.calendar = CalendarManager(self.display_manager, self.config) if self.config.get('calendar', {}).get('enabled', False) else None
|
self.calendar = CalendarManager(self.display_manager, self.config) if self.config.get('calendar', {}).get('enabled', False) else None
|
||||||
self.youtube = YouTubeDisplay(self.display_manager, self.config) if self.config.get('youtube', {}).get('enabled', False) else None
|
self.youtube = YouTubeDisplay(self.display_manager, self.config) if self.config.get('youtube', {}).get('enabled', False) else None
|
||||||
self.text_display = TextDisplay(self.display_manager, self.config) if self.config.get('text_display', {}).get('enabled', False) else None
|
self.text_display = TextDisplay(self.display_manager, self.config) if self.config.get('text_display', {}).get('enabled', False) else None
|
||||||
self.of_the_day = OfTheDayManager(self.display_manager, self.config) if self.config.get('of_the_day', {}).get('enabled', False) else None
|
self.of_the_day = OfTheDayManager(self.display_manager, self.config) if self.config.get('of_the_day', {}).get('enabled', False) else None
|
||||||
self.news_manager = NewsManager(self.config, self.display_manager) if self.config.get('news_manager', {}).get('enabled', False) else None
|
self.news_manager = NewsManager(self.config, self.display_manager, self.config_manager) if self.config.get('news_manager', {}).get('enabled', False) else None
|
||||||
logger.info(f"Calendar Manager initialized: {'Object' if self.calendar else 'None'}")
|
logger.info(f"Calendar Manager initialized: {'Object' if self.calendar else 'None'}")
|
||||||
logger.info(f"Text Display initialized: {'Object' if self.text_display else 'None'}")
|
logger.info(f"Text Display initialized: {'Object' if self.text_display else 'None'}")
|
||||||
logger.info(f"OfTheDay Manager initialized: {'Object' if self.of_the_day else 'None'}")
|
logger.info(f"OfTheDay Manager initialized: {'Object' if self.of_the_day else 'None'}")
|
||||||
@@ -258,6 +260,7 @@ class DisplayController:
|
|||||||
if self.stocks: self.available_modes.append('stocks')
|
if self.stocks: self.available_modes.append('stocks')
|
||||||
if self.news: self.available_modes.append('stock_news')
|
if self.news: self.available_modes.append('stock_news')
|
||||||
if self.odds_ticker: self.available_modes.append('odds_ticker')
|
if self.odds_ticker: self.available_modes.append('odds_ticker')
|
||||||
|
if self.leaderboard: self.available_modes.append('leaderboard')
|
||||||
if self.calendar: self.available_modes.append('calendar')
|
if self.calendar: self.available_modes.append('calendar')
|
||||||
if self.youtube: self.available_modes.append('youtube')
|
if self.youtube: self.available_modes.append('youtube')
|
||||||
if self.text_display: self.available_modes.append('text_display')
|
if self.text_display: self.available_modes.append('text_display')
|
||||||
@@ -427,11 +430,9 @@ class DisplayController:
|
|||||||
logger.info(f"Initial display mode: {self.current_display_mode}")
|
logger.info(f"Initial display mode: {self.current_display_mode}")
|
||||||
logger.info("DisplayController initialized with display_manager: %s", id(self.display_manager))
|
logger.info("DisplayController initialized with display_manager: %s", id(self.display_manager))
|
||||||
|
|
||||||
# --- SCHEDULING & CONFIG REFRESH ---
|
# --- SCHEDULING ---
|
||||||
self.config_check_interval = 30
|
|
||||||
self.last_config_check = 0
|
|
||||||
self.is_display_active = True
|
self.is_display_active = True
|
||||||
self._load_config() # Initial load of schedule
|
self._load_schedule_config() # Load schedule config once at startup
|
||||||
|
|
||||||
def _handle_music_update(self, track_info: Dict[str, Any], significant_change: bool = False):
|
def _handle_music_update(self, track_info: Dict[str, Any], significant_change: bool = False):
|
||||||
"""Callback for when music track info changes."""
|
"""Callback for when music track info changes."""
|
||||||
@@ -516,6 +517,20 @@ class DisplayController:
|
|||||||
# Fall back to configured duration
|
# Fall back to configured duration
|
||||||
return self.display_durations.get(mode_key, 60)
|
return self.display_durations.get(mode_key, 60)
|
||||||
|
|
||||||
|
# Handle dynamic duration for leaderboard
|
||||||
|
if mode_key == 'leaderboard' and self.leaderboard:
|
||||||
|
try:
|
||||||
|
dynamic_duration = self.leaderboard.get_dynamic_duration()
|
||||||
|
# Only log if duration has changed or we haven't logged this duration yet
|
||||||
|
if not hasattr(self, '_last_logged_leaderboard_duration') or self._last_logged_leaderboard_duration != dynamic_duration:
|
||||||
|
logger.info(f"Using dynamic duration for leaderboard: {dynamic_duration} seconds")
|
||||||
|
self._last_logged_leaderboard_duration = dynamic_duration
|
||||||
|
return dynamic_duration
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting dynamic duration for leaderboard: {e}")
|
||||||
|
# Fall back to configured duration
|
||||||
|
return self.display_durations.get(mode_key, 60)
|
||||||
|
|
||||||
# Simplify weather key handling
|
# Simplify weather key handling
|
||||||
if mode_key.startswith('weather_'):
|
if mode_key.startswith('weather_'):
|
||||||
return self.display_durations.get(mode_key, 15)
|
return self.display_durations.get(mode_key, 15)
|
||||||
@@ -530,6 +545,24 @@ class DisplayController:
|
|||||||
|
|
||||||
def _update_modules(self):
|
def _update_modules(self):
|
||||||
"""Call update methods on active managers."""
|
"""Call update methods on active managers."""
|
||||||
|
# Check if we're currently scrolling and defer updates if so
|
||||||
|
if self.display_manager.is_currently_scrolling():
|
||||||
|
logger.debug("Display is currently scrolling, deferring module updates")
|
||||||
|
# Defer updates for modules that might cause lag during scrolling
|
||||||
|
if self.odds_ticker:
|
||||||
|
self.display_manager.defer_update(self.odds_ticker.update, priority=1)
|
||||||
|
if self.stocks:
|
||||||
|
self.display_manager.defer_update(self.stocks.update_stock_data, priority=2)
|
||||||
|
if self.news:
|
||||||
|
self.display_manager.defer_update(self.news.update_news_data, priority=2)
|
||||||
|
# Continue with non-scrolling-sensitive updates
|
||||||
|
if self.weather: self.weather.get_weather()
|
||||||
|
if self.calendar: self.calendar.update(time.time())
|
||||||
|
if self.youtube: self.youtube.update()
|
||||||
|
if self.text_display: self.text_display.update()
|
||||||
|
if self.of_the_day: self.of_the_day.update(time.time())
|
||||||
|
else:
|
||||||
|
# Not scrolling, perform all updates normally
|
||||||
if self.weather: self.weather.get_weather()
|
if self.weather: self.weather.get_weather()
|
||||||
if self.stocks: self.stocks.update_stock_data()
|
if self.stocks: self.stocks.update_stock_data()
|
||||||
if self.news: self.news.update_news_data()
|
if self.news: self.news.update_news_data()
|
||||||
@@ -538,6 +571,7 @@ class DisplayController:
|
|||||||
if self.youtube: self.youtube.update()
|
if self.youtube: self.youtube.update()
|
||||||
if self.text_display: self.text_display.update()
|
if self.text_display: self.text_display.update()
|
||||||
if self.of_the_day: self.of_the_day.update(time.time())
|
if self.of_the_day: self.of_the_day.update(time.time())
|
||||||
|
|
||||||
# News manager fetches data when displayed, not during updates
|
# News manager fetches data when displayed, not during updates
|
||||||
# if self.news_manager: self.news_manager.fetch_news_data()
|
# if self.news_manager: self.news_manager.fetch_news_data()
|
||||||
|
|
||||||
@@ -829,14 +863,14 @@ class DisplayController:
|
|||||||
self.ncaa_fb_showing_recent = True # Reset to recent for the new team
|
self.ncaa_fb_showing_recent = True # Reset to recent for the new team
|
||||||
|
|
||||||
# --- SCHEDULING METHODS ---
|
# --- SCHEDULING METHODS ---
|
||||||
def _load_config(self):
|
def _load_schedule_config(self):
|
||||||
"""Load configuration from the config manager and parse schedule settings."""
|
"""Load schedule configuration once at startup."""
|
||||||
self.config = self.config_manager.load_config()
|
|
||||||
schedule_config = self.config.get('schedule', {})
|
schedule_config = self.config.get('schedule', {})
|
||||||
self.schedule_enabled = schedule_config.get('enabled', False)
|
self.schedule_enabled = schedule_config.get('enabled', False)
|
||||||
try:
|
try:
|
||||||
self.start_time = datetime.strptime(schedule_config.get('start_time', '07:00'), '%H:%M').time()
|
self.start_time = datetime.strptime(schedule_config.get('start_time', '07:00'), '%H:%M').time()
|
||||||
self.end_time = datetime.strptime(schedule_config.get('end_time', '22:00'), '%H:%M').time()
|
self.end_time = datetime.strptime(schedule_config.get('end_time', '22:00'), '%H:%M').time()
|
||||||
|
logger.info(f"Schedule loaded: enabled={self.schedule_enabled}, start={self.start_time}, end={self.end_time}")
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
logger.warning("Invalid time format in schedule config. Using defaults.")
|
logger.warning("Invalid time format in schedule config. Using defaults.")
|
||||||
self.start_time = time_obj(7, 0)
|
self.start_time = time_obj(7, 0)
|
||||||
@@ -926,12 +960,7 @@ class DisplayController:
|
|||||||
while True:
|
while True:
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
|
|
||||||
# Periodically check for config changes
|
# Check the schedule (no config reload needed)
|
||||||
if current_time - self.last_config_check > self.config_check_interval:
|
|
||||||
self._load_config()
|
|
||||||
self.last_config_check = current_time
|
|
||||||
|
|
||||||
# Enforce the schedule
|
|
||||||
self._check_schedule()
|
self._check_schedule()
|
||||||
if not self.is_display_active:
|
if not self.is_display_active:
|
||||||
time.sleep(60)
|
time.sleep(60)
|
||||||
@@ -940,6 +969,9 @@ class DisplayController:
|
|||||||
# Update data for all modules first
|
# Update data for all modules first
|
||||||
self._update_modules()
|
self._update_modules()
|
||||||
|
|
||||||
|
# Process any deferred updates that may have accumulated
|
||||||
|
self.display_manager.process_deferred_updates()
|
||||||
|
|
||||||
# Update live modes in rotation if needed
|
# Update live modes in rotation if needed
|
||||||
self._update_live_modes_in_rotation()
|
self._update_live_modes_in_rotation()
|
||||||
|
|
||||||
@@ -1097,6 +1129,8 @@ class DisplayController:
|
|||||||
manager_to_display = self.news
|
manager_to_display = self.news
|
||||||
elif self.current_display_mode == 'odds_ticker' and self.odds_ticker:
|
elif self.current_display_mode == 'odds_ticker' and self.odds_ticker:
|
||||||
manager_to_display = self.odds_ticker
|
manager_to_display = self.odds_ticker
|
||||||
|
elif self.current_display_mode == 'leaderboard' and self.leaderboard:
|
||||||
|
manager_to_display = self.leaderboard
|
||||||
elif self.current_display_mode == 'calendar' and self.calendar:
|
elif self.current_display_mode == 'calendar' and self.calendar:
|
||||||
manager_to_display = self.calendar
|
manager_to_display = self.calendar
|
||||||
elif self.current_display_mode == 'youtube' and self.youtube:
|
elif self.current_display_mode == 'youtube' and self.youtube:
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ class DisplayManager:
|
|||||||
self._snapshot_path = "/tmp/led_matrix_preview.png"
|
self._snapshot_path = "/tmp/led_matrix_preview.png"
|
||||||
self._snapshot_min_interval_sec = 0.2 # max ~5 fps
|
self._snapshot_min_interval_sec = 0.2 # max ~5 fps
|
||||||
self._last_snapshot_ts = 0.0
|
self._last_snapshot_ts = 0.0
|
||||||
|
|
||||||
|
# Scrolling state tracking for graceful updates
|
||||||
|
self._scrolling_state = {
|
||||||
|
'is_scrolling': False,
|
||||||
|
'last_scroll_activity': 0,
|
||||||
|
'scroll_inactivity_threshold': 2.0, # seconds of inactivity before considering "not scrolling"
|
||||||
|
'deferred_updates': []
|
||||||
|
}
|
||||||
|
|
||||||
self._setup_matrix()
|
self._setup_matrix()
|
||||||
logger.info("Matrix setup completed in %.3f seconds", time.time() - start_time)
|
logger.info("Matrix setup completed in %.3f seconds", time.time() - start_time)
|
||||||
|
|
||||||
@@ -634,6 +643,77 @@ class DisplayManager:
|
|||||||
|
|
||||||
return dt.strftime(f"%b %-d{suffix}")
|
return dt.strftime(f"%b %-d{suffix}")
|
||||||
|
|
||||||
|
def set_scrolling_state(self, is_scrolling: bool):
|
||||||
|
"""Set the current scrolling state. Call this when a display starts/stops scrolling."""
|
||||||
|
current_time = time.time()
|
||||||
|
self._scrolling_state['is_scrolling'] = is_scrolling
|
||||||
|
if is_scrolling:
|
||||||
|
self._scrolling_state['last_scroll_activity'] = current_time
|
||||||
|
logger.debug(f"Scrolling state set to: {is_scrolling}")
|
||||||
|
|
||||||
|
def is_currently_scrolling(self) -> bool:
|
||||||
|
"""Check if the display is currently in a scrolling state."""
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
# If explicitly not scrolling, return False
|
||||||
|
if not self._scrolling_state['is_scrolling']:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If we've been inactive for the threshold period, consider it not scrolling
|
||||||
|
if current_time - self._scrolling_state['last_scroll_activity'] > self._scrolling_state['scroll_inactivity_threshold']:
|
||||||
|
self._scrolling_state['is_scrolling'] = False
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def defer_update(self, update_func, priority: int = 0):
|
||||||
|
"""Defer an update function to be called when not scrolling.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
update_func: Function to call when not scrolling
|
||||||
|
priority: Priority level (lower numbers = higher priority)
|
||||||
|
"""
|
||||||
|
self._scrolling_state['deferred_updates'].append({
|
||||||
|
'func': update_func,
|
||||||
|
'priority': priority,
|
||||||
|
'timestamp': time.time()
|
||||||
|
})
|
||||||
|
# Sort by priority (lower numbers first)
|
||||||
|
self._scrolling_state['deferred_updates'].sort(key=lambda x: x['priority'])
|
||||||
|
logger.debug(f"Deferred update added. Total deferred: {len(self._scrolling_state['deferred_updates'])}")
|
||||||
|
|
||||||
|
def process_deferred_updates(self):
|
||||||
|
"""Process any deferred updates if not currently scrolling."""
|
||||||
|
if self.is_currently_scrolling():
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._scrolling_state['deferred_updates']:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Process all deferred updates
|
||||||
|
updates_to_process = self._scrolling_state['deferred_updates'].copy()
|
||||||
|
self._scrolling_state['deferred_updates'].clear()
|
||||||
|
|
||||||
|
logger.debug(f"Processing {len(updates_to_process)} deferred updates")
|
||||||
|
|
||||||
|
for update_info in updates_to_process:
|
||||||
|
try:
|
||||||
|
update_info['func']()
|
||||||
|
logger.debug("Deferred update executed successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error executing deferred update: {e}")
|
||||||
|
# Re-add failed updates for retry
|
||||||
|
self._scrolling_state['deferred_updates'].append(update_info)
|
||||||
|
|
||||||
|
def get_scrolling_stats(self) -> dict:
|
||||||
|
"""Get current scrolling statistics for debugging."""
|
||||||
|
return {
|
||||||
|
'is_scrolling': self._scrolling_state['is_scrolling'],
|
||||||
|
'last_activity': self._scrolling_state['last_scroll_activity'],
|
||||||
|
'deferred_count': len(self._scrolling_state['deferred_updates']),
|
||||||
|
'inactivity_threshold': self._scrolling_state['scroll_inactivity_threshold']
|
||||||
|
}
|
||||||
|
|
||||||
def _write_snapshot_if_due(self) -> None:
|
def _write_snapshot_if_due(self) -> None:
|
||||||
"""Write the current image to a PNG snapshot file at a limited frequency."""
|
"""Write the current image to a PNG snapshot file at a limited frequency."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
1279
src/leaderboard_manager.py
Normal file
662
src/logo_downloader.py
Normal file
@@ -0,0 +1,662 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Centralized logo downloader utility for automatically fetching team logos from ESPN API.
|
||||||
|
This module provides functionality to download missing team logos for various sports leagues,
|
||||||
|
with special support for FCS teams and other NCAA divisions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
|
from pathlib import Path
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
from urllib3.util.retry import Retry
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class LogoDownloader:
|
||||||
|
"""Centralized logo downloader for team logos from ESPN API."""
|
||||||
|
|
||||||
|
# ESPN API endpoints for different sports/leagues
|
||||||
|
API_ENDPOINTS = {
|
||||||
|
'nfl': 'https://site.api.espn.com/apis/site/v2/sports/football/nfl/teams',
|
||||||
|
'nba': 'https://site.api.espn.com/apis/site/v2/sports/basketball/nba/teams',
|
||||||
|
'mlb': 'https://site.api.espn.com/apis/site/v2/sports/baseball/mlb/teams',
|
||||||
|
'nhl': 'https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/teams',
|
||||||
|
'ncaa_fb': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams',
|
||||||
|
'ncaa_fb_all': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams', # Includes FCS
|
||||||
|
'fcs': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams', # FCS teams from same endpoint
|
||||||
|
'ncaam_basketball': 'https://site.api.espn.com/apis/site/v2/sports/basketball/mens-college-basketball/teams',
|
||||||
|
'ncaa_baseball': 'https://site.api.espn.com/apis/site/v2/sports/baseball/college-baseball/teams'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Directory mappings for different leagues
|
||||||
|
LOGO_DIRECTORIES = {
|
||||||
|
'nfl': 'assets/sports/nfl_logos',
|
||||||
|
'nba': 'assets/sports/nba_logos',
|
||||||
|
'mlb': 'assets/sports/mlb_logos',
|
||||||
|
'nhl': 'assets/sports/nhl_logos',
|
||||||
|
'ncaa_fb': 'assets/sports/ncaa_fbs_logos',
|
||||||
|
'ncaa_fb_all': 'assets/sports/ncaa_fbs_logos', # FCS teams go in same directory
|
||||||
|
'fcs': 'assets/sports/ncaa_fbs_logos', # FCS teams go in same directory
|
||||||
|
'ncaam_basketball': 'assets/sports/ncaa_fbs_logos',
|
||||||
|
'ncaa_baseball': 'assets/sports/ncaa_fbs_logos'
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, request_timeout: int = 30, retry_attempts: int = 3):
|
||||||
|
"""Initialize the logo downloader with HTTP session and retry logic."""
|
||||||
|
self.request_timeout = request_timeout
|
||||||
|
self.retry_attempts = retry_attempts
|
||||||
|
|
||||||
|
# Set up session with retry logic
|
||||||
|
self.session = requests.Session()
|
||||||
|
retry_strategy = Retry(
|
||||||
|
total=retry_attempts,
|
||||||
|
backoff_factor=1,
|
||||||
|
status_forcelist=[429, 500, 502, 503, 504],
|
||||||
|
allowed_methods=["GET", "HEAD", "OPTIONS"]
|
||||||
|
)
|
||||||
|
adapter = HTTPAdapter(max_retries=retry_strategy)
|
||||||
|
self.session.mount("https://", adapter)
|
||||||
|
self.session.mount("http://", adapter)
|
||||||
|
|
||||||
|
# Set up headers
|
||||||
|
self.headers = {
|
||||||
|
'User-Agent': 'LEDMatrix/1.0 (https://github.com/yourusername/LEDMatrix; contact@example.com)',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Accept-Language': 'en-US,en;q=0.9',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
}
|
||||||
|
|
||||||
|
def normalize_abbreviation(self, abbreviation: str) -> str:
|
||||||
|
"""Normalize team abbreviation for consistent filename usage."""
|
||||||
|
# Handle special characters that can cause filesystem issues
|
||||||
|
normalized = abbreviation.upper()
|
||||||
|
# Replace problematic characters with safe alternatives
|
||||||
|
normalized = normalized.replace('&', 'AND')
|
||||||
|
normalized = normalized.replace('/', '_')
|
||||||
|
normalized = normalized.replace('\\', '_')
|
||||||
|
normalized = normalized.replace(':', '_')
|
||||||
|
normalized = normalized.replace('*', '_')
|
||||||
|
normalized = normalized.replace('?', '_')
|
||||||
|
normalized = normalized.replace('"', '_')
|
||||||
|
normalized = normalized.replace('<', '_')
|
||||||
|
normalized = normalized.replace('>', '_')
|
||||||
|
normalized = normalized.replace('|', '_')
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
def get_logo_directory(self, league: str) -> str:
|
||||||
|
"""Get the logo directory for a given league."""
|
||||||
|
return self.LOGO_DIRECTORIES.get(league, f'assets/sports/{league}_logos')
|
||||||
|
|
||||||
|
def ensure_logo_directory(self, logo_dir: str) -> bool:
|
||||||
|
"""Ensure the logo directory exists, create if necessary."""
|
||||||
|
try:
|
||||||
|
os.makedirs(logo_dir, exist_ok=True)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to create logo directory {logo_dir}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def download_logo(self, logo_url: str, filepath: Path, team_name: str) -> bool:
|
||||||
|
"""Download a single logo from URL and save to filepath."""
|
||||||
|
try:
|
||||||
|
response = self.session.get(logo_url, headers=self.headers, timeout=self.request_timeout)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# Verify it's actually an image
|
||||||
|
content_type = response.headers.get('content-type', '').lower()
|
||||||
|
if not any(img_type in content_type for img_type in ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']):
|
||||||
|
logger.warning(f"Downloaded content for {team_name} is not an image: {content_type}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(filepath, 'wb') as f:
|
||||||
|
f.write(response.content)
|
||||||
|
|
||||||
|
# Verify and convert the downloaded image to RGBA format
|
||||||
|
try:
|
||||||
|
with Image.open(filepath) as img:
|
||||||
|
# Convert to RGBA to avoid PIL warnings about palette images with transparency
|
||||||
|
if img.mode in ('P', 'LA', 'L'):
|
||||||
|
# Convert palette or grayscale images to RGBA
|
||||||
|
img = img.convert('RGBA')
|
||||||
|
elif img.mode == 'RGB':
|
||||||
|
# Convert RGB to RGBA (add alpha channel)
|
||||||
|
img = img.convert('RGBA')
|
||||||
|
elif img.mode != 'RGBA':
|
||||||
|
# For any other mode, convert to RGBA
|
||||||
|
img = img.convert('RGBA')
|
||||||
|
|
||||||
|
# Save the converted image
|
||||||
|
img.save(filepath, 'PNG')
|
||||||
|
|
||||||
|
logger.info(f"Successfully downloaded and converted logo for {team_name} -> {filepath.name}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Downloaded file for {team_name} is not a valid image or conversion failed: {e}")
|
||||||
|
try:
|
||||||
|
os.remove(filepath) # Remove invalid file
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"Failed to download logo for {team_name}: {e}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error downloading logo for {team_name}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def fetch_teams_data(self, league: str) -> Optional[Dict]:
|
||||||
|
"""Fetch team data from ESPN API for a specific league."""
|
||||||
|
api_url = self.API_ENDPOINTS.get(league)
|
||||||
|
if not api_url:
|
||||||
|
logger.error(f"No API endpoint configured for league: {league}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"Fetching team data for {league} from ESPN API...")
|
||||||
|
response = self.session.get(api_url, headers=self.headers, timeout=self.request_timeout)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
logger.info(f"Successfully fetched team data for {league}")
|
||||||
|
return data
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"Error fetching team data for {league}: {e}")
|
||||||
|
return None
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.error(f"Error parsing JSON response for {league}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def extract_teams_from_data(self, data: Dict, league: str) -> List[Dict[str, str]]:
|
||||||
|
"""Extract team information from ESPN API response."""
|
||||||
|
teams = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
sports = data.get('sports', [])
|
||||||
|
for sport in sports:
|
||||||
|
leagues_data = sport.get('leagues', [])
|
||||||
|
for league_data in leagues_data:
|
||||||
|
teams_data = league_data.get('teams', [])
|
||||||
|
|
||||||
|
for team_data in teams_data:
|
||||||
|
team_info = team_data.get('team', {})
|
||||||
|
|
||||||
|
abbreviation = team_info.get('abbreviation', '')
|
||||||
|
display_name = team_info.get('displayName', 'Unknown')
|
||||||
|
logos = team_info.get('logos', [])
|
||||||
|
|
||||||
|
if not abbreviation or not logos:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get the default logo (first one is usually default)
|
||||||
|
logo_url = logos[0].get('href', '')
|
||||||
|
if not logo_url:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# For NCAA football, try to determine if it's FCS or FBS
|
||||||
|
team_category = 'FBS' # Default
|
||||||
|
if league in ['ncaa_fb', 'ncaa_fb_all', 'fcs']:
|
||||||
|
# Check if this is an FCS team by looking at conference or other indicators
|
||||||
|
# ESPN API includes both FBS and FCS teams in the same endpoint
|
||||||
|
# We'll include all teams and let the user decide which ones to use
|
||||||
|
team_category = self._determine_ncaa_football_division(team_info, league_data)
|
||||||
|
|
||||||
|
teams.append({
|
||||||
|
'abbreviation': abbreviation,
|
||||||
|
'display_name': display_name,
|
||||||
|
'logo_url': logo_url,
|
||||||
|
'league': league,
|
||||||
|
'category': team_category,
|
||||||
|
'conference': league_data.get('name', 'Unknown')
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info(f"Extracted {len(teams)} teams for {league}")
|
||||||
|
return teams
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error extracting teams for {league}: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _determine_ncaa_football_division(self, team_info: Dict, league_data: Dict) -> str:
|
||||||
|
"""Determine if an NCAA football team is FBS or FCS based on conference and other indicators."""
|
||||||
|
conference_name = league_data.get('name', '').lower()
|
||||||
|
|
||||||
|
# FBS Conferences (more comprehensive list)
|
||||||
|
fbs_conferences = {
|
||||||
|
'acc', 'american athletic', 'big 12', 'big ten', 'conference usa', 'c-usa',
|
||||||
|
'mid-american', 'mac', 'mountain west', 'pac-12', 'pac-10', 'sec',
|
||||||
|
'sun belt', 'independents', 'big east'
|
||||||
|
}
|
||||||
|
|
||||||
|
# FCS Conferences (more comprehensive list)
|
||||||
|
fcs_conferences = {
|
||||||
|
'big sky', 'big south', 'colonial athletic', 'caa', 'ivy league',
|
||||||
|
'meac', 'missouri valley', 'mvfc', 'northeast', 'nec',
|
||||||
|
'ohio valley', 'ovc', 'patriot league', 'pioneer football',
|
||||||
|
'southland', 'southern', 'southwestern athletic', 'swac',
|
||||||
|
'western athletic', 'wac', 'ncaa division i-aa'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Also check for specific team indicators
|
||||||
|
team_abbreviation = team_info.get('abbreviation', '').upper()
|
||||||
|
|
||||||
|
# Known FBS teams that might be misclassified
|
||||||
|
known_fbs_teams = {
|
||||||
|
'ASU', 'ARIZ', 'ARK', 'AUB', 'BOIS', 'CSU', 'FLA', 'HAW', 'IDHO', 'USA'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if it's a known FBS team first
|
||||||
|
if team_abbreviation in known_fbs_teams:
|
||||||
|
return 'FBS'
|
||||||
|
|
||||||
|
# Check conference names
|
||||||
|
if any(fbs_conf in conference_name for fbs_conf in fbs_conferences):
|
||||||
|
return 'FBS'
|
||||||
|
elif any(fcs_conf in conference_name for fcs_conf in fcs_conferences):
|
||||||
|
return 'FCS'
|
||||||
|
|
||||||
|
# If conference is just "NCAA - Football", we need to use other indicators
|
||||||
|
if conference_name == 'ncaa - football':
|
||||||
|
# Check team name for indicators of FCS (smaller schools, Division II/III)
|
||||||
|
team_name = team_info.get('displayName', '').lower()
|
||||||
|
fcs_indicators = ['college', 'university', 'state', 'tech', 'community']
|
||||||
|
|
||||||
|
# If it has typical FCS naming patterns and isn't a known FBS team
|
||||||
|
if any(indicator in team_name for indicator in fcs_indicators):
|
||||||
|
return 'FCS'
|
||||||
|
else:
|
||||||
|
return 'FBS'
|
||||||
|
|
||||||
|
# Default to FBS for unknown conferences
|
||||||
|
return 'FBS'
|
||||||
|
|
||||||
|
def _get_team_name_variations(self, abbreviation: str) -> List[str]:
|
||||||
|
"""Generate common variations of a team abbreviation for matching."""
|
||||||
|
variations = set()
|
||||||
|
abbr = abbreviation.upper()
|
||||||
|
variations.add(abbr)
|
||||||
|
|
||||||
|
# Add normalized version
|
||||||
|
variations.add(self.normalize_abbreviation(abbr))
|
||||||
|
|
||||||
|
# Common substitutions
|
||||||
|
substitutions = {
|
||||||
|
'&': ['AND', 'A'],
|
||||||
|
'A&M': ['TAMU', 'TA&M', 'TEXASAM'],
|
||||||
|
'STATE': ['ST', 'ST.'],
|
||||||
|
'UNIVERSITY': ['U', 'UNIV'],
|
||||||
|
'COLLEGE': ['C', 'COL'],
|
||||||
|
'TECHNICAL': ['TECH', 'T'],
|
||||||
|
'NORTHERN': ['NORTH', 'N'],
|
||||||
|
'SOUTHERN': ['SOUTH', 'S'],
|
||||||
|
'EASTERN': ['EAST', 'E'],
|
||||||
|
'WESTERN': ['WEST', 'W']
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply substitutions
|
||||||
|
for original, replacements in substitutions.items():
|
||||||
|
if original in abbr:
|
||||||
|
for replacement in replacements:
|
||||||
|
variations.add(abbr.replace(original, replacement))
|
||||||
|
variations.add(abbr.replace(original, '')) # Remove the word entirely
|
||||||
|
|
||||||
|
# Add common abbreviations for Texas A&M
|
||||||
|
if 'A&M' in abbr or 'TAMU' in abbr:
|
||||||
|
variations.update(['TAMU', 'TA&M', 'TEXASAM', 'TEXAS_A&M', 'TEXAS_AM'])
|
||||||
|
|
||||||
|
return list(variations)
|
||||||
|
|
||||||
|
def download_missing_logos_for_league(self, league: str, force_download: bool = False) -> Tuple[int, int]:
|
||||||
|
"""Download missing logos for a specific league."""
|
||||||
|
logger.info(f"Starting logo download for league: {league}")
|
||||||
|
|
||||||
|
# Get logo directory
|
||||||
|
logo_dir = self.get_logo_directory(league)
|
||||||
|
if not self.ensure_logo_directory(logo_dir):
|
||||||
|
logger.error(f"Failed to create logo directory for {league}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# Fetch team data
|
||||||
|
data = self.fetch_teams_data(league)
|
||||||
|
if not data:
|
||||||
|
logger.error(f"Failed to fetch team data for {league}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# Extract teams
|
||||||
|
teams = self.extract_teams_from_data(data, league)
|
||||||
|
if not teams:
|
||||||
|
logger.warning(f"No teams found for {league}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# Download missing logos
|
||||||
|
downloaded_count = 0
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
for team in teams:
|
||||||
|
abbreviation = team['abbreviation']
|
||||||
|
display_name = team['display_name']
|
||||||
|
logo_url = team['logo_url']
|
||||||
|
|
||||||
|
# Create filename
|
||||||
|
filename = f"{self.normalize_abbreviation(abbreviation)}.png"
|
||||||
|
filepath = Path(logo_dir) / filename
|
||||||
|
|
||||||
|
# Skip if already exists and not forcing download
|
||||||
|
if filepath.exists() and not force_download:
|
||||||
|
logger.debug(f"Skipping {display_name}: {filename} already exists")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Download logo
|
||||||
|
if self.download_logo(logo_url, filepath, display_name):
|
||||||
|
downloaded_count += 1
|
||||||
|
else:
|
||||||
|
failed_count += 1
|
||||||
|
|
||||||
|
# Small delay to be respectful to the API
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
logger.info(f"Logo download complete for {league}: {downloaded_count} downloaded, {failed_count} failed")
|
||||||
|
return downloaded_count, failed_count
|
||||||
|
|
||||||
|
def download_all_ncaa_football_logos(self, include_fcs: bool = True, force_download: bool = False) -> Tuple[int, int]:
|
||||||
|
"""Download all NCAA football team logos including FCS teams."""
|
||||||
|
logger.info(f"Starting comprehensive NCAA football logo download (FCS: {include_fcs})")
|
||||||
|
|
||||||
|
# Use the comprehensive NCAA football endpoint
|
||||||
|
league = 'ncaa_fb_all'
|
||||||
|
logo_dir = self.get_logo_directory(league)
|
||||||
|
if not self.ensure_logo_directory(logo_dir):
|
||||||
|
logger.error(f"Failed to create logo directory for {league}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# Fetch team data
|
||||||
|
data = self.fetch_teams_data(league)
|
||||||
|
if not data:
|
||||||
|
logger.error(f"Failed to fetch team data for {league}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# Extract teams
|
||||||
|
teams = self.extract_teams_from_data(data, league)
|
||||||
|
if not teams:
|
||||||
|
logger.warning(f"No teams found for {league}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# Filter teams based on FCS inclusion
|
||||||
|
if not include_fcs:
|
||||||
|
teams = [team for team in teams if team.get('category') == 'FBS']
|
||||||
|
logger.info(f"Filtered to FBS teams only: {len(teams)} teams")
|
||||||
|
|
||||||
|
# Download missing logos
|
||||||
|
downloaded_count = 0
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
for team in teams:
|
||||||
|
abbreviation = team['abbreviation']
|
||||||
|
display_name = team['display_name']
|
||||||
|
logo_url = team['logo_url']
|
||||||
|
category = team.get('category', 'Unknown')
|
||||||
|
conference = team.get('conference', 'Unknown')
|
||||||
|
|
||||||
|
# Create filename
|
||||||
|
filename = f"{self.normalize_abbreviation(abbreviation)}.png"
|
||||||
|
filepath = Path(logo_dir) / filename
|
||||||
|
|
||||||
|
# Skip if already exists and not forcing download
|
||||||
|
if filepath.exists() and not force_download:
|
||||||
|
logger.debug(f"Skipping {display_name} ({category}, {conference}): {filename} already exists")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Download logo
|
||||||
|
if self.download_logo(logo_url, filepath, display_name):
|
||||||
|
downloaded_count += 1
|
||||||
|
logger.info(f"Downloaded {display_name} ({category}, {conference}) -> {filename}")
|
||||||
|
else:
|
||||||
|
failed_count += 1
|
||||||
|
logger.warning(f"Failed to download {display_name} ({category}, {conference})")
|
||||||
|
|
||||||
|
# Small delay to be respectful to the API
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
logger.info(f"Comprehensive NCAA football logo download complete: {downloaded_count} downloaded, {failed_count} failed")
|
||||||
|
return downloaded_count, failed_count
|
||||||
|
|
||||||
|
def download_missing_logo_for_team(self, team_abbreviation: str, league: str, team_name: str = None) -> bool:
|
||||||
|
"""Download a specific team's logo if it's missing."""
|
||||||
|
logo_dir = self.get_logo_directory(league)
|
||||||
|
if not self.ensure_logo_directory(logo_dir):
|
||||||
|
return False
|
||||||
|
|
||||||
|
filename = f"{self.normalize_abbreviation(team_abbreviation)}.png"
|
||||||
|
filepath = Path(logo_dir) / filename
|
||||||
|
|
||||||
|
# Return True if logo already exists
|
||||||
|
if filepath.exists():
|
||||||
|
logger.debug(f"Logo already exists for {team_abbreviation}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Fetch team data to find the logo URL
|
||||||
|
data = self.fetch_teams_data(league)
|
||||||
|
if not data:
|
||||||
|
return False
|
||||||
|
|
||||||
|
teams = self.extract_teams_from_data(data, league)
|
||||||
|
|
||||||
|
# Find the specific team with improved matching
|
||||||
|
target_team = None
|
||||||
|
normalized_search = self.normalize_abbreviation(team_abbreviation)
|
||||||
|
|
||||||
|
# First try exact match
|
||||||
|
for team in teams:
|
||||||
|
if team['abbreviation'].upper() == team_abbreviation.upper():
|
||||||
|
target_team = team
|
||||||
|
break
|
||||||
|
|
||||||
|
# If not found, try normalized match
|
||||||
|
if not target_team:
|
||||||
|
for team in teams:
|
||||||
|
normalized_team_abbr = self.normalize_abbreviation(team['abbreviation'])
|
||||||
|
if normalized_team_abbr == normalized_search:
|
||||||
|
target_team = team
|
||||||
|
break
|
||||||
|
|
||||||
|
# If still not found, try partial matching for common variations
|
||||||
|
if not target_team:
|
||||||
|
search_variations = self._get_team_name_variations(team_abbreviation)
|
||||||
|
for team in teams:
|
||||||
|
team_variations = self._get_team_name_variations(team['abbreviation'])
|
||||||
|
if any(var in team_variations for var in search_variations):
|
||||||
|
target_team = team
|
||||||
|
logger.info(f"Found team {team_abbreviation} as {team['abbreviation']} ({team['display_name']})")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_team:
|
||||||
|
logger.warning(f"Team {team_abbreviation} not found in {league} data")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Download the logo
|
||||||
|
success = self.download_logo(target_team['logo_url'], filepath, target_team['display_name'])
|
||||||
|
if success:
|
||||||
|
time.sleep(0.1) # Small delay
|
||||||
|
return success
|
||||||
|
|
||||||
|
def download_all_missing_logos(self, leagues: List[str] = None, force_download: bool = False) -> Dict[str, Tuple[int, int]]:
|
||||||
|
"""Download missing logos for all specified leagues."""
|
||||||
|
if leagues is None:
|
||||||
|
leagues = list(self.API_ENDPOINTS.keys())
|
||||||
|
|
||||||
|
results = {}
|
||||||
|
total_downloaded = 0
|
||||||
|
total_failed = 0
|
||||||
|
|
||||||
|
for league in leagues:
|
||||||
|
if league not in self.API_ENDPOINTS:
|
||||||
|
logger.warning(f"Skipping unknown league: {league}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
downloaded, failed = self.download_missing_logos_for_league(league, force_download)
|
||||||
|
results[league] = (downloaded, failed)
|
||||||
|
total_downloaded += downloaded
|
||||||
|
total_failed += failed
|
||||||
|
|
||||||
|
logger.info(f"Overall logo download results: {total_downloaded} downloaded, {total_failed} failed")
|
||||||
|
return results
|
||||||
|
|
||||||
|
def create_placeholder_logo(self, team_abbreviation: str, logo_dir: str, team_name: str = None) -> bool:
|
||||||
|
"""Create a placeholder logo when real logo cannot be downloaded."""
|
||||||
|
try:
|
||||||
|
# Ensure the logo directory exists
|
||||||
|
if not self.ensure_logo_directory(logo_dir):
|
||||||
|
logger.error(f"Failed to create logo directory: {logo_dir}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
filename = f"{self.normalize_abbreviation(team_abbreviation)}.png"
|
||||||
|
filepath = Path(logo_dir) / filename
|
||||||
|
|
||||||
|
# Check if we can write to the directory
|
||||||
|
try:
|
||||||
|
# Test write permissions by creating a temporary file
|
||||||
|
test_file = filepath.parent / "test_write.tmp"
|
||||||
|
test_file.touch()
|
||||||
|
test_file.unlink() # Remove the test file
|
||||||
|
except PermissionError:
|
||||||
|
logger.error(f"Permission denied: Cannot write to directory {logo_dir}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Directory access error for {logo_dir}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create a simple placeholder logo
|
||||||
|
logo = Image.new('RGBA', (64, 64), (100, 100, 100, 255)) # Gray background
|
||||||
|
draw = ImageDraw.Draw(logo)
|
||||||
|
|
||||||
|
# Try to load a font, fallback to default
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype("assets/fonts/PressStart2P-Regular.ttf", 12)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
except:
|
||||||
|
font = None
|
||||||
|
|
||||||
|
# Draw team abbreviation
|
||||||
|
text = team_abbreviation
|
||||||
|
if font:
|
||||||
|
# Center the text
|
||||||
|
bbox = draw.textbbox((0, 0), text, font=font)
|
||||||
|
text_width = bbox[2] - bbox[0]
|
||||||
|
text_height = bbox[3] - bbox[1]
|
||||||
|
x = (64 - text_width) // 2
|
||||||
|
y = (64 - text_height) // 2
|
||||||
|
draw.text((x, y), text, font=font, fill=(255, 255, 255, 255))
|
||||||
|
else:
|
||||||
|
# Fallback without font
|
||||||
|
draw.text((16, 24), text, fill=(255, 255, 255, 255))
|
||||||
|
|
||||||
|
logo.save(filepath)
|
||||||
|
logger.info(f"Created placeholder logo for {team_abbreviation} at {filepath}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to create placeholder logo for {team_abbreviation}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def convert_image_to_rgba(self, filepath: Path) -> bool:
|
||||||
|
"""Convert an image file to RGBA format to avoid PIL warnings."""
|
||||||
|
try:
|
||||||
|
with Image.open(filepath) as img:
|
||||||
|
if img.mode != 'RGBA':
|
||||||
|
# Convert to RGBA
|
||||||
|
converted_img = img.convert('RGBA')
|
||||||
|
converted_img.save(filepath, 'PNG')
|
||||||
|
logger.debug(f"Converted {filepath.name} from {img.mode} to RGBA")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.debug(f"{filepath.name} is already in RGBA format")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to convert {filepath.name} to RGBA: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def convert_all_logos_to_rgba(self, league: str) -> Tuple[int, int]:
|
||||||
|
"""Convert all logos in a league directory to RGBA format."""
|
||||||
|
logo_dir = Path(self.get_logo_directory(league))
|
||||||
|
if not logo_dir.exists():
|
||||||
|
logger.warning(f"Logo directory does not exist: {logo_dir}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
converted_count = 0
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
for logo_file in logo_dir.glob("*.png"):
|
||||||
|
if self.convert_image_to_rgba(logo_file):
|
||||||
|
converted_count += 1
|
||||||
|
else:
|
||||||
|
failed_count += 1
|
||||||
|
|
||||||
|
logger.info(f"Converted {converted_count} logos to RGBA format for {league}, {failed_count} failed")
|
||||||
|
return converted_count, failed_count
|
||||||
|
|
||||||
|
|
||||||
|
# Convenience function for easy integration
|
||||||
|
def download_missing_logo(team_abbreviation: str, league: str, team_name: str = None, create_placeholder: bool = True) -> bool:
|
||||||
|
"""
|
||||||
|
Convenience function to download a missing team logo.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
team_abbreviation: Team abbreviation (e.g., 'UGA', 'BAMA', 'TA&M')
|
||||||
|
league: League identifier (e.g., 'ncaa_fb', 'nfl')
|
||||||
|
team_name: Optional team name for logging
|
||||||
|
create_placeholder: Whether to create a placeholder if download fails
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if logo exists or was successfully downloaded, False otherwise
|
||||||
|
"""
|
||||||
|
downloader = LogoDownloader()
|
||||||
|
|
||||||
|
# Check if logo already exists
|
||||||
|
logo_dir = downloader.get_logo_directory(league)
|
||||||
|
filename = f"{downloader.normalize_abbreviation(team_abbreviation)}.png"
|
||||||
|
filepath = Path(logo_dir) / filename
|
||||||
|
|
||||||
|
if filepath.exists():
|
||||||
|
logger.debug(f"Logo already exists for {team_abbreviation} ({league})")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Try to download the real logo first
|
||||||
|
logger.info(f"Attempting to download logo for {team_abbreviation} ({team_name or 'Unknown'}) from {league}")
|
||||||
|
success = downloader.download_missing_logo_for_team(team_abbreviation, league, team_name)
|
||||||
|
|
||||||
|
if not success and create_placeholder:
|
||||||
|
logger.info(f"Creating placeholder logo for {team_abbreviation} ({team_name or 'Unknown'})")
|
||||||
|
# Create placeholder as fallback
|
||||||
|
success = downloader.create_placeholder_logo(team_abbreviation, logo_dir, team_name)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
logger.info(f"Successfully handled logo for {team_abbreviation} ({team_name or 'Unknown'})")
|
||||||
|
else:
|
||||||
|
logger.warning(f"Failed to download or create logo for {team_abbreviation} ({team_name or 'Unknown'})")
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
|
def download_all_logos_for_league(league: str, force_download: bool = False) -> Tuple[int, int]:
|
||||||
|
"""
|
||||||
|
Convenience function to download all missing logos for a league.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
league: League identifier (e.g., 'ncaa_fb', 'nfl')
|
||||||
|
force_download: Whether to re-download existing logos
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (downloaded_count, failed_count)
|
||||||
|
"""
|
||||||
|
downloader = LogoDownloader()
|
||||||
|
return downloader.download_missing_logos_for_league(league, force_download)
|
||||||
@@ -29,12 +29,14 @@ class BaseMLBManager:
|
|||||||
def __init__(self, config: Dict[str, Any], display_manager, cache_manager: CacheManager):
|
def __init__(self, config: Dict[str, Any], display_manager, cache_manager: CacheManager):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.display_manager = display_manager
|
self.display_manager = display_manager
|
||||||
|
# Store reference to config instead of creating new ConfigManager
|
||||||
|
self.config_manager = None # Not used in this class
|
||||||
self.mlb_config = config.get('mlb', {})
|
self.mlb_config = config.get('mlb', {})
|
||||||
self.show_odds = self.mlb_config.get("show_odds", False)
|
self.show_odds = self.mlb_config.get("show_odds", False)
|
||||||
self.favorite_teams = self.mlb_config.get('favorite_teams', [])
|
self.favorite_teams = self.mlb_config.get('favorite_teams', [])
|
||||||
self.show_records = self.mlb_config.get('show_records', False)
|
self.show_records = self.mlb_config.get('show_records', False)
|
||||||
self.cache_manager = cache_manager
|
self.cache_manager = cache_manager
|
||||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
self.odds_manager = OddsManager(self.cache_manager, self.config_manager)
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Logo handling
|
# Logo handling
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import requests
|
import requests
|
||||||
from typing import Union
|
from typing import Union, Dict, Any, Optional
|
||||||
from PIL import Image, ImageEnhance
|
from PIL import Image, ImageEnhance
|
||||||
import queue # Added import
|
import queue # Added import
|
||||||
|
|
||||||
@@ -15,6 +15,14 @@ from .spotify_client import SpotifyClient
|
|||||||
from .ytm_client import YTMClient
|
from .ytm_client import YTMClient
|
||||||
# Removed: import config
|
# Removed: import config
|
||||||
|
|
||||||
|
# Import the API counter function from web interface
|
||||||
|
try:
|
||||||
|
from web_interface_v2 import increment_api_counter
|
||||||
|
except ImportError:
|
||||||
|
# Fallback if web interface is not available
|
||||||
|
def increment_api_counter(kind: str, count: int = 1):
|
||||||
|
pass
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -313,6 +321,10 @@ class MusicManager:
|
|||||||
try:
|
try:
|
||||||
response = requests.get(url, timeout=5) # 5-second timeout for image download
|
response = requests.get(url, timeout=5) # 5-second timeout for image download
|
||||||
response.raise_for_status() # Raise an exception for bad status codes
|
response.raise_for_status() # Raise an exception for bad status codes
|
||||||
|
|
||||||
|
# Increment API counter for music data
|
||||||
|
increment_api_counter('music', 1)
|
||||||
|
|
||||||
img_data = BytesIO(response.content)
|
img_data = BytesIO(response.content)
|
||||||
img = Image.open(img_data)
|
img = Image.open(img_data)
|
||||||
|
|
||||||
|
|||||||
@@ -43,10 +43,11 @@ class BaseNBAManager:
|
|||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||||
self.display_manager = display_manager
|
self.display_manager = display_manager
|
||||||
self.config_manager = ConfigManager()
|
# Store reference to config instead of creating new ConfigManager
|
||||||
|
self.config_manager = None # Not used in this class
|
||||||
self.config = config
|
self.config = config
|
||||||
self.cache_manager = cache_manager
|
self.cache_manager = cache_manager
|
||||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.nba_config = config.get("nba_scoreboard", {})
|
self.nba_config = config.get("nba_scoreboard", {})
|
||||||
self.is_enabled = self.nba_config.get("enabled", False)
|
self.is_enabled = self.nba_config.get("enabled", False)
|
||||||
@@ -63,13 +64,9 @@ class BaseNBAManager:
|
|||||||
# Set logging level to INFO to reduce noise
|
# Set logging level to INFO to reduce noise
|
||||||
self.logger.setLevel(logging.INFO)
|
self.logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
# Get display dimensions from config
|
# Get display dimensions from matrix
|
||||||
display_config = config.get("display", {})
|
self.display_width = self.display_manager.matrix.width
|
||||||
hardware_config = display_config.get("hardware", {})
|
self.display_height = self.display_manager.matrix.height
|
||||||
cols = hardware_config.get("cols", 64)
|
|
||||||
chain = hardware_config.get("chain_length", 1)
|
|
||||||
self.display_width = int(cols * chain)
|
|
||||||
self.display_height = hardware_config.get("rows", 32)
|
|
||||||
|
|
||||||
# Cache for loaded logos
|
# Cache for loaded logos
|
||||||
self._logo_cache = {}
|
self._logo_cache = {}
|
||||||
@@ -79,7 +76,8 @@ class BaseNBAManager:
|
|||||||
|
|
||||||
def _get_timezone(self):
|
def _get_timezone(self):
|
||||||
try:
|
try:
|
||||||
return pytz.timezone(self.config_manager.get_timezone())
|
timezone_str = self.config.get('timezone', 'UTC')
|
||||||
|
return pytz.timezone(timezone_str)
|
||||||
except pytz.UnknownTimeZoneError:
|
except pytz.UnknownTimeZoneError:
|
||||||
return pytz.utc
|
return pytz.utc
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ class BaseNCAABaseballManager:
|
|||||||
def __init__(self, config: Dict[str, Any], display_manager, cache_manager: CacheManager):
|
def __init__(self, config: Dict[str, Any], display_manager, cache_manager: CacheManager):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.display_manager = display_manager
|
self.display_manager = display_manager
|
||||||
|
# Store reference to config instead of creating new ConfigManager
|
||||||
|
self.config_manager = None # Not used in this class
|
||||||
self.ncaa_baseball_config = config.get('ncaa_baseball_scoreboard', {})
|
self.ncaa_baseball_config = config.get('ncaa_baseball_scoreboard', {})
|
||||||
self.show_odds = self.ncaa_baseball_config.get('show_odds', False)
|
self.show_odds = self.ncaa_baseball_config.get('show_odds', False)
|
||||||
self.show_records = self.ncaa_baseball_config.get('show_records', False)
|
self.show_records = self.ncaa_baseball_config.get('show_records', False)
|
||||||
self.favorite_teams = self.ncaa_baseball_config.get('favorite_teams', [])
|
self.favorite_teams = self.ncaa_baseball_config.get('favorite_teams', [])
|
||||||
self.cache_manager = cache_manager
|
self.cache_manager = cache_manager
|
||||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
self.odds_manager = OddsManager(self.cache_manager, self.config_manager)
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.logger.setLevel(logging.DEBUG) # Set logger level to DEBUG
|
self.logger.setLevel(logging.DEBUG) # Set logger level to DEBUG
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from src.display_manager import DisplayManager
|
|||||||
from src.cache_manager import CacheManager # Keep CacheManager import
|
from src.cache_manager import CacheManager # Keep CacheManager import
|
||||||
from src.config_manager import ConfigManager
|
from src.config_manager import ConfigManager
|
||||||
from src.odds_manager import OddsManager
|
from src.odds_manager import OddsManager
|
||||||
|
from src.logo_downloader import download_missing_logo
|
||||||
import pytz
|
import pytz
|
||||||
from requests.adapters import HTTPAdapter
|
from requests.adapters import HTTPAdapter
|
||||||
from urllib3.util.retry import Retry
|
from urllib3.util.retry import Retry
|
||||||
@@ -53,6 +54,7 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
self.logo_dir = self.ncaa_fb_config.get("logo_dir", "assets/sports/ncaa_fbs_logos") # Changed logo dir
|
self.logo_dir = self.ncaa_fb_config.get("logo_dir", "assets/sports/ncaa_fbs_logos") # Changed logo dir
|
||||||
self.update_interval = self.ncaa_fb_config.get("update_interval_seconds", 60)
|
self.update_interval = self.ncaa_fb_config.get("update_interval_seconds", 60)
|
||||||
self.show_records = self.ncaa_fb_config.get('show_records', False)
|
self.show_records = self.ncaa_fb_config.get('show_records', False)
|
||||||
|
self.show_ranking = self.ncaa_fb_config.get('show_ranking', False)
|
||||||
self.season_cache_duration = self.ncaa_fb_config.get("season_cache_duration_seconds", 86400) # 24 hours default
|
self.season_cache_duration = self.ncaa_fb_config.get("season_cache_duration_seconds", 86400) # 24 hours default
|
||||||
# Number of games to show (instead of time-based windows)
|
# Number of games to show (instead of time-based windows)
|
||||||
self.recent_games_to_show = self.ncaa_fb_config.get("recent_games_to_show", 5) # Show last 5 games
|
self.recent_games_to_show = self.ncaa_fb_config.get("recent_games_to_show", 5) # Show last 5 games
|
||||||
@@ -91,22 +93,66 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
|
|
||||||
self.logger.setLevel(logging.INFO)
|
self.logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
display_config = config.get("display", {})
|
self.display_width = self.display_manager.matrix.width
|
||||||
hardware_config = display_config.get("hardware", {})
|
self.display_height = self.display_manager.matrix.height
|
||||||
cols = hardware_config.get("cols", 64)
|
|
||||||
chain = hardware_config.get("chain_length", 1)
|
|
||||||
self.display_width = int(cols * chain)
|
|
||||||
self.display_height = hardware_config.get("rows", 32)
|
|
||||||
|
|
||||||
self._logo_cache = {}
|
self._logo_cache = {}
|
||||||
|
|
||||||
|
# Initialize team rankings cache
|
||||||
|
self._team_rankings_cache = {}
|
||||||
|
self._rankings_cache_timestamp = 0
|
||||||
|
self._rankings_cache_duration = 3600 # Cache rankings for 1 hour
|
||||||
|
|
||||||
self.logger.info(f"Initialized NCAAFB manager with display dimensions: {self.display_width}x{self.display_height}")
|
self.logger.info(f"Initialized NCAAFB manager with display dimensions: {self.display_width}x{self.display_height}")
|
||||||
self.logger.info(f"Logo directory: {self.logo_dir}")
|
self.logger.info(f"Logo directory: {self.logo_dir}")
|
||||||
self.logger.info(f"Display modes - Recent: {self.recent_enabled}, Upcoming: {self.upcoming_enabled}, Live: {self.live_enabled}")
|
self.logger.info(f"Display modes - Recent: {self.recent_enabled}, Upcoming: {self.upcoming_enabled}, Live: {self.live_enabled}")
|
||||||
|
|
||||||
|
def _fetch_team_rankings(self) -> Dict[str, int]:
|
||||||
|
"""Fetch current team rankings from ESPN API."""
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
# Check if we have cached rankings that are still valid
|
||||||
|
if (self._team_rankings_cache and
|
||||||
|
current_time - self._rankings_cache_timestamp < self._rankings_cache_duration):
|
||||||
|
return self._team_rankings_cache
|
||||||
|
|
||||||
|
try:
|
||||||
|
rankings_url = "https://site.api.espn.com/apis/site/v2/sports/football/college-football/rankings"
|
||||||
|
response = self.session.get(rankings_url, headers=self.headers, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
rankings = {}
|
||||||
|
rankings_data = data.get('rankings', [])
|
||||||
|
|
||||||
|
if rankings_data:
|
||||||
|
# Use the first ranking (usually AP Top 25)
|
||||||
|
first_ranking = rankings_data[0]
|
||||||
|
teams = first_ranking.get('ranks', [])
|
||||||
|
|
||||||
|
for team_data in teams:
|
||||||
|
team_info = team_data.get('team', {})
|
||||||
|
team_abbr = team_info.get('abbreviation', '')
|
||||||
|
current_rank = team_data.get('current', 0)
|
||||||
|
|
||||||
|
if team_abbr and current_rank > 0:
|
||||||
|
rankings[team_abbr] = current_rank
|
||||||
|
|
||||||
|
# Cache the results
|
||||||
|
self._team_rankings_cache = rankings
|
||||||
|
self._rankings_cache_timestamp = current_time
|
||||||
|
|
||||||
|
self.logger.debug(f"Fetched rankings for {len(rankings)} teams")
|
||||||
|
return rankings
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error fetching team rankings: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
def _get_timezone(self):
|
def _get_timezone(self):
|
||||||
try:
|
try:
|
||||||
return pytz.timezone(self.config_manager.get_timezone())
|
timezone_str = self.config.get('timezone', 'UTC')
|
||||||
|
return pytz.timezone(timezone_str)
|
||||||
except pytz.UnknownTimeZoneError:
|
except pytz.UnknownTimeZoneError:
|
||||||
return pytz.utc
|
return pytz.utc
|
||||||
|
|
||||||
@@ -313,39 +359,8 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
draw.text((x + dx, y + dy), text, font=font, fill=outline_color)
|
draw.text((x + dx, y + dy), text, font=font, fill=outline_color)
|
||||||
draw.text((x, y), text, font=font, fill=fill)
|
draw.text((x, y), text, font=font, fill=fill)
|
||||||
|
|
||||||
def _download_team_logo(self, team_id):
|
def _load_and_resize_logo(self, team_abbrev: str, team_name: str = None) -> Optional[Image.Image]:
|
||||||
# Base API URL with placeholder for team ID
|
"""Load and resize a team logo, with caching and automatic download if missing."""
|
||||||
API_URL_TEMPLATE = "http://site.api.espn.com/apis/site/v2/sports/football/college-football/teams/{}"
|
|
||||||
|
|
||||||
url = API_URL_TEMPLATE.format(team_id)
|
|
||||||
response = requests.get(url)
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
|
||||||
print(f"Failed to fetch data for team ID {team_id}. Status code: {response.status_code}")
|
|
||||||
return
|
|
||||||
|
|
||||||
data = response.json()
|
|
||||||
team = data.get("team", {})
|
|
||||||
logos = team.get("logos", [])
|
|
||||||
|
|
||||||
if logos:
|
|
||||||
logo_url = logos[0].get("href")
|
|
||||||
if logo_url:
|
|
||||||
try:
|
|
||||||
img_data = requests.get(logo_url).content
|
|
||||||
file_path = f"{self.logo_dir}/{team_id}.png"
|
|
||||||
with open(file_path, "wb") as f:
|
|
||||||
f.write(img_data)
|
|
||||||
print(f"Saved logo for {team_id} as {file_path}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error downloading logo for {team_id}: {e}")
|
|
||||||
else:
|
|
||||||
print(f"No logo URL found for team ID {team_id}")
|
|
||||||
else:
|
|
||||||
print(f"No logos found for team ID {team_id}")
|
|
||||||
|
|
||||||
def _load_and_resize_logo(self, team_abbrev: str) -> Optional[Image.Image]:
|
|
||||||
"""Load and resize a team logo, with caching."""
|
|
||||||
if team_abbrev in self._logo_cache:
|
if team_abbrev in self._logo_cache:
|
||||||
return self._logo_cache[team_abbrev]
|
return self._logo_cache[team_abbrev]
|
||||||
|
|
||||||
@@ -353,14 +368,16 @@ class BaseNCAAFBManager: # Renamed class
|
|||||||
self.logger.debug(f"Logo path: {logo_path}")
|
self.logger.debug(f"Logo path: {logo_path}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Try to download team logo
|
# Try to download missing logo first
|
||||||
if not os.path.exists(logo_path):
|
if not os.path.exists(logo_path):
|
||||||
self.logger.warning(f"Logo not found for {team_abbrev} at {logo_path}. Attempting to download.")
|
self.logger.info(f"Logo not found for {team_abbrev} at {logo_path}. Attempting to download.")
|
||||||
self._download_team_logo(team_abbrev)
|
|
||||||
|
|
||||||
# Check to make sure logo was able to be downloaded and saved. If not, create a placeholder.
|
# Try to download the logo from ESPN API
|
||||||
if not os.path.exists(logo_path):
|
success = download_missing_logo(team_abbrev, 'ncaa_fb', team_name)
|
||||||
self.logger.warning(f"Error occured donwloading logo for {team_abbrev} at {logo_path}. Creating placeholder.")
|
|
||||||
|
if not success:
|
||||||
|
# Create placeholder if download fails
|
||||||
|
self.logger.warning(f"Failed to download logo for {team_abbrev}. Creating placeholder.")
|
||||||
os.makedirs(os.path.dirname(logo_path), exist_ok=True)
|
os.makedirs(os.path.dirname(logo_path), exist_ok=True)
|
||||||
logo = Image.new('RGBA', (32, 32), (200, 200, 200, 255)) # Gray placeholder
|
logo = Image.new('RGBA', (32, 32), (200, 200, 200, 255)) # Gray placeholder
|
||||||
draw = ImageDraw.Draw(logo)
|
draw = ImageDraw.Draw(logo)
|
||||||
@@ -775,8 +792,8 @@ class NCAAFBLiveManager(BaseNCAAFBManager): # Renamed class
|
|||||||
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
||||||
draw_overlay = ImageDraw.Draw(overlay) # Draw text elements on overlay first
|
draw_overlay = ImageDraw.Draw(overlay) # Draw text elements on overlay first
|
||||||
|
|
||||||
home_logo = self._load_and_resize_logo(game["home_abbr"])
|
home_logo = self._load_and_resize_logo(game["home_abbr"], game.get("home_team_name"))
|
||||||
away_logo = self._load_and_resize_logo(game["away_abbr"])
|
away_logo = self._load_and_resize_logo(game["away_abbr"], game.get("away_team_name"))
|
||||||
|
|
||||||
if not home_logo or not away_logo:
|
if not home_logo or not away_logo:
|
||||||
self.logger.error(f"[NCAAFB] Failed to load logos for live game: {game.get('id')}") # Changed log prefix
|
self.logger.error(f"[NCAAFB] Failed to load logos for live game: {game.get('id')}") # Changed log prefix
|
||||||
@@ -1002,8 +1019,8 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class
|
|||||||
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
||||||
draw_overlay = ImageDraw.Draw(overlay)
|
draw_overlay = ImageDraw.Draw(overlay)
|
||||||
|
|
||||||
home_logo = self._load_and_resize_logo(game["home_abbr"])
|
home_logo = self._load_and_resize_logo(game["home_abbr"], game.get("home_team_name"))
|
||||||
away_logo = self._load_and_resize_logo(game["away_abbr"])
|
away_logo = self._load_and_resize_logo(game["away_abbr"], game.get("away_team_name"))
|
||||||
|
|
||||||
if not home_logo or not away_logo:
|
if not home_logo or not away_logo:
|
||||||
self.logger.error(f"[NCAAFB Recent] Failed to load logos for game: {game.get('id')}") # Changed log prefix
|
self.logger.error(f"[NCAAFB Recent] Failed to load logos for game: {game.get('id')}") # Changed log prefix
|
||||||
@@ -1046,29 +1063,72 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class
|
|||||||
if 'odds' in game and game['odds']:
|
if 'odds' in game and game['odds']:
|
||||||
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height)
|
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height)
|
||||||
|
|
||||||
# Draw records if enabled
|
# Draw records or rankings if enabled
|
||||||
if self.show_records:
|
if self.show_records or self.show_ranking:
|
||||||
try:
|
try:
|
||||||
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
|
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
|
||||||
except IOError:
|
except IOError:
|
||||||
record_font = ImageFont.load_default()
|
record_font = ImageFont.load_default()
|
||||||
|
|
||||||
away_record = game.get('away_record', '')
|
# Get team abbreviations
|
||||||
home_record = game.get('home_record', '')
|
away_abbr = game.get('away_abbr', '')
|
||||||
|
home_abbr = game.get('home_abbr', '')
|
||||||
|
|
||||||
record_bbox = draw_overlay.textbbox((0,0), "0-0", font=record_font)
|
record_bbox = draw_overlay.textbbox((0,0), "0-0", font=record_font)
|
||||||
record_height = record_bbox[3] - record_bbox[1]
|
record_height = record_bbox[3] - record_bbox[1]
|
||||||
record_y = self.display_height - record_height
|
record_y = self.display_height - record_height
|
||||||
|
|
||||||
if away_record:
|
# Display away team info
|
||||||
away_record_x = 0
|
if away_abbr:
|
||||||
self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font)
|
if self.show_ranking:
|
||||||
|
# Show ranking if available
|
||||||
|
rankings = self._fetch_team_rankings()
|
||||||
|
away_rank = rankings.get(away_abbr, 0)
|
||||||
|
if away_rank > 0:
|
||||||
|
away_text = f"#{away_rank}"
|
||||||
|
elif self.show_records:
|
||||||
|
# Only show record if show_records is enabled
|
||||||
|
away_text = game.get('away_record', '')
|
||||||
|
else:
|
||||||
|
# Show nothing if show_records is false and team is unranked
|
||||||
|
away_text = ''
|
||||||
|
else:
|
||||||
|
# Show record only if show_records is enabled
|
||||||
|
if self.show_records:
|
||||||
|
away_text = game.get('away_record', '')
|
||||||
|
else:
|
||||||
|
away_text = ''
|
||||||
|
|
||||||
if home_record:
|
if away_text:
|
||||||
home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font)
|
away_record_x = 0
|
||||||
|
self._draw_text_with_outline(draw_overlay, away_text, (away_record_x, record_y), record_font)
|
||||||
|
|
||||||
|
# Display home team info
|
||||||
|
if home_abbr:
|
||||||
|
if self.show_ranking:
|
||||||
|
# Show ranking if available
|
||||||
|
rankings = self._fetch_team_rankings()
|
||||||
|
home_rank = rankings.get(home_abbr, 0)
|
||||||
|
if home_rank > 0:
|
||||||
|
home_text = f"#{home_rank}"
|
||||||
|
elif self.show_records:
|
||||||
|
# Only show record if show_records is enabled
|
||||||
|
home_text = game.get('home_record', '')
|
||||||
|
else:
|
||||||
|
# Show nothing if show_records is false and team is unranked
|
||||||
|
home_text = ''
|
||||||
|
else:
|
||||||
|
# Show record only if show_records is enabled
|
||||||
|
if self.show_records:
|
||||||
|
home_text = game.get('home_record', '')
|
||||||
|
else:
|
||||||
|
home_text = ''
|
||||||
|
|
||||||
|
if home_text:
|
||||||
|
home_record_bbox = draw_overlay.textbbox((0,0), home_text, font=record_font)
|
||||||
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
||||||
home_record_x = self.display_width - home_record_width
|
home_record_x = self.display_width - home_record_width
|
||||||
self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font)
|
self._draw_text_with_outline(draw_overlay, home_text, (home_record_x, record_y), record_font)
|
||||||
|
|
||||||
# Composite and display
|
# Composite and display
|
||||||
main_img = Image.alpha_composite(main_img, overlay)
|
main_img = Image.alpha_composite(main_img, overlay)
|
||||||
@@ -1234,8 +1294,8 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class
|
|||||||
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
||||||
draw_overlay = ImageDraw.Draw(overlay)
|
draw_overlay = ImageDraw.Draw(overlay)
|
||||||
|
|
||||||
home_logo = self._load_and_resize_logo(game["home_abbr"])
|
home_logo = self._load_and_resize_logo(game["home_abbr"], game.get("home_team_name"))
|
||||||
away_logo = self._load_and_resize_logo(game["away_abbr"])
|
away_logo = self._load_and_resize_logo(game["away_abbr"], game.get("away_team_name"))
|
||||||
|
|
||||||
if not home_logo or not away_logo:
|
if not home_logo or not away_logo:
|
||||||
self.logger.error(f"[NCAAFB Upcoming] Failed to load logos for game: {game.get('id')}") # Changed log prefix
|
self.logger.error(f"[NCAAFB Upcoming] Failed to load logos for game: {game.get('id')}") # Changed log prefix
|
||||||
@@ -1284,29 +1344,72 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class
|
|||||||
if 'odds' in game and game['odds']:
|
if 'odds' in game and game['odds']:
|
||||||
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height)
|
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height)
|
||||||
|
|
||||||
# Draw records if enabled
|
# Draw records or rankings if enabled
|
||||||
if self.show_records:
|
if self.show_records or self.show_ranking:
|
||||||
try:
|
try:
|
||||||
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
|
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
|
||||||
except IOError:
|
except IOError:
|
||||||
record_font = ImageFont.load_default()
|
record_font = ImageFont.load_default()
|
||||||
|
|
||||||
away_record = game.get('away_record', '')
|
# Get team abbreviations
|
||||||
home_record = game.get('home_record', '')
|
away_abbr = game.get('away_abbr', '')
|
||||||
|
home_abbr = game.get('home_abbr', '')
|
||||||
|
|
||||||
record_bbox = draw_overlay.textbbox((0,0), "0-0", font=record_font)
|
record_bbox = draw_overlay.textbbox((0,0), "0-0", font=record_font)
|
||||||
record_height = record_bbox[3] - record_bbox[1]
|
record_height = record_bbox[3] - record_bbox[1]
|
||||||
record_y = self.display_height - record_height
|
record_y = self.display_height - record_height
|
||||||
|
|
||||||
if away_record:
|
# Display away team info
|
||||||
away_record_x = 0
|
if away_abbr:
|
||||||
self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font)
|
if self.show_ranking:
|
||||||
|
# Show ranking if available
|
||||||
|
rankings = self._fetch_team_rankings()
|
||||||
|
away_rank = rankings.get(away_abbr, 0)
|
||||||
|
if away_rank > 0:
|
||||||
|
away_text = f"#{away_rank}"
|
||||||
|
elif self.show_records:
|
||||||
|
# Only show record if show_records is enabled
|
||||||
|
away_text = game.get('away_record', '')
|
||||||
|
else:
|
||||||
|
# Show nothing if show_records is false and team is unranked
|
||||||
|
away_text = ''
|
||||||
|
else:
|
||||||
|
# Show record only if show_records is enabled
|
||||||
|
if self.show_records:
|
||||||
|
away_text = game.get('away_record', '')
|
||||||
|
else:
|
||||||
|
away_text = ''
|
||||||
|
|
||||||
if home_record:
|
if away_text:
|
||||||
home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font)
|
away_record_x = 0
|
||||||
|
self._draw_text_with_outline(draw_overlay, away_text, (away_record_x, record_y), record_font)
|
||||||
|
|
||||||
|
# Display home team info
|
||||||
|
if home_abbr:
|
||||||
|
if self.show_ranking:
|
||||||
|
# Show ranking if available
|
||||||
|
rankings = self._fetch_team_rankings()
|
||||||
|
home_rank = rankings.get(home_abbr, 0)
|
||||||
|
if home_rank > 0:
|
||||||
|
home_text = f"#{home_rank}"
|
||||||
|
elif self.show_records:
|
||||||
|
# Only show record if show_records is enabled
|
||||||
|
home_text = game.get('home_record', '')
|
||||||
|
else:
|
||||||
|
# Show nothing if show_records is false and team is unranked
|
||||||
|
home_text = ''
|
||||||
|
else:
|
||||||
|
# Show record only if show_records is enabled
|
||||||
|
if self.show_records:
|
||||||
|
home_text = game.get('home_record', '')
|
||||||
|
else:
|
||||||
|
home_text = ''
|
||||||
|
|
||||||
|
if home_text:
|
||||||
|
home_record_bbox = draw_overlay.textbbox((0,0), home_text, font=record_font)
|
||||||
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
||||||
home_record_x = self.display_width - home_record_width
|
home_record_x = self.display_width - home_record_width
|
||||||
self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font)
|
self._draw_text_with_outline(draw_overlay, home_text, (home_record_x, record_y), record_font)
|
||||||
|
|
||||||
# Composite and display
|
# Composite and display
|
||||||
main_img = Image.alpha_composite(main_img, overlay)
|
main_img = Image.alpha_composite(main_img, overlay)
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ from src.config_manager import ConfigManager
|
|||||||
from src.odds_manager import OddsManager
|
from src.odds_manager import OddsManager
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
|
# Import the API counter function from web interface
|
||||||
|
try:
|
||||||
|
from web_interface_v2 import increment_api_counter
|
||||||
|
except ImportError:
|
||||||
|
# Fallback if web interface is not available
|
||||||
|
def increment_api_counter(kind: str, count: int = 1):
|
||||||
|
pass
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
ESPN_NCAAMB_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/basketball/mens-college-basketball/scoreboard"
|
ESPN_NCAAMB_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/basketball/mens-college-basketball/scoreboard"
|
||||||
|
|
||||||
@@ -35,10 +43,11 @@ class BaseNCAAMBasketballManager:
|
|||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||||
self.display_manager = display_manager
|
self.display_manager = display_manager
|
||||||
self.config_manager = ConfigManager()
|
# Store reference to config instead of creating new ConfigManager
|
||||||
|
self.config_manager = None # Not used in this class
|
||||||
self.config = config
|
self.config = config
|
||||||
self.cache_manager = cache_manager
|
self.cache_manager = cache_manager
|
||||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.ncaam_basketball_config = config.get("ncaam_basketball_scoreboard", {})
|
self.ncaam_basketball_config = config.get("ncaam_basketball_scoreboard", {})
|
||||||
self.is_enabled = self.ncaam_basketball_config.get("enabled", False)
|
self.is_enabled = self.ncaam_basketball_config.get("enabled", False)
|
||||||
@@ -55,13 +64,9 @@ class BaseNCAAMBasketballManager:
|
|||||||
# Set logging level to INFO to reduce noise
|
# Set logging level to INFO to reduce noise
|
||||||
self.logger.setLevel(logging.INFO)
|
self.logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
# Get display dimensions from config
|
# Get display dimensions from matrix
|
||||||
display_config = config.get("display", {})
|
self.display_width = self.display_manager.matrix.width
|
||||||
hardware_config = display_config.get("hardware", {})
|
self.display_height = self.display_manager.matrix.height
|
||||||
cols = hardware_config.get("cols", 64)
|
|
||||||
chain = hardware_config.get("chain_length", 1)
|
|
||||||
self.display_width = int(cols * chain)
|
|
||||||
self.display_height = hardware_config.get("rows", 32)
|
|
||||||
|
|
||||||
# Cache for loaded logos
|
# Cache for loaded logos
|
||||||
self._logo_cache = {}
|
self._logo_cache = {}
|
||||||
@@ -99,7 +104,8 @@ class BaseNCAAMBasketballManager:
|
|||||||
|
|
||||||
def _get_timezone(self):
|
def _get_timezone(self):
|
||||||
try:
|
try:
|
||||||
return pytz.timezone(self.config_manager.get_timezone())
|
timezone_str = self.config.get('timezone', 'UTC')
|
||||||
|
return pytz.timezone(timezone_str)
|
||||||
except pytz.UnknownTimeZoneError:
|
except pytz.UnknownTimeZoneError:
|
||||||
return pytz.utc
|
return pytz.utc
|
||||||
|
|
||||||
@@ -328,6 +334,9 @@ class BaseNCAAMBasketballManager:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
|
# Increment API counter for sports data
|
||||||
|
increment_api_counter('sports', 1)
|
||||||
|
|
||||||
if use_cache:
|
if use_cache:
|
||||||
self.cache_manager.set(cache_key, data)
|
self.cache_manager.set(cache_key, data)
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ logging.basicConfig(level=logging.INFO)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class NewsManager:
|
class NewsManager:
|
||||||
def __init__(self, config: Dict[str, Any], display_manager):
|
def __init__(self, config: Dict[str, Any], display_manager, config_manager=None):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.config_manager = ConfigManager()
|
# Use provided config_manager or create a new one if none provided
|
||||||
|
self.config_manager = config_manager or ConfigManager()
|
||||||
self.display_manager = display_manager
|
self.display_manager = display_manager
|
||||||
self.news_config = config.get('news_manager', {})
|
self.news_config = config.get('news_manager', {})
|
||||||
self.last_update = time.time() # Initialize to current time
|
self.last_update = time.time() # Initialize to current time
|
||||||
|
|||||||
@@ -37,10 +37,11 @@ class BaseNFLManager: # Renamed class
|
|||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||||
self.display_manager = display_manager
|
self.display_manager = display_manager
|
||||||
self.config_manager = ConfigManager()
|
# Store reference to config instead of creating new ConfigManager
|
||||||
|
self.config_manager = None # Not used in this class
|
||||||
self.config = config
|
self.config = config
|
||||||
self.cache_manager = cache_manager
|
self.cache_manager = cache_manager
|
||||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.nfl_config = config.get("nfl_scoreboard", {}) # Changed config key
|
self.nfl_config = config.get("nfl_scoreboard", {}) # Changed config key
|
||||||
self.is_enabled = self.nfl_config.get("enabled", False)
|
self.is_enabled = self.nfl_config.get("enabled", False)
|
||||||
@@ -62,12 +63,8 @@ class BaseNFLManager: # Renamed class
|
|||||||
|
|
||||||
self.logger.setLevel(logging.INFO)
|
self.logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
display_config = config.get("display", {})
|
self.display_width = self.display_manager.matrix.width
|
||||||
hardware_config = display_config.get("hardware", {})
|
self.display_height = self.display_manager.matrix.height
|
||||||
cols = hardware_config.get("cols", 64)
|
|
||||||
chain = hardware_config.get("chain_length", 1)
|
|
||||||
self.display_width = int(cols * chain)
|
|
||||||
self.display_height = hardware_config.get("rows", 32)
|
|
||||||
|
|
||||||
self._logo_cache = {}
|
self._logo_cache = {}
|
||||||
|
|
||||||
@@ -87,7 +84,8 @@ class BaseNFLManager: # Renamed class
|
|||||||
|
|
||||||
def _get_timezone(self):
|
def _get_timezone(self):
|
||||||
try:
|
try:
|
||||||
return pytz.timezone(self.config_manager.get_timezone())
|
timezone_str = self.config.get('timezone', 'UTC')
|
||||||
|
return pytz.timezone(timezone_str)
|
||||||
except pytz.UnknownTimeZoneError:
|
except pytz.UnknownTimeZoneError:
|
||||||
return pytz.utc
|
return pytz.utc
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,11 @@ class BaseNHLManager:
|
|||||||
|
|
||||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||||
self.display_manager = display_manager
|
self.display_manager = display_manager
|
||||||
self.config_manager = ConfigManager()
|
# Store reference to config instead of creating new ConfigManager
|
||||||
|
self.config_manager = None # Not used in this class
|
||||||
self.config = config
|
self.config = config
|
||||||
self.cache_manager = cache_manager
|
self.cache_manager = cache_manager
|
||||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.nhl_config = config.get("nhl_scoreboard", {})
|
self.nhl_config = config.get("nhl_scoreboard", {})
|
||||||
self.is_enabled = self.nhl_config.get("enabled", False)
|
self.is_enabled = self.nhl_config.get("enabled", False)
|
||||||
@@ -63,13 +64,9 @@ class BaseNHLManager:
|
|||||||
# Set logging level to DEBUG to see all messages
|
# Set logging level to DEBUG to see all messages
|
||||||
self.logger.setLevel(logging.DEBUG)
|
self.logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
# Get display dimensions from config
|
# Get display dimensions from matrix
|
||||||
display_config = config.get("display", {})
|
self.display_width = self.display_manager.matrix.width
|
||||||
hardware_config = display_config.get("hardware", {})
|
self.display_height = self.display_manager.matrix.height
|
||||||
cols = hardware_config.get("cols", 64)
|
|
||||||
chain = hardware_config.get("chain_length", 1)
|
|
||||||
self.display_width = int(cols * chain)
|
|
||||||
self.display_height = hardware_config.get("rows", 32)
|
|
||||||
|
|
||||||
# Cache for loaded logos
|
# Cache for loaded logos
|
||||||
self._logo_cache = {}
|
self._logo_cache = {}
|
||||||
@@ -107,7 +104,8 @@ class BaseNHLManager:
|
|||||||
|
|
||||||
def _get_timezone(self):
|
def _get_timezone(self):
|
||||||
try:
|
try:
|
||||||
return pytz.timezone(self.config_manager.get_timezone())
|
timezone_str = self.config.get('timezone', 'UTC')
|
||||||
|
return pytz.timezone(timezone_str)
|
||||||
except pytz.UnknownTimeZoneError:
|
except pytz.UnknownTimeZoneError:
|
||||||
return pytz.utc
|
return pytz.utc
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
import requests
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import requests
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from src.cache_manager import CacheManager
|
from src.cache_manager import CacheManager
|
||||||
from src.config_manager import ConfigManager
|
import pytz
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Dict, Any, Optional, List
|
||||||
|
|
||||||
|
# Import the API counter function from web interface
|
||||||
|
try:
|
||||||
|
from web_interface_v2 import increment_api_counter
|
||||||
|
except ImportError:
|
||||||
|
# Fallback if web interface is not available
|
||||||
|
def increment_api_counter(kind: str, count: int = 1):
|
||||||
|
pass
|
||||||
|
|
||||||
class OddsManager:
|
class OddsManager:
|
||||||
def __init__(self, cache_manager: CacheManager, config_manager: ConfigManager):
|
def __init__(self, cache_manager: CacheManager, config_manager=None):
|
||||||
self.cache_manager = cache_manager
|
self.cache_manager = cache_manager
|
||||||
self.config_manager = config_manager
|
self.config_manager = config_manager
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
@@ -31,6 +40,9 @@ class OddsManager:
|
|||||||
response = requests.get(url, timeout=10)
|
response = requests.get(url, timeout=10)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
raw_data = response.json()
|
raw_data = response.json()
|
||||||
|
|
||||||
|
# Increment API counter for odds data
|
||||||
|
increment_api_counter('odds', 1)
|
||||||
self.logger.debug(f"Received raw odds data from ESPN: {json.dumps(raw_data, indent=2)}")
|
self.logger.debug(f"Received raw odds data from ESPN: {json.dumps(raw_data, indent=2)}")
|
||||||
|
|
||||||
odds_data = self._extract_espn_data(raw_data)
|
odds_data = self._extract_espn_data(raw_data)
|
||||||
|
|||||||