main.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import pandas as pd
  2. from icalendar import Calendar, Event
  3. from datetime import datetime, timedelta
  4. import uuid
  5. import os
  6. import requests
  7. telegram_bot_api_key = USER = os.getenv('TELEGRAM_BOT_API_KEY')
  8. telegram_bot_chat_id = USER = os.getenv('TELEGRAM_BOT_CHAT_ID')
  9. def send_message(message:str)->None:
  10. """Send message to me on Telegram when updated.
  11. Args:
  12. message (str): String of message to send.
  13. """
  14. requests.post(f'https://api.telegram.org/bot{telegram_bot_api_key}/sendMessage', json={'chat_id': telegram_bot_chat_id, 'text': message})
  15. def store_df_as_csv(df:pd.DataFrame, name:str)->None:
  16. """Store dataframe as a CSV file.
  17. Args:
  18. df (pd.DataFrame): Dataframe of fixtures.
  19. name (str): Name of the CSV file.
  20. """
  21. df.to_csv(f'./{name}.csv', index=False)
  22. def compare_dfs(df:pd.DataFrame, name:str)->bool:
  23. """Compare the latest DF with the stored DF for any changes.
  24. Args:
  25. df (pd.DataFrame): Latest copy of fixtures in dataframe
  26. name (str): Name of the CSV file.
  27. Returns:
  28. bool: True if match, False if no match.
  29. """
  30. df2 = pd.read_csv(f'./{name}.csv')
  31. return df.equals(df2)
  32. def write_calendar(cal:Calendar)->None:
  33. """Write the cal object to an ics file.
  34. Args:
  35. cal (Calendar): iCalendar object with all the ics details.
  36. """
  37. f = open(os.path.join('./', 'fixtures.ics'), 'wb')
  38. f.write(cal.to_ical())
  39. f.close()
  40. def does_csv_exist()->bool:
  41. """Check if the CSV file exists.
  42. Returns:
  43. bool: True if CSV file exists, False if not.
  44. """
  45. return os.path.isfile('./fixtures.csv')
  46. def make_ordinal(n:int)->str:
  47. '''
  48. Convert an integer into its ordinal representation::
  49. make_ordinal(0) => '0th'
  50. make_ordinal(3) => '3rd'
  51. make_ordinal(122) => '122nd'
  52. make_ordinal(213) => '213th'
  53. '''
  54. n = int(n)
  55. if 11 <= (n % 100) <= 13:
  56. suffix = 'th'
  57. else:
  58. suffix = ['th', 'st', 'nd', 'rd', 'th'][min(n % 10, 4)]
  59. return str(n) + suffix
  60. def create_ical_file(df:pd.DataFrame, cal:Calendar, table:pd.DataFrame)->None:
  61. """Create an iCalendar file from a dataframe.
  62. Args:
  63. df (pd.DataFrame): Dataframe of fixtures.
  64. cal (Calendar): iCalendar object with all the ics details.
  65. table (pd.DataFrame): Dataframe of table details.
  66. """
  67. for index, row in df.iterrows():
  68. event = Event()
  69. match_type = str(row['Type'])
  70. home_team = str(row['Home Team'])
  71. if ("Tongham") not in home_team:
  72. home_team = str(row['Home Team']).replace(" U12","")
  73. away_team = str(row['Away Team.1'])
  74. if ("Tongham") not in away_team:
  75. away_team = str(row['Away Team.1']).replace(" U12","")
  76. venue = str(row['Venue'])
  77. print(row['Date / Time'], home_team, away_team, venue)
  78. start_date_time = datetime.strptime(row['Date / Time'], '%d/%m/%y %H:%M')
  79. # Set default 8am start time to normal 930 kickoff time.
  80. if start_date_time.hour == 8:
  81. start_date_time = start_date_time + timedelta(hours=1, minutes=30)
  82. # Arrival time is 30 mins before kickoff time.
  83. arrival_time = start_date_time + timedelta(minutes=-30)
  84. if match_type == 'L':
  85. summary = "(League) " + home_team + f" ({make_ordinal(table.loc[table['Team'] == home_team, 'POS'].iloc[0])})" + f" {str(row['Unnamed: 4'])} " + away_team + f" ({make_ordinal(table.loc[table['Team'] == away_team, 'POS'].iloc[0])})"
  86. else:
  87. summary = "(Cup) " + home_team + f" {str(row['Unnamed: 4'])} " + away_team
  88. event.add('summary', summary)
  89. notes = row['Status / Notes']
  90. if pd.isna(notes):
  91. notes = 'None'
  92. event.add('description', "Arrive by - " + str(arrival_time) + "\n Notes - " + notes + "\n Table - \n" + str(table))
  93. event.add('dtstart', start_date_time)
  94. # End 2 hours after start_date_time
  95. event.add('dtend', start_date_time + timedelta(hours=2))
  96. event.add('dtstamp', start_date_time)
  97. event.add('uid', str(uuid.uuid4()))
  98. event.add('location', venue)
  99. cal.add_component(event)
  100. write_calendar(cal)
  101. def process_table(table_df:pd.DataFrame)->pd.DataFrame:
  102. table_df = table_df[:-1]
  103. table_df.drop(table_df.columns[len(table_df.columns)-1], axis=1, inplace=True)
  104. table_df['POS'] = table_df['POS'].astype('int')
  105. table_df['P'] = table_df['P'].astype('int')
  106. table_df['W'] = table_df['W'].astype('int')
  107. table_df['D'] = table_df['D'].astype('int')
  108. table_df['L'] = table_df['L'].astype('int')
  109. table_df['PTS'] = table_df['PTS'].astype('int')
  110. store_df_as_csv(table_df, "table")
  111. return table_df
  112. def compare_table():
  113. table_df = pd.read_html("https://fulltime.thefa.com/table.html?selectedSeason=19010414&selectedDivision=165601607&ftsTablePageContent.fixtureAnalysisForm.standingsTableDay=13&ftsTablePageContent.fixtureAnalysisForm.standingsTableMonth=0&ftsTablePageContent.fixtureAnalysisForm.standingsTableYear=2024&activeTab=1")[0]
  114. store_df_as_csv(table_df, "base_table")
  115. return table_df
  116. cal = Calendar()
  117. cal.add('prodid', 'Down Grange Pumas Fixtures')
  118. cal.add('version', '2.0')
  119. fixtures_df = pd.read_html("https://fulltime.thefa.com/fixtures.html?selectedSeason=19010414&selectedFixtureGroupAgeGroup=11&selectedFixtureGroupKey=1_579285719&selectedDateCode=all&selectedClub=&selectedTeam=466317969&selectedRelatedFixtureOption=3&selectedFixtureDateStatus=&selectedFixtureStatus=&previousSelectedFixtureGroupAgeGroup=11&previousSelectedFixtureGroupKey=1_579285719&previousSelectedClub=&itemsPerPage=25")[0]
  120. fixtures_df.head()
  121. table = compare_table()
  122. exists = does_csv_exist()
  123. if exists:
  124. fixtures_change = compare_dfs(fixtures_df, "fixtures")
  125. table_change = compare_dfs(table, "base_table")
  126. if not table_change:
  127. send_message("Table has updated")
  128. if not all([fixtures_change, table_change]):
  129. print("Data Updated, ical updated")
  130. store_df_as_csv(fixtures_df, "fixtures")
  131. create_ical_file(fixtures_df, cal, process_table(table))
  132. send_message("Fixtures updated, ical updated")
  133. else:
  134. print("No Data Updated, No update to ical")
  135. else:
  136. store_df_as_csv(fixtures_df, "fixtures")
  137. create_ical_file(fixtures_df, cal, process_table(table))
  138. send_message("New ical file created")
  139. print("New ical file created")