<repo_name>mihai-varga/fileChangeNotifier<file_sep>/main.py
import webapp2, os, jinja2, sys, cgi, urllib, urllib2, hashlib
from google.appengine.api import users, mail, urlfetch
sys.path.append('./models')
import models
from webapp2_extras import sessions

JINJA_ENVIRONMENT = jinja2.Environment(
    loader = jinja2.FileSystemLoader(os.path.dirname(__file__)),
    extensions = ['jinja2.ext.autoescape'])

config = {}
config['webapp2_extras.sessions'] = {
        'secret_key' : 'super-banan-girl-unguessable',
        }

class BaseHandler(webapp2.RequestHandler):
    def dispatch(self):
        self.session_store = sessions.get_store(request=self.request)
        
        try:
            webapp2.RequestHandler.dispatch(self)
        finally:
            self.session_store.save_sessions(self.response)

    @webapp2.cached_property
    def session(self):
        return self.session_store.get_session()

class LoginPage(BaseHandler):
    def get(self):
        template_values = {
                'g_sign_in' : users.create_login_url(self.request.uri)}
        template = JINJA_ENVIRONMENT.get_template('templates/login_page.html')
        self.response.write(template.render(template_values))

    def post(self):
        # Facebook does a POST request when loading the page in the iFrame
        template_values = {
                'g_sign_in' : users.create_login_url(self.request.uri)}
        template = JINJA_ENVIRONMENT.get_template('templates/login_page.html')
        self.response.write(template.render(template_values))

class SessionData(BaseHandler):
    def get(self):
        # noting to do here
        self.redirect('/')

    def post(self):
        # save the userID for this session
        self.session['user_id'] = self.request.get('userID')
        self.session['email'] = self.request.get('email')
        if self.session['email'] == '':
            # we're on facebook
            self.session['on_facebook'] = True
        else:
            self.session['user_id'] = self.session['user_id'] + '@google'
            # encode it, in case a user_id exists both on facebook and google
        

class MainPage(BaseHandler):
    def get(self):
        user_id = self.session.get('user_id')
        if user_id == None:
            # session has expired
            self.redirect('/')
            
        file_list = []
        file_query = models.Files.query( ancestor = models.FilesKey(
            user_id)).fetch()
        for entry in file_query:
            dict = {}
            dict['url'] = entry.file_url
            dict['name'] = entry.file_name
            file_list.append(dict)

        template_values = { 'file_list' : file_list}
        template = JINJA_ENVIRONMENT.get_template('templates/index.html')
        self.response.write(template.render(template_values))

    def post(self):
        user_id = self.session.get('user_id')
        if user_id == None:
            # session has expired
            self.redirect('/')


        new_file = models.Files(parent = models.FilesKey(user_id))
        if self.session.get('on_facebook'):
            new_file.fb_user = user_id
        else:
            new_file.g_user = user_id
            new_file.email = self.session.get('email')
        new_file.file_url = str(cgi.escape(self.request.get('file_url')))
        new_file.file_name = str(cgi.escape(self.request.get('file_name')))
        if new_file.file_url != '' and new_file.file_url != None:
            new_file.put()
        self.redirect('/index')

class LogoutHandler(BaseHandler):
    def get(self):
        self.session['user_id'] = None
        self.redirect('/')


class UnWatch(BaseHandler):
    def get(self):
        user_id = self.session.get('user_id')
        if user_id == None:
            # session has expired
            self.redirect('/')
            
        file_list = []
        file_query = models.Files.query( ancestor = models.FilesKey(
            user_id)).fetch()
        for entry in file_query:
            dict = {}
            dict['url'] = entry.file_url
            dict['name'] = entry.file_name
            dict['chbox'] = 'chbox' + entry.file_url
            file_list.append(dict)

        if len(file_list) == 0:
            self.redirect('/index')

        template_values = { 'file_list' : file_list}
        template = JINJA_ENVIRONMENT.get_template('templates/unwatch.html')
        self.response.write(template.render(template_values))

    def post(self):
        user_id = self.session.get('user_id')
        if user_id == None:
            # session has expired
            self.redirect('/')

        file_query = models.Files.query( ancestor = models.FilesKey(
            user_id)).fetch()
        files_to_unwatch = []
        for entry in file_query:
            if ('on' == str(cgi.escape(self.request.get('chbox'+entry.file_url)))):
                files_to_unwatch.append(entry)

        for entry in files_to_unwatch:
            entry.key.delete()

        self.redirect('/unwatch')

class ChannelUrl(webapp2.RequestHandler):
    def get(self):
        template = JINJA_ENVIRONMENT.get_template('templates/channelUrl.html')
        self.response.write(template.render())

class PrivacyPolicy(webapp2.RequestHandler):
    def get(self):
        template = JINJA_ENVIRONMENT.get_template('templates/privacyPolicy.html')
        self.response.write(template.render())
    def post(self):
        template = JINJA_ENVIRONMENT.get_template('templates/privacyPolicy.html')
        self.response.write(template.render())

class TermsOfService(webapp2.RequestHandler):
    def get(self):
        template = JINJA_ENVIRONMENT.get_template('templates/termsOfUse.html')
        self.response.write(template.render())
    def post(self):
        template = JINJA_ENVIRONMENT.get_template('templates/termsOfUse.html')
        self.response.write(template.render())


application = webapp2.WSGIApplication([
    ('/', LoginPage),
    ('/index', MainPage),
    ('/unwatch', UnWatch),
    ('/session', SessionData),
    ('/channelUrl', ChannelUrl),
    ('/logout', LogoutHandler),
    ('/privacy', PrivacyPolicy),
    ('/terms', TermsOfService),
    ], debug = True,
    config = config)
<file_sep>/watcher.py
import webapp2, sys, urllib, hashlib
from google.appengine.api import users, mail, urlfetch
sys.path.append('./models')
import models

FACEBOOK_APP_ID = '189988587852276'
FACEBOOK_APP_SECRET = ''


class Watcher(webapp2.RequestHandler):
    def get(self):
        file_query = models.Files.query().fetch()
        rpcs = []

        def handle_result(rpc, entry):
            response = rpc.get_result().content
            md5 = hashlib.md5(response).hexdigest()
            if entry.md5 == None or entry.md5 == '':
                entry.md5 = md5
                entry.put()
            elif entry.md5 != md5:
                entry.md5 = md5
                entry.put()

                if entry.fb_user != None:
                    # Send notification on facebook:
                    template = 'The file: "' + entry.file_name + '" has changed!'
                    url = 'https://graph.facebook.com/' + entry.fb_user + '/notifications/'
                    data = { 'access_token' : FACEBOOK_APP_ID + '|' + FACEBOOK_APP_SECRET,
                            'href' : 'index',
                            'template' : template}
                    data = urllib.urlencode(data)
                    response = urlfetch.fetch(url=url,
                            payload=data,
                            method=urlfetch.POST,
                            headers={'Content-Type': 'application/x-www-form-urlencoded'})
                else:
                    # we have a google+ user
                    sender = 'File Change Notifier <<EMAIL>>'
                    subject = 'A file has changed!'
                    body = ('The file: "' + entry.file_name + '" at ' +
                            entry.file_url + ' has changed.')
                    mail.send_mail(sender, entry.email, subject, body)

        def create_callback(rpc, entry):
            return lambda: handle_result(rpc, entry)

        for entry in file_query:
            rpc = urlfetch.create_rpc()
            rpc.callback = create_callback(rpc, entry)
            urlfetch.make_fetch_call(rpc, entry.file_url,
                        headers={'Content-Type': 'application/x-www-form-urlencoded'},
                        follow_redirects = False)
            rpcs.append(rpc)

        for rpc in rpcs:
            rpc.wait()


application = webapp2.WSGIApplication([
    ('/watcher', Watcher)
    ], debug = True)
<file_sep>/models/models.py
from google.appengine.ext import ndb

class Files(ndb.Model):
    g_user = ndb.StringProperty()
    fb_user = ndb.StringProperty()
    email = ndb.StringProperty()
    file_url = ndb.StringProperty()
    file_name = ndb.StringProperty()
    md5 = ndb.StringProperty()

def FilesKey(user_id):
    return ndb.Key('profile', user_id)


<file_sep>/README.md
Facebook app, it sends notifications when certain files on the web have changed <br>
Available for Facebook: https://apps.facebook.com/flchangenotifier/ <br>
and Google+: http://filechgnotifier.appspot.com/

