PageRenderTime 46ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/main.py

https://github.com/gtracy/APODEmail
Python | 249 lines | 169 code | 53 blank | 27 comment | 27 complexity | 8f4294d474a6fdfa0d66dfc9441d10ac MD5 | raw file
  1. import webapp2
  2. import os
  3. import re
  4. import logging
  5. import time
  6. import datetime
  7. import calendar
  8. from google.appengine.api import users
  9. from google.appengine.api import mail
  10. from google.appengine.api import urlfetch
  11. from google.appengine.api.labs.taskqueue import Task
  12. from google.appengine.ext import db
  13. from google.appengine.ext.webapp import template
  14. from google.appengine.runtime import apiproxy_errors
  15. from BeautifulSoup import BeautifulSoup, Tag
  16. import config
  17. from data_model import UserCounter
  18. class MainHandler(webapp2.RequestHandler):
  19. def get(self):
  20. # figure out how many people are currently on the distribution list
  21. counter = db.GqlQuery("SELECT * FROM UserCounter").get()
  22. # add the counter to the template values
  23. if counter is None:
  24. userCount = 0
  25. else:
  26. userCount = counter.userCount
  27. template_values = { 'counter':userCount,'captcha_secret':config.RECAPTCHA_KEY }
  28. # generate the html
  29. path = os.path.join(os.path.dirname(__file__), 'templates/index.html')
  30. self.response.out.write(template.render(path, template_values))
  31. ## end MainHandler
  32. class FetchHandler(webapp2.RequestHandler):
  33. def get(self, year, start, end):
  34. end_day = calendar.monthrange(int(year),int(end))[1]
  35. logging.info("fetch APOD for year : %s, start : %s, end : %s, day : %s" % (year,start,end,end_day))
  36. start = datetime.datetime(int(year),int(start),1)
  37. end = datetime.datetime(int(year),int(end),end_day,23,59,59)
  38. logging.info("%s : %s" % (start,end))
  39. user = users.get_current_user()
  40. if user or self.request.headers['X-AppEngine-Cron']:
  41. fetchAPOD(self, start, end, True)
  42. else:
  43. logging.error("failed to find a user! bailing out...")
  44. ## end FetchHandler
  45. class EmailWorker(webapp2.RequestHandler):
  46. def post(self):
  47. try:
  48. email = self.request.get('email')
  49. body = self.request.get('body')
  50. subject = self.request.get('subject')
  51. # send email
  52. apod_message = mail.EmailMessage()
  53. apod_message.subject = subject
  54. apod_message.sender = 'gtracy@gmail.com'
  55. apod_message.html = body
  56. apod_message.to = email
  57. if subject.find('APOD Email') > -1 and self.request.get('bcc') == 'True':
  58. apod_message.bcc = 'gtracy@gmail.com'
  59. apod_message.send()
  60. logging.info(email)
  61. except apiproxy_errors.DeadlineExceededError:
  62. logging.error("DeadlineExceededError exception!?! Try to set status and return normally")
  63. self.response.clear()
  64. self.response.set_status(200)
  65. self.response.out.write("Task took to long for %s - BAIL!" % email)
  66. ## end EmailWorker
  67. class BackgroundCountHandler(webapp2.RequestHandler):
  68. def get(self):
  69. self.post()
  70. def post(self):
  71. counter = 0
  72. q = db.GqlQuery("SELECT __key__ FROM UserSignup LIMIT 1000")
  73. offset = 0
  74. result = q.fetch(1000)
  75. while result:
  76. counter += len(result)
  77. offset += len(result)
  78. result = q.fetch(1000,offset)
  79. counterEntity = db.GqlQuery("SELECT * FROM UserCounter").get()
  80. if counterEntity is None:
  81. counterEntity = UserCounter()
  82. logging.info("we have no counter currently!? creating a new one...")
  83. logging.info("setting the counter to %s" % counter)
  84. counterEntity.userCount = counter
  85. counterEntity.put()
  86. self.response.out.write(counter)
  87. ## end
  88. class CleanEmailsHandler(webapp2.RequestHandler):
  89. def get(self):
  90. numbers = re.compile('[0-9]')
  91. user_list = []
  92. users = db.GqlQuery("SELECT * FROM UserSignup order by date desc")
  93. for u in users:
  94. user_list.append({'key':u.key(),
  95. 'email':u.email,
  96. 'note':u.notes,
  97. 'referral':u.referral,
  98. 'date':u.date
  99. })
  100. # add the counter to the template values
  101. template_values = { 'users':user_list, }
  102. # generate the html
  103. path = os.path.join(os.path.dirname(__file__), 'templates/cleaner.html')
  104. self.response.out.write(template.render(path, template_values))
  105. ## end MainHandler
  106. class AdhocEmailHandler(webapp2.RequestHandler):
  107. def get(self):
  108. subject = "APOD is coming back!"
  109. template_file = "templates/adhocemail.html"
  110. # use the task infrastructure to send emails
  111. template_values = { }
  112. path = os.path.join(os.path.dirname(__file__), template_file)
  113. body = template.render(path, template_values)
  114. task = Task(url='/emailqueue', params={'email':'gtracy@gmail.com','subject':'testing','body':body})
  115. task.add('emailqueue')
  116. self.response.out.write(template.render(path, template_values))
  117. ## end AdhocEmailHandler
  118. urlbase = "https://apod.nasa.gov/apod"
  119. url = urlbase + "/astropix.html"
  120. myemail = 'gtracy@gmail.com'
  121. footerText = "<hr><p><i><strong>This is an automated email. If you notice any problems, just send me a note at <a href=mailto:gtracy@gmail.com>gtracy@gmail.com</a>. You can add and remove email addresses to this distribution list here, <a href=https://apodemail.org>https://apodemail.org</a>.</strong></i><a href='mailto:unsubscribe@apodemail-hrd.appspotmail.com?Subject=unsubscribe'>Unsubscribe</a></p>"
  122. #googleAnalytics = "https://www.google-analytics.com/collect?v=1&tid=UA-12345678-1&cid=CLIENT_ID_NUMBER&t=event&ec=email&ea=open&el=recipient_id&cs=newsletter&cm=email&cn=Campaign_Name"
  123. def fetchAPOD(self, start, end, sendEmail):
  124. logging.debug("fetching the APOD site...")
  125. loop = 0
  126. done = False
  127. result = None
  128. while not done and loop < 3:
  129. try:
  130. result = urlfetch.fetch(urlbase)
  131. done = True;
  132. except urlfetch.DownloadError:
  133. logging.error("Error loading page (%s)... sleeping" % loop)
  134. if result:
  135. logging.debug("Error status: %s" % result.status_code)
  136. logging.debug("Error header: %s" % result.headers)
  137. logging.debug("Error content: %s" % result.content)
  138. time.sleep(4)
  139. loop = loop+1
  140. if result is None or result.status_code != 200:
  141. logging.error("Exiting early: error fetching URL: " + result.status_code)
  142. return
  143. soup = BeautifulSoup(result.content)
  144. #logging.debug(soup)
  145. # fix all of the relative links
  146. for a in soup.html.body.findAll('a'):
  147. if a['href'].find("http") < 0:
  148. fullurl = "%s/%s" % (urlbase,a['href'])
  149. a['href'] = a['href'].replace(a['href'],fullurl)
  150. # fix all of the relative image source references
  151. for i in soup.html.body.findAll('img'):
  152. if i['src'].find("http") < 0:
  153. fullurl = "%s/%s" % (urlbase,i['src'])
  154. i['src'] = i['src'].replace(i['src'],fullurl)
  155. # soup pulled out the center tag at the end for some reason
  156. # so add it back in
  157. first, second = soup.findAll('hr')
  158. first.insert(0,"<center>")
  159. footer = Tag(soup, "p")
  160. footer.insert(0,footerText)
  161. soup('br')[-1].insert(0,footer)
  162. title = soup('center')[1].b.string
  163. subject = "APOD - %s" % title
  164. # is this a video/youtube APOD?
  165. if soup('iframe') and soup('iframe')[0]:
  166. video_src = soup.find('iframe')['src']
  167. # push in the youtube link and a video preview image
  168. video_id = re.findall('embed/(\S+)[?]', video_src)
  169. logging.info(video_src)
  170. logging.info(video_id[0])
  171. soup('br')[0].insert(0,"<center><a href=\"%s\">%s</a></center><br>" % (video_src,video_src))
  172. img_src = "https://img.youtube.com/vi/%s/hqdefault.jpg" % video_id[0]
  173. soup('br')[0].insert(1,"<center><a href=\"%s\"><img src=\"%s\"></a></center><br>" % (video_src,img_src))
  174. # template_values = { 'content':soup }
  175. # path = os.path.join(os.path.dirname(__file__), 'templates/cron.html')
  176. # self.response.out.write(template.render(path, template_values))
  177. user_count = 0
  178. if sendEmail:
  179. users = db.GqlQuery("SELECT email FROM UserSignup WHERE date >= :1 AND date <= :2", start, end)
  180. for u in users:
  181. user_count += 1
  182. task = Task(url='/emailqueue', params={'title':title,'email':u.email,'subject':subject,'body':str(soup)})
  183. task.add('emailqueue')
  184. logging.info('Spawned %s email tasks for %s' % (user_count, start))
  185. self.response.out.write('%s %s' % (subject, user_count))
  186. #
  187. # Create the WSGI application instance for the APOD signup
  188. #
  189. app = webapp2.WSGIApplication([('/', MainHandler),
  190. ('/dailyemail/(.*)/(.*)/(.*)', FetchHandler),
  191. ('/adhocemail', AdhocEmailHandler),
  192. ('/emailqueue', EmailWorker),
  193. ('/usercount', BackgroundCountHandler),
  194. ('/admin/clean', CleanEmailsHandler),
  195. ],
  196. debug=True)