Esempio n. 1
0
class SlackFeedItem(db.Model):
    __tablename__ = 'slack_feed_items'
    id = db.Column(db.BigInteger, primary_key=True)
    slack_feed_id = db.Column(db.Integer,
                              db.ForeignKey('feed_setting_slack.id'))

    timestamp = db.Column(db.DateTime(timezone=True), default=None)
    data = db.Column(db.PickleType)

    @property
    def ts(self):
        return self.data['ts']
Esempio n. 2
0
class User(db.Model):
    __tablename__ = 'users'
    uid = db.Column(
        db.Integer, primary_key=True
    )  #todo: maybe write migration to rename to id to be consistant
    username = db.Column(db.String(20), unique=True, default=None)
    # todo: write migration to name `password`
    passwords = db.Column(db.String(100), default=None)
    email = db.Column(db.String(50), default=None)
    settings = db.Column(db.Text, default=None)
    tracking = db.Column(db.Text, default=None)
    rank = db.Column(db.Integer, default=None)

    def __init__(self, username, password, email):
        self.username = username
        self.passwords = sha256_crypt.encrypt(password)
        self.email = email

    def password_is_correct(self, password):
        return sha256_crypt.verify(password, self.passwords)

    @classmethod
    def username_exists(cls, username):
        # todo: look if this query.filter method is proper way to query
        return cls.query.filter(cls.username == username).count() > 0

    @classmethod
    def load_by_username(cls, username):
        return cls.query.filter_by(username=username).first()

    def __repr__(self):
        return '<User %r>' % self.username
Esempio n. 3
0
class TwitterFeedItem(db.Model):
    __tablename__ = 'twitter_feed_items'
    id = db.Column(db.BigInteger, primary_key=True)
    twitter_feed_id = db.Column(db.Integer,
                                db.ForeignKey('feed_setting_twitter.id'))
    tweet_id = db.Column(db.BigInteger)
    tweet_retrieved = db.Column(db.DateTime(timezone=True),
                                default=datetime.now)
    tweet_data = db.Column(db.PickleType)

    def __init__(self, tweet_id, twitter_feed_id, tweet_data):
        self.tweet_id = tweet_id
        self.twitter_feed_id = twitter_feed_id
        self.tweet_data = tweet_data

    @property
    def status_url(self):
        return ('https://twitter.com/statuses/' + str(self.tweet_id))
Esempio n. 4
0
class GithubFeedItem(db.Model):
    __tablename__ = 'github_feed_items'
    id = db.Column(db.BigInteger, primary_key=True)
    github_feed_id = db.Column(db.Integer, db.ForeignKey('feed_setting_github.id'))

    # todo: need to add an index on this due to the lookups here
    sha = db.Column( db.String(40) , default=None, index=True) # git commit id

    commit_date = db.Column( db.DateTime(timezone=True), default=None )
    date_retrieved = db.Column( db.DateTime(timezone=True), default=datetime.now )

    # throwing these all in until decide what is best to use here :/
    git_commit_data = db.Column( db.PickleType )
    #  other potential ideas: github_commit data -   includes stuff like author data


    def __init__(self, github_feed_id=github_feed_id, git_commit_data=git_commit_data ):

        self.github_feed_id = github_feed_id
        self.date_retrieved = datetime.now()
        self.sha = git_commit_data.sha
        self.git_commit_data = git_commit_data.raw_data['commit']
        self.commit_date = dateutil.parser.parse( git_commit_data.raw_data['commit']['committer']['date'] ).replace( tzinfo=None )
Esempio n. 5
0
class TwitterFeedSetting(db.Model):
    __tablename__ = 'feed_setting_twitter'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.uid'))
    # handle = db.Column( db.String(64) )
    hashtag = db.Column(db.String(128), default=None)
    status = db.Column(db.String(64), default=None)
    last_updated = db.Column(db.DateTime(timezone=True), default=None)

    feed_items = db.relationship('TwitterFeedItem',
                                 backref='feed_setting_twitter',
                                 cascade="all, delete-orphan",
                                 lazy='select')

    #todo: move method that starts adding hashtags here

    def set_updating(self):
        self.status = 'updating'
        db.session.commit()
        return self

    def set_updated(self):
        self.status = 'updated'
        self.last_updated = datetime.now()
        db.session.commit()
        return self

    def start_populate(self):
        # download 200 latest tweets with this hash tag, this can go up to 1500 according to twitter api docs
        self.download_tweets(max_tweets=200)
        return True

    def get_last_tweet_id_downloaded(self):
        return db.session.query(db.func.max(
            TwitterFeedItem.tweet_id)).filter_by(
                twitter_feed_id=self.id).scalar()

    def download_tweets(self, since_id=False, max_tweets=100):

        query = self.hashtag
        self.set_updating()
        if since_id:
            hashtag_tweets = [
                status for status in tweepy.Cursor(
                    tweepy_API.search, rpp=100, q=query,
                    since_id=since_id).items(max_tweets)
            ]
        else:
            hashtag_tweets = [
                status for status in tweepy.Cursor(
                    tweepy_API.search, rpp=100, q=query).items(max_tweets)
            ]

        for tweet in hashtag_tweets:
            feed_item = TwitterFeedItem(tweet.id, self.id, tweet)
            db.session.add(feed_item)
        self.set_updated()
        db.session.commit()

    def do_feed_update(self):
        last_tweet = self.get_last_tweet_id_downloaded()
        if (not (last_tweet)):
            self.start_populate()
        else:
            self.download_tweets(since_id=last_tweet)
        return True

    @classmethod
    def belonging_to_user(cls, user_id):
        return cls.query.filter_by(user_id=user_id).all()

    @classmethod
    def update_items(cls):
        items = cls.query.filter_by(status='updated').all()
        for item in items:
            item.do_feed_update()
        db.session.close()
        return True

    @classmethod
    def new_items(cls):
        return cls.query.filter_by(status='new').all()

    @classmethod
    def start_populate_new_items(cls):
        new_items = cls.new_items()
        for item in new_items:
            item.start_populate()
        db.session.close()
        return True
Esempio n. 6
0
class GithubFeedSetting(db.Model):
    __tablename__ = 'feed_setting_github'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.uid'))

    username = db.Column( db.String(128) )
    project = db.Column( db.String(128) , default=None)
    branch = db.Column( db.String(128) , default='master')

    status = db.Column( db.String(64), default=None)
    last_updated = db.Column( db.DateTime(timezone=True), default=None )

    feed_items = db.relationship( 'GithubFeedItem' , backref='github_feed_items', cascade="all, delete-orphan", lazy='select')

    def set_updating(self):
        self.status = 'updating'
        db.session.commit()
        return self

    def set_updated(self):
        self.status = 'updated'
        self.last_updated = datetime.now()
        db.session.commit()
        return self

    def get_branch(self):
        branch = self.branch
        if ( not(self.branch) or (self.branch.strip() == '') ):
            branch = 'master'
        return branch


    @property
    def latest_feed_item(self):
        items = GithubFeedItem.query.order_by(
                            db.desc( GithubFeedItem.commit_date )
                        ).filter_by(
                            github_feed_id=self.id
                        ).limit(1).all()

        if len(items) == 1:
            return items[0]
        else:
            return False

    def download_commits(self, since=False):
        self.set_updating()
        #todo: if this feed update fails, due to exception like http error, throttle limit, etc, then
        #        we need revert the status and (probably) log the error

        if type(since) is datetime:
            commits = self.repo.get_commits(sha=self.branch, since=since )
        else:
            commits = self.repo.get_commits(sha=self.branch)

        for commit in commits:
            # when interating over this PaginatedList type of class it results in
            #  a single request downloading each commit details which may result in too many
            #  request and end up hitting our 5000 req/s rate limit -
            #    in that case may wish to use the requests lib to just make a call to api endpoint like
            #     https://api.github.com/repos/jwenerd/SKTimeline/commits?sha=github_events&per_page=100&since=2016-07-13T19:49:47Z to fetch these details in one go
            if not( self.commit_already_stored(commit.sha) ):
                feed_item = GithubFeedItem(github_feed_id=self.id, git_commit_data=commit)
                db.session.add(feed_item)

        self.set_updated()
        db.session.commit()

    def do_feed_update(self):
        since = False
        if self.latest_feed_item:
            since = self.latest_feed_item.commit_date
        self.download_commits(since=since)

    def commit_already_stored(self, sha):
        count = GithubFeedItem.query.filter_by(github_feed_id=self.id).filter_by(sha=sha).count()
        return count > 0

    @property
    def repo(self):
        repo = GithubAPI.get_repo( self.username + '/' + self.project)
        return repo

    @classmethod
    def belonging_to_user(cls, user_id):
        return cls.query.filter_by(user_id=user_id).all()

    @classmethod
    def update_items(cls):
        items = cls.query.filter_by(status='updated').all()
        for item in items:
            item.do_feed_update()
        db.session.close()
        return True

    @classmethod
    def new_items(cls):
        return cls.query.filter_by(status='new').all()

    @classmethod
    def start_populate_new_items(cls):
        for item in cls.new_items():
            item.do_feed_update()
        db.session.close()
        return True
Esempio n. 7
0
class SlackFeedSetting(db.Model):
    __tablename__ = 'feed_setting_slack'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.uid'))
    status = db.Column(db.String(64), default=None)
    last_updated = db.Column(db.DateTime(timezone=True), default=None)

    # holds the token info, user id, team id, team name
    slack_auth_info = db.Column(db.PickleType)

    # slack machine readable id used for api request
    channel_id = db.Column(db.String(128), default=None)
    # channel info like name/description, members, etc
    channel_info = db.Column(db.PickleType)

    feed_items = db.relationship('SlackFeedItem',
                                 backref='slack_feed_items',
                                 cascade="all, delete-orphan",
                                 lazy='select')

    user_data = db.Column(db.PickleType)

    _slack_client = False

    def __init__(self):
        self.status = 'new'
        self.last_updated = datetime.now()

    def set_updating(self):
        self.status = 'updating'
        db.session.commit()
        return self

    def set_updated(self):
        self.status = 'updated'
        self.last_updated = datetime.now()
        db.session.commit()
        return self

    def start_populate(self):
        # download 200 latest tweets with this hash tag, this can go up to 1500 according to twitter api docs
        self.download_history(count=100)
        return True

    def download_history(self, count=100, oldest=0):
        self.set_updating()
        response = self.slack_client.api_call('channels.history',
                                              channel=self.channel_id,
                                              count=count,
                                              oldest=oldest)
        if response['messages']:
            for message in response['messages']:
                feed_item = SlackFeedItem()
                feed_item.timestamp = datetime.utcfromtimestamp(
                    float(message['ts']))
                feed_item.slack_feed_id = self.id
                feed_item.data = message
                db.session.add(feed_item)

        self.set_updated()
        db.session.commit()
        return response

    @property
    def latest_feed_item(self):
        items = SlackFeedItem.query.order_by(db.desc(
            SlackFeedItem.timestamp)).filter_by(
                slack_feed_id=self.id).limit(1).all()

        if len(items) == 1:
            return items[0]
        else:
            return False

    @classmethod
    def new_items(cls):
        return cls.query.filter_by(status='new').all()

    @classmethod
    def start_populate_new_items(cls):
        new_items = cls.new_items()
        for item in new_items:
            item.start_populate()
        db.session.close()
        return True

    def do_update(self):
        oldest = 0
        if self.latest_feed_item:
            oldest = self.latest_feed_item.ts
        self.download_history(count=1000, oldest=oldest)
        # todo: will need to see how to handle this if there are > 1000 messages to add
        return True

    @classmethod
    def update_items(cls):
        items = cls.query.filter_by(status='updated').all()
        for item in items:
            item.do_update()
        db.session.close()
        return True

    @property
    def slack_client(self):
        if not self.is_token_info_present:
            return False
        if not self._slack_client:
            self._slack_client = SlackClient(self.token)
        return self._slack_client

    @property
    def team_name(self):
        if not self.slack_auth_info:
            return False
        return self.slack_auth_info['team_name']

    @property
    def is_channel_info_present(self):
        if not self.channel_info:
            return False
        return True

    @property
    def channel_name(self):
        if not self.is_channel_info_present:
            return False
        return self.channel_info['name']

    @property
    def is_token_info_present(self):
        if not self.slack_auth_info:
            return False
        if not self.slack_auth_info['access_token']:
            return False
        return True

    @property
    def token(self):
        if not self.is_token_info_present:
            return False
        return self.slack_auth_info['access_token']

    @classmethod
    # todo: these can be dry by using module I think
    def belonging_to_user(cls, user_id):
        return cls.query.filter_by(user_id=user_id).all()

    def slack_user_info(self, user_id):
        if not self.user_data:
            self.user_data = {}

        if user_id in self.user_data:
            # todo: check the _time_retrieved and add and expire length time to see if we should update these records after a certain amount of time
            return self.user_data[user_id]

        user = self.slack_client.api_call('users.info', user=user_id)
        if ('ok' in user) and user['ok']:
            user = user['user']
            user['_time_retrieved'] = int(
                datetime.now().strftime("%s")
            )  #  use this time later for for determining when to update
            user_data_cache = self.user_data
            user_data_cache[user_id] = user

            self.user_data = None
            db.session.commit()

            self.user_data = user_data_cache
            db.session.commit()

            return user

        return False

    def user_name_from_id(self, user_id):
        data = self.slack_user_info(user_id)
        if not data:
            return False
        return data['name']

    def channel_name_from_id(self, channel_id):
        if channel_id == self.channel_id:
            return self.channel_name
        else:
            # for now going to make a request to the API to get the channel name here,
            # we should reconsider another approach here later where it can cache the channel info data, so that it does not continually
            # repeat the same requests over and over again
            channel_info = self.slack_client.api_call('channels.info',
                                                      channel=channel_id)
            name = ''
            if channel_info['ok']:
                name = channel_info['channel']['name']
            return name