class TestColumnManager:
    @classmethod
    @pytest.fixture(autouse=True)
    def setup_class(self, monkeypatch):
        config = AppConfig()
        columns = ["foo-twitter-timeline", "bar-twitter-replies"]
        monkeypatch.setattr(config, "get_stored_columns", lambda: columns)

        self.colman = ColumnManager(config)

    def test_init(self):
        assert len(self.colman) == 2

    # TODO: Test iterator
    def test_iteration(self):
        pass

    # TODO: Test private methods
    def test_count(self):
        pass

    def test_register(self, monkeypatch):
        with pytest.raises(ColumnAlreadyRegistered):
            self.colman.register("foo-twitter-timeline")

        monkeypatch.setattr(self.colman.config, "write", lambda x, y, z: None)
        response = self.colman.register("ble-twitter-timeline")
        assert response == "ble-twitter-timeline"

    def test_unregister(self, monkeypatch):
        monkeypatch.setattr(self.colman.config, "write_section",
                            lambda x, y: None)

        response = self.colman.unregister("foo-twitter-timeline")
        assert response == "foo-twitter-timeline"

    def test_get(self):
        response = self.colman.get("foo-twitter-timeline")
        assert isinstance(response, Column)

        response = self.colman.get("bar-twitter-timeline")
        assert response is None

    def test_columns(self):
        response = self.colman.columns()
        assert isinstance(response, dict)
        assert len(response) == 2

    def test_columns_by_order(self, monkeypatch):
        monkeypatch.setattr(self.colman.config, "read", lambda x, y: None)
        response = self.colman.columns_by_order()
        assert isinstance(response, list)
        assert len(response) == 0

    def test_is_registered(self):
        response = self.colman.is_registered("foo-twitter-timeline")
        assert response == True

        response = self.colman.is_registered("bar-twitter-timeline")
        assert response == False
Exemple #2
0
class TestColumnManager:
    @classmethod
    @pytest.fixture(autouse=True)
    def setup_class(self, monkeypatch):
        config = AppConfig()
        columns = ["foo-twitter-timeline", "bar-twitter-replies"]
        monkeypatch.setattr(config, "get_stored_columns", lambda: columns)

        self.colman = ColumnManager(config)

    def test_init(self):
        assert len(self.colman) == 2

    # TODO: Test iterator
    def test_iteration(self):
        pass

    # TODO: Test private methods
    def test_count(self):
        pass

    def test_register(self, monkeypatch):
        with pytest.raises(ColumnAlreadyRegistered):
            self.colman.register("foo-twitter-timeline")

        monkeypatch.setattr(self.colman.config, "write", lambda x, y, z: None)
        response = self.colman.register("ble-twitter-timeline")
        assert response == "ble-twitter-timeline"

    def test_unregister(self, monkeypatch):
        monkeypatch.setattr(self.colman.config, "write_section", lambda x, y: None)

        response = self.colman.unregister("foo-twitter-timeline")
        assert response == "foo-twitter-timeline"

    def test_get(self):
        response = self.colman.get("foo-twitter-timeline")
        assert isinstance(response, Column)

        response = self.colman.get("bar-twitter-timeline")
        assert response is None

    def test_columns(self):
        response = self.colman.columns()
        assert isinstance(response, dict)
        assert len(response) == 2

    def test_columns_by_order(self, monkeypatch):
        monkeypatch.setattr(self.colman.config, "read", lambda x, y: None)
        response = self.colman.columns_by_order()
        assert isinstance(response, list)
        assert len(response) == 0

    def test_is_registered(self):
        response = self.colman.is_registered("foo-twitter-timeline")
        assert response == True

        response = self.colman.is_registered("bar-twitter-timeline")
        assert response == False
Exemple #3
0
 def __init__(self, load_accounts=True):
     self.config = AppConfig()
     self.accman = AccountManager(self.config, load_accounts)
     self.column_manager = ColumnManager(self.config)
Exemple #4
0
class Core:
    """
    This is the main object in libturpial. This should be the only class you need to
    instantiate to use libturpial. Most important arguments used in Core are
    *account_id* and *column_id*.

    * account_id: Is a composite string formed by the **username** and the 
      **protocol_id** that identify every single account.
    * column_id: Is composite string formed by **account_id** and the 
      **column_name** that identify one column of one account.

    Examples of account_id:

    >>> my_twitter_account = 'foo-twitter'
    >>> my_identica_account = 'foo-identica'

    Example of column_id:

    >>> twitter_timeline = 'foo-twitter-timeline'
    >>> identica_replies = 'foo-identica-replies'

    When you instantiate Core it will load all registered accounts
    automatically, so you don't need to worry about it. If you already registered the
    accounts before, they will be available after you create the core object.

    All the Core methods will return an object defined in 
    :class:`libturpial.api.models` or a valid python object if request is 
    successful, otherwise they will raise an exception.

    If the request returns an array, you can iterate over the elements with:

    >>> for object in response:
    >>>     print object

    In all the following functions the following apply:
        *account_id* must be a string ("username-service")
        *column_id* must be a string ("columnname-username-service")
    """

    def __init__(self, load_accounts=True):
        self.config = AppConfig()
        self.accman = AccountManager(self.config, load_accounts)
        self.column_manager = ColumnManager(self.config)

    def __get_upload_media_object(self, service):
        return UPLOAD_MEDIA_SERVICES[service]

    def __get_short_url_object(self, service):
        return URL_SERVICES[service]

    def filter_statuses(self, statuses):
        filtered_statuses = []
        filtered_terms = self.config.load_filters()
        if len(filtered_terms) == 0:
            return statuses

        for status in statuses:
            for term in map(lambda x: x.lower(), filtered_terms):
                if term.startswith('@'):
                    # Filter statuses by user
                    if status.username.lower() == term[1:]:
                        continue
                    # Filter statuses repeated by filtered users
                    elif status.repeated_by:
                        if status.repeated_by.lower().find(term[1:]) >= 0:
                            continue
                else:
                    if status.text.lower().find(term) >= 0:
                        continue
                filtered_statuses.append(status)
        return filtered_statuses

    def fetch_image(self, url):
        """
        Retrieve an image by it *URL*. Return the binary data of the image
        """
        response = requests.get(url)
        return response.content

    ###########################################################################
    # Multi-account and multi-column API
    ###########################################################################

    def list_accounts(self):
        """
        Return an array with the ids of all registered accounts. For example:

        >>> ['foo-twitter', 'foo-identica']
        """
        return self.accman.list()

    def register_account(self, account):
        # TODO: Add documention/reference for account validation
        """
        Register *account* into config files. *account* must be a
        valid and authenticated :class:`libturpial.api.models.account.Account`
        object.

        When instantiating Core() all accounts get automatically registered.

        Return a string with the id of the account registered.
        """
        return self.accman.register(account)

    def unregister_account(self, account_id, delete_all=False):
        """
        Removes the account identified by *account_id* from memory. If 
        *delete_all* is **True** it deletes all the files asociated to 
        that account from disk otherwise the account will be available
        the next time you load Core.

        Return a string with the id of the account unregistered.
        """
        return self.accman.unregister(account_id, delete_all)

    def all_columns(self):
        """
        Return a dictionary with all columns per account. Example:

        >>> {'foo-twitter': [libturpial.api.models.Column foo-twitter-timeline,
            libturpial.api.models.Column foo-twitter-replies,
            libturpial.api.models.Column foo-twitter-direct,
            libturpial.api.models.Column foo-twitter-sent,
            libturpial.api.models.Column foo-twitter-favorites]}
        """
        columns = {}
        for account in self.registered_accounts():
            columns[account.id_] = []
            for column in account.get_columns():
                columns[account.id_].append(column)
        return columns


    def register_column(self, column_id):
        """
        Register a column identified by *column_id* column and return a string 
        with the id of the column registered on success.
        """
        return self.column_manager.register(column_id)

    def unregister_column(self, column_id):
        """
        Removes the column identified by *column_id* from config and return a
        string with the id if the column unregistered on success.
        """
        return self.column_manager.unregister(column_id)

    def list_protocols(self):
        """
        Return an array with the ids of supported protocols. For example:

        >>> ['twitter', 'identica']
        """
        return Protocol.availables()


    def available_columns(self):
        """
        Return a dictionary with all available (non-registered-yet)
        columns per account. Example:

        >>> {'foo-twitter': [libturpial.api.models.Column foo-twitter-direct,
            libturpial.api.models.Column foo-twitter-replies,
            libturpial.api.models.Column foo-twitter-sent]}
        """
        columns = {}
        for account in self.registered_accounts():
            columns[account.id_] = []
            for column in account.get_columns():
                if not self.column_manager.is_registered(column.id_):
                    columns[account.id_].append(column)
        return columns

    def registered_columns(self):
        """
        Return a *dict* with :class:`libturpial.api.models.column.Column` objects
        per column registered. This method DO NOT return columns in the order they
        have been registered. For ordered columns check
        :method:`registered_columns_by_order()`
        """
        return self.column_manager.columns()

    def registered_columns_by_order(self):
        """
        Return a *list* with :class:`libturpial.api.models.column.Column` objects
        per each column in the same order they have been registered.
        """
        return self.column_manager.columns_by_order()

    def registered_accounts(self):
        """
        Return a *dict* with all registered accounts as an array of
        :class:`libturpial.api.models.account.Account` objects registered
        """
        return self.accman.accounts()

    def get_single_column(self, column_id):
        """
        Return the :class:`libturpial.api.models.column.Column` object identified
        with *column_id*
        """
        return self.column_manager.get(column_id)

    def get_single_account(self, account_id):
        """
        Return the :class:`libturpial.api.models.account.Account` object identified
        with *account_id*
        """
        return self.accman.get(account_id)


    ###########################################################################
    # Microblogging API
    ###########################################################################

    def get_column_statuses(self, account_id, column_id,
                            count=NUM_STATUSES, since_id=None):
        """
        Fetch the statuses for the account *account_id* and the column *column_id*.
        *count* let you specify how many statuses do you want to fetch, values
        range goes from 0-200. If *since_id* is not **None** libturpial will
        only fetch statuses newer than that.

        """
        if column_id.find(ColumnType.SEARCH) == 0:
            criteria = column_id[len(ColumnType.SEARCH) + 1:]
            return self.search(account_id, criteria, count, since_id)

        account = self.accman.get(account_id)
        if column_id == ColumnType.TIMELINE:
            rtn = account.get_timeline(count, since_id)
        elif column_id == ColumnType.REPLIES:
            rtn = account.get_replies(count, since_id)
        elif column_id == ColumnType.DIRECTS:
            rtn = account.get_directs(count, since_id)
        elif column_id == ColumnType.SENT:
            rtn = account.get_sent(count, since_id)
        elif column_id == ColumnType.FAVORITES:
            rtn = account.get_favorites(count)
        elif column_id == ColumnType.PUBLIC:
            rtn = account.get_public_timeline(count, since_id)
        else:
            list_id = account.get_list_id(column_id)
            if list_id is None:
                raise UserListNotFound
            rtn = account.get_list_statuses(list_id, count, since_id)
        return rtn

    def get_public_timeline(self, account_id, count=NUM_STATUSES, since_id=None):
        # TODO: Remove this function and replace with streamming api
        """
        Fetch the public timeline for the service associated to the
        account *account_id*. *count* and *since_id* work in the same way
        that in :meth:`libturpial.api.core.Core.get_column_statuses`
        """
        account = self.accman.get(account_id)
        return account.get_public_timeline(count, since_id)

    def get_followers(self, account_id, only_id=False):
        """
        Return a :class:`libturpial.api.models.profile.Profile` list with 
        all the followers of the account *account_id*
        """
        account = self.accman.get(account_id)
        return account.get_followers(only_id)

    def get_following(self, account_id, only_id=False):
        """
        Return a :class:`libturpial.api.models.profile.Profile` list of 
        all the accounts that *account_id* follows
        """
        account = self.accman.get(account_id)
        return account.get_following(only_id)

    def get_all_friends_list(self):
        """
        Return a list with all the username friends of all the registered 
        accounts.
        """
        friends = []
        for account in self.accman.accounts():
            for profile in account.get_following():
                if profile.username not in friends:
                    friends.append(profile.username)
        self.config.save_friends(friends)
        return friends

    def load_all_friends_list(self):
        return self.config.load_friends()

    def get_user_profile(self, account_id, user=None):
        """
        Return the profile of the *user*, using the *account_id*,
        if user is None, it returns the profile of account_id itself.
        """
        account = self.accman.get(account_id)
        if user:
            profile = account.get_profile(user)
            profile.followed_by = account.is_friend(user)
            profile.muted = self.is_muted(profile.username)
        else:
            profile = account.profile
        return profile

    def get_conversation(self, account_id, status_id):
        account = self.accman.get(account_id)
        return account.get_conversation(status_id)

    def update_status(self, account_id, text, in_reply_id=None, media=None):
        """
        Updates the account *account_id* with content of *text*

        if *in_reply_id* is not None, specifies the tweets that is being answered.

        *media* can specify the filepath of an image. If not None, the status is posted with
        the image attached. At this moment, this method is only valid for Twitter.
        """
        account = self.accman.get(account_id)
        return account.update_status(text, in_reply_id, media)

    def broadcast_status(self, account_id_array, text):
        """
        Updates all the accounts in account_id_array with the content of *text*

        if account_id_array is None or an empty list all registered accounts 
        get updated.
        """
        if not account_id_array:
            account_id_array = [acc.id_ for acc in self.registered_accounts()]

        response = {}
        for account_id in account_id_array:
            try:
                account = self.accman.get(account_id)
                response[account_id] = account.update_status(text)
            except Exception, exc:
                response[account_id] = exc
        return response
Exemple #5
0
 def __init__(self):
     self.config = AppConfig()
     self.accman = AccountManager(self.config)
     self.column_manager = ColumnManager(self.config)
    def setup_class(self, monkeypatch):
        config = AppConfig()
        columns = ["foo-twitter-timeline", "bar-twitter-replies"]
        monkeypatch.setattr(config, "get_stored_columns", lambda: columns)

        self.colman = ColumnManager(config)
Exemple #7
0
 def __init__(self, load_accounts=True):
     self.config = AppConfig()
     self.accman = AccountManager(self.config, load_accounts)
     self.column_manager = ColumnManager(self.config)
Exemple #8
0
class Core:
    """
    This is the main object in libturpial. This should be the only class you
    need to instantiate to use libturpial. Most important arguments used in
    Core are *account_id* and *column_id*.

    * account_id: Is a composite string formed by the **username** and the
      **protocol_id** that identify every single account.
    * column_id: Is composite string formed by **account_id** and the
      **column_name** that identify one column of one account.

    Examples of account_id:

    >>> my_twitter_account = 'foo-twitter'
    >>> my_identica_account = 'foo-identica'

    Example of column_id:

    >>> twitter_timeline = 'foo-twitter-timeline'
    >>> identica_replies = 'foo-identica-replies'

    When you instantiate Core it will load all registered accounts
    automatically, so you don't need to worry about it. If you already
    registered the accounts before, they will be available after you create
    the core object.

    All the Core methods will return an object defined in
    :class:`libturpial.api.models` or a valid python object if request is
    successful, otherwise they will raise an exception.

    If the request returns an array, you can iterate over the elements with:

    >>> core = Core()
    >>> followers = core.get_followers('username-protocol')
    >>> [follower for follower in followers]

    In all the following functions the following apply:
        *account_id* must be a string ("username-service")
        *column_id* must be a string ("columnname-username-service")
    """

    def __init__(self, load_accounts=True):
        self.config = AppConfig()
        self.accman = AccountManager(self.config, load_accounts)
        self.column_manager = ColumnManager(self.config)

    def __get_upload_media_object(self, service):
        return UPLOAD_MEDIA_SERVICES[service]

    def __get_short_url_object(self, service):
        return URL_SERVICES[service]

    def filter_statuses(self, statuses):
        filtered_statuses = []
        filtered_terms = self.config.load_filters()
        if len(filtered_terms) == 0:
            return statuses

        for status in statuses:
            filtered = False
            for term in map(lambda x: x.lower(), filtered_terms):
                if term.startswith('@'):
                    # Filter statuses by user
                    if status.username.lower() == term[1:]:
                        filtered = True
                        break
                    # Filter statuses repeated by filtered users
                    elif status.repeated_by:
                        if status.repeated_by.lower() == term[1:]:
                            filtered = True
                            break
                else:
                    if status.text.lower().find(term) >= 0:
                        filtered = True
                        continue

            if status not in filtered_statuses and not filtered:
                filtered_statuses.append(status)
        return filtered_statuses

    def fetch_image(self, url):
        """
        Retrieve an image by it *URL*. Return the binary data of the image
        """
        response = requests.get(url)
        return response.content

    ###########################################################################
    # Multi-account and multi-column API
    ###########################################################################

    def list_accounts(self):
        """
        Return an array with the ids of all registered accounts. For example:

        >>> ['foo-twitter', 'foo-identica']
        """
        return self.accman.list()

    def register_account(self, account):
        # TODO: Add documention/reference for account validation
        """
        Register *account* into config files. *account* must be a
        valid and authenticated :class:`libturpial.api.models.account.Account`
        object.

        When instantiating Core() all accounts get automatically registered.

        Return a string with the id of the account registered.
        """
        return self.accman.register(account)

    def unregister_account(self, account_id, delete_all=False):
        """
        Removes the account identified by *account_id* from memory. If
        *delete_all* is **True** it deletes all the files asociated to
        that account from disk otherwise the account will be available
        the next time you load Core.

        Return a string with the id of the account unregistered.
        """
        return self.accman.unregister(account_id, delete_all)

    def all_columns(self):
        # TODO: add __str__ function to libturpial.api.models.column.Column
        # objects
        """
        Return a dictionary with all columns per account. Example:

        >>> {'foo-twitter': [libturpial.api.models.Column foo-twitter-timeline,
            libturpial.api.models.Column foo-twitter-replies,
            libturpial.api.models.Column foo-twitter-direct,
            libturpial.api.models.Column foo-twitter-sent,
            libturpial.api.models.Column foo-twitter-favorites]}
        """
        columns = {}
        for account in self.registered_accounts():
            columns[account.id_] = []
            for column in account.get_columns():
                columns[account.id_].append(column)
        return columns

    def register_column(self, column_id):
        """
        Register a column identified by *column_id* column and return a string
        with the id of the column registered on success.
        """
        return self.column_manager.register(column_id)

    def unregister_column(self, column_id):
        """
        Removes the column identified by *column_id* from config and return a
        string with the id if the column unregistered on success.
        """
        return self.column_manager.unregister(column_id)

    def list_protocols(self):
        """
        Return an array with the ids of supported protocols. For example:

        >>> ['twitter', 'identica']
        """
        return Protocol.availables()

    def available_columns(self):
        """
        Return a dictionary with all available (non-registered-yet)
        columns per account. Example:

        >>> {'foo-twitter': [libturpial.api.models.Column foo-twitter-direct,
            libturpial.api.models.Column foo-twitter-replies,
            libturpial.api.models.Column foo-twitter-sent]}
        """
        columns = {}
        for account in self.registered_accounts():
            columns[account.id_] = []
            for column in account.get_columns():
                if not self.column_manager.is_registered(column.id_):
                    columns[account.id_].append(column)
        return columns

    def registered_columns(self):
        """
        Return a *dict* with :class:`libturpial.api.models.column.Column` objects
        per column registered. This method DO NOT return columns in the order they
        have been registered. For ordered columns check
        :meth:`registered_columns_by_order()`
        """
        return self.column_manager.columns()

    def registered_columns_by_order(self):
        """
        Return a *list* with :class:`libturpial.api.models.column.Column` objects
        per each column in the same order they have been registered.
        """
        return self.column_manager.columns_by_order()

    def registered_accounts(self):
        """
        Return a *dict* with all registered accounts as an array of
        :class:`libturpial.api.models.account.Account` objects registered
        """
        return self.accman.accounts()

    def get_single_column(self, column_id):
        """
        Return the :class:`libturpial.api.models.column.Column` object identified
        with *column_id*
        """
        return self.column_manager.get(column_id)

    def get_single_account(self, account_id):
        """
        Return the :class:`libturpial.api.models.account.Account` object identified
        with *account_id*
        """
        return self.accman.get(account_id)


    ###########################################################################
    # Microblogging API
    ###########################################################################

    def get_column_statuses(self, account_id, column_id,
                            count=NUM_STATUSES, since_id=None):
        """
        Fetch the statuses for the account *account_id* and the column
        *column_id*. *count* let you specify how many statuses do you want to
        fetch, values range goes from 0-200. If *since_id* is not **None**
        libturpial will only fetch statuses newer than that.
        """
        if column_id.find(ColumnType.SEARCH) == 0:
            criteria = column_id[len(ColumnType.SEARCH) + 1:]
            return self.search(account_id, criteria, count, since_id)

        account = self.accman.get(account_id)
        if column_id == ColumnType.TIMELINE:
            rtn = account.get_timeline(count, since_id)
        elif column_id == ColumnType.REPLIES:
            rtn = account.get_replies(count, since_id)
        elif column_id == ColumnType.DIRECTS:
            rtn = account.get_directs(count, since_id)
        elif column_id == ColumnType.SENT:
            rtn = account.get_sent(count, since_id)
        elif column_id == ColumnType.FAVORITES:
            rtn = account.get_favorites(count)
        elif column_id == ColumnType.PUBLIC:
            rtn = account.get_public_timeline(count, since_id)
        else:
            column_slug = unescape_list_name(column_id)
            list_id = account.get_list_id(column_slug)
            if list_id is None:
                raise UserListNotFound
            rtn = account.get_list_statuses(list_id, count, since_id)
        return rtn

    def get_public_timeline(self, account_id, count=NUM_STATUSES,
                            since_id=None):
        # TODO: Remove this function and replace with streamming api
        """
        Fetch the public timeline for the service associated to the
        account *account_id*. *count* and *since_id* work in the same way
        that in :meth:`libturpial.api.core.Core.get_column_statuses`
        """
        account = self.accman.get(account_id)
        return account.get_public_timeline(count, since_id)

    def get_followers(self, account_id, only_id=False):
        # TODO: define __str__ function for
        # in libturpial.api.models.profile.Profile Class
        """
        Returns a :class:`libturpial.api.models.profile.Profile` list with
        all the followers of the account *account_id*
        """
        account = self.accman.get(account_id)
        return account.get_followers(only_id)

    def get_following(self, account_id, only_id=False):
        """
        Returns a :class:`libturpial.api.models.profile.Profile` list of
        all the accounts that *account_id* follows
        """
        account = self.accman.get(account_id)
        return account.get_following(only_id)

    def get_all_friends_list(self):
        """
        Return a list with all the username friends of all the registered
        accounts.
        """
        friends = []
        for account in self.accman.accounts():
            for profile in account.get_following():
                if profile.username not in friends:
                    friends.append(profile.username)
        self.config.save_friends(friends)
        return friends

    def load_all_friends_list(self):
        return self.config.load_friends()

    def get_user_profile(self, account_id, user=None):
        """
        Return the profile of the *user*, using the *account_id*,
        if user is None, it returns the profile of account_id itself.
        """
        account = self.accman.get(account_id)
        if user:
            profile = account.get_profile(user)
            profile.followed_by = account.is_friend(user)
            profile.muted = self.is_muted(profile.username)
        else:
            profile = account.profile
        return profile

    def get_conversation(self, account_id, status_id):
        account = self.accman.get(account_id)
        return account.get_conversation(status_id)

    def update_status(self, account_id, text, in_reply_id=None, media=None):
        """
        Updates the account *account_id* with content of *text*

        if *in_reply_id* is not None, specifies the tweets that is being
        answered.

        *media* can specify the filepath of an image. If not None, the status is posted with
        the image attached. At this moment, this method is only valid for Twitter.
        """
        account = self.accman.get(account_id)
        return account.update_status(text, in_reply_id, media)

    def broadcast_status(self, account_id_array, text):
        # TODO: add __str__ to libturpial.api.models.account.Account
        """
        Updates all the accounts in account_id_array with the content of *text*

        if account_id_array is None or an empty list all registered accounts
        get updated.
        """
        if not account_id_array:
            account_id_array = [acc.id_ for acc in self.registered_accounts()]

        response = {}
        for account_id in account_id_array:
            try:
                account = self.accman.get(account_id)
                response[account_id] = account.update_status(text)
            except Exception, exc:
                response[account_id] = exc
        return response
Exemple #9
0
    def setup_class(self, monkeypatch):
        config = AppConfig()
        columns = ["foo-twitter-timeline", "bar-twitter-replies"]
        monkeypatch.setattr(config, "get_stored_columns", lambda: columns)

        self.colman = ColumnManager(config)