model.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import re
  2. import _html
  3. import _bs
  4. import pytz
  5. import datetime
  6. # import time
  7. import psycopg2.extras
  8. from pprint import pprint
  9. import arrow
  10. import view
  11. import time
  12. import warnings
  13. try:
  14. warnings.simplefilter("ignore", arrow.factory.ArrowParseWarning)
  15. except AttributeError:
  16. pass
  17. """
  18. Modules _html and _bs4 contain specialized methods.
  19. """
  20. local_timezones = {
  21. "NSW": "Australia/Sydney",
  22. "VIC": "Australia/Melbourne",
  23. "QLD": "Australia/Brisbane",
  24. "WA": "Australia/Perth",
  25. "SA": "Australia/Adelaide",
  26. "TAS": "Australia/Hobart",
  27. "ACT": "Australia/Sydney",
  28. "NT": "Australia/Darwin"}
  29. def scrape_racingaustralia_main_page(row):
  30. this_url = """https://racingaustralia.horse/Home.aspx"""
  31. this_data = _html.get_page(this_url)
  32. venues_all = _bs.get_today_row(this_data, row)
  33. return venues_all
  34. def scrape_racenet_scratchings_page():
  35. this_url = """https://www.racenet.com.au/updates/scratchings"""
  36. this_data = _html.get_page(this_url)
  37. # print(this_data[:50])
  38. json = _bs.get_racenet_json(this_data)
  39. return json
  40. def scrape_racenet_races_page():
  41. this_url = """https://www.racenet.com.au/racing-form-guide"""
  42. this_data = _html.get_page(this_url)
  43. # print(this_data[:50])
  44. json = _bs.get_racenet_races(this_data)
  45. # return json
  46. def get_raw_scratchings(this_venue):
  47. this_raw_data = _html.get_page(this_venue.scratchings_url)
  48. return this_raw_data
  49. def process_raw_data(this_raw_data, this_venue):
  50. """
  51. Processes the raw data from the Scratchings page to obtain meta data.
  52. this_venue is passed to _bs.process_scratchings() to create the inherited namedTuple
  53. :param this_raw_data:
  54. :param this_venue:
  55. :return:
  56. """
  57. race_day_info = _bs.get_meta_data(this_raw_data, this_venue)
  58. return race_day_info
  59. def get_scratching_details(this_raw_data, this_venue):
  60. # this_data = _html.get_page(this_venue.scratchings_url)
  61. scratchings_info = _bs.process_scratchings(this_raw_data, this_venue)
  62. return scratchings_info
  63. def convert_to_unixtime(dt_object):
  64. """
  65. Simple utility function that returns the unixtime from a timezone aware dateTime object
  66. :param dt_object:
  67. :return:
  68. """
  69. utc = pytz.UTC
  70. d = dt_object.astimezone(utc)
  71. epoch = datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=pytz.UTC)
  72. ts = int((d - epoch).total_seconds())
  73. return ts
  74. def convert_to_date(weird_string):
  75. """
  76. Converts a string like 'MONDAY 15 JUL' to a python datetime object
  77. :param weird_string:
  78. :return datetime object:
  79. """
  80. weird_string = re.sub(r' (\d) ', ' 0\1 ', weird_string)
  81. local_timezone = pytz.timezone('Australia/Sydney')
  82. now = datetime.datetime.now(local_timezone)
  83. calculated_date = datetime.datetime.strptime(str(now.year) + ' ' + weird_string, "%Y %A %d %b").date()
  84. # print(calculated_date)
  85. return calculated_date
  86. def send_messages(scratches, source, start=0):
  87. long_message = ''
  88. message_string = '{} {}venue = {} {} {}-{} | race = {} starts at {} | {} UTC | horse = {}{}'
  89. for m in scratches:
  90. flag = ''
  91. if m.torn:
  92. flag = 'FLAGGED!! '
  93. pprint(m)
  94. end = time.time()
  95. message = message_string.format(source,
  96. flag,
  97. m.date.strftime('%a'),
  98. m.date,
  99. m.state,
  100. m.venue,
  101. m.race,
  102. m.time,
  103. m.utc,
  104. '{} {}'.format(m.horse_no, m.horse_display_name),
  105. ' | {0:.2f}s'.format(end-start) if start != 0 else '')
  106. print('this_message: {}'.format(message))
  107. # Append message if possible
  108. if len(long_message) + len(message) < 5997:
  109. if len(long_message) == 0:
  110. long_message = message
  111. else:
  112. long_message += '\n' + message
  113. else:
  114. # Send long message (max 6k characters)
  115. print('Sending very long message > {}'.format(len(long_message)))
  116. view.broadcast(long_message)
  117. # Best would be to now store horses that were just broadcast
  118. long_message = m
  119. # Send all messages
  120. if len(long_message) > 0:
  121. print('Sending long_message > {}'.format(len(long_message)))
  122. view.broadcast(long_message)
  123. def store_scratched_horses(db, full_scratches):
  124. query = """INSERT INTO horses(venue, race_date, race, horse_no, horse_display_name, utctime)
  125. VALUES(%s, %s, %s, %s, %s, %s)
  126. ON CONFLICT(race, horse_no, utctime) DO NOTHING;"""
  127. scratches_to_return = []
  128. regex = r'^INSERT \d+ (\d+)$'
  129. cur3 = db.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)
  130. for this_scratching in full_scratches:
  131. database_entry = (this_scratching.venue, this_scratching.date,
  132. this_scratching.race, this_scratching.horse_no,
  133. this_scratching.horse_display_name,
  134. this_scratching.utc)
  135. cur3.execute(query, database_entry)
  136. match = re.match(regex, cur3.statusmessage)
  137. status = 0
  138. if match:
  139. status = int(match.group(1))
  140. if status > 0:
  141. print('Stored: {}'.format(database_entry))
  142. print(cur3.statusmessage)
  143. scratches_to_return.append(this_scratching)
  144. cur3.close()
  145. db.commit()
  146. return scratches_to_return
  147. def get_race_from_races(this_haystack, needle_date, needle_venue, needle_race):
  148. """
  149. From the previous acquired database data this will return 'start_time',
  150. 'utctime' and torn (boolean) where the date, venue and race are given.
  151. :param this_haystack:
  152. :param needle_date:
  153. :param needle_venue:
  154. :param needle_race:
  155. :return:
  156. """
  157. return_values = ()
  158. # needle_date = arrow.get(needle_date).date()
  159. # print('{} -> {}'.format(needle_date, needle_date.strftime('%Y-%m-%d')))
  160. for race in this_haystack:
  161. # pprint(race)
  162. # print(race.race_date == needle_date)
  163. # print('race.race_date == needle_date: {} == {}'.format(race.race_date, needle_date))
  164. # print('type(race.race_date) == type(needle_date): {} == {}'.format(type(race.race_date), type(needle_date)))
  165. # print(race.venue == needle_venue)
  166. # print(race.race == needle_race)
  167. if ((race.race_date == needle_date) and (race.venue == needle_venue) and (
  168. race.race == needle_race)):
  169. return_values = (race.start_time, race.utctime, race.torn)
  170. break
  171. return return_values
  172. def get_relevant_races_from_database(db):
  173. query = """
  174. SELECT venue, start_time, race_date, utctime, race, torn FROM race_program
  175. WHERE race_date >= %s;
  176. """
  177. # Run this query once and use resulting NamedTuple for data
  178. cur_races = db.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)
  179. today = arrow.utcnow().date()
  180. # print('today: {}'.format(today))
  181. cur_races.execute(query, (today,))
  182. races = cur_races.fetchall()
  183. # print('len(races): {}'.format(len(races)))
  184. # pprint(races)
  185. cur_races.close()
  186. return races