def test_custom_key(self):
        self.seed_fn.return_value = [row(foo=1), row(foo=2)]
        self.cache = Cache(self.seed_fn, self.insert_fn, key_name="foo")

        self.assertEqual(self.cache.get(1), {"foo": 1})
        self.assertIsNone(self.cache.get(3))

        self.cache.add({"foo": 3})

        self.assertEqual(self.cache.get(3), {"foo": 3})

        self.seed_fn.assert_called_once()
        self.insert_fn.assert_called_once_with({"foo": 3})
Exemple #2
0
    def __init__(self, asana_client, db_client, config):
        self._asana_client = asana_client
        self._db_client = db_client
        self._config = config
        self._cache = {}
        self._custom_fields_written = set()

        self.projects = Cache(
            self._fetch_all_fn(SELECT_PROJECTS, self.projects_table_name()),
            self._insert_fn(INSERT_PROJECT, self.projects_table_name(),
                            ["id", "name"]))
        self.users = Cache(
            self._fetch_all_fn(SELECT_USERS, self.users_table_name()),
            self._insert_fn(INSERT_USER, self.users_table_name(),
                            ["id", "name"]))
        self.custom_field_enum_values = Cache(
            self._fetch_all_fn(SELECT_CUSTOM_FIELD_ENUM_VALUES,
                               self.custom_field_enum_values_table_name()),
            self._insert_fn(
                INSERT_CUSTOM_FIELD_ENUM_VALUE,
                self.custom_field_enum_values_table_name(),
                ["custom_field_id", "id", "name", "enabled", "color"]))
Exemple #3
0
class Workspace(object):
    """Abstraction around all the supporting values for a project that are
    global to the workspace, such as users and custom fields."""

    # TODO: Read and cache the database values so we know what needs updates
    # and can avoid unnecessary database calls.

    def __init__(self, asana_client, db_client, config):
        self._asana_client = asana_client
        self._db_client = db_client
        self._config = config
        self._cache = {}
        self._custom_fields_written = set()

        self.projects = Cache(
            self._fetch_all_fn(SELECT_PROJECTS, self.projects_table_name()),
            self._insert_fn(INSERT_PROJECT, self.projects_table_name(),
                            ["id", "name"]))
        self.users = Cache(
            self._fetch_all_fn(SELECT_USERS, self.users_table_name()),
            self._insert_fn(INSERT_USER, self.users_table_name(),
                            ["id", "name"]))
        self.custom_field_enum_values = Cache(
            self._fetch_all_fn(SELECT_CUSTOM_FIELD_ENUM_VALUES,
                               self.custom_field_enum_values_table_name()),
            self._insert_fn(
                INSERT_CUSTOM_FIELD_ENUM_VALUE,
                self.custom_field_enum_values_table_name(),
                ["custom_field_id", "id", "name", "enabled", "color"]))

    def projects_table_name(self):
        return self._config.projects_table_name or PROJECTS_TABLE_NAME

    def project_memberships_table_name(self):
        return self._config.project_memberships_table_name or PROJECT_MEMBERSHIPS_TABLE_NAME

    def users_table_name(self):
        return self._config.users_table_name or USERS_TABLE_NAME

    def followers_table_name(self):
        return self._config.followers_table_name or FOLLOWERS_TABLE_NAME

    def custom_fields_table_name(self):
        return self._config.custom_fields_table_name or CUSTOM_FIELDS_TABLE_NAME

    def custom_field_enum_values_table_name(self):
        return self._config.custom_field_enum_values_table_name or CUSTOM_FIELD_ENUM_VALUES_TABLE_NAME

    def custom_field_values_table_name(self):
        return self._config.custom_field_values_table_name or CUSTOM_FIELD_VALUES_TABLE_NAME

    def create_tables(self):
        self._db_client.write(
            CREATE_PROJECTS_TABLE.format(
                table_name=self.projects_table_name()))
        self._db_client.write(
            CREATE_PROJECT_MEMBERSHIPS_TABLE.format(
                table_name=self.project_memberships_table_name()))
        self._db_client.write(
            CREATE_USERS_TABLE.format(table_name=self.users_table_name()))
        self._db_client.write(
            CREATE_FOLLOWERS_TABLE.format(
                table_name=self.followers_table_name()))
        self._db_client.write(
            CREATE_CUSTOM_FIELDS_TABLE.format(
                table_name=self.custom_fields_table_name()))
        self._db_client.write(
            CREATE_CUSTOM_FIELD_ENUM_VALUES_TABLE.format(
                table_name=self.custom_field_enum_values_table_name()))
        self._db_client.write(
            CREATE_CUSTOM_FIELD_VALUES_TABLE.format(
                table_name=self.custom_field_values_table_name()))

    def _fetch_all_fn(self, SQL, table_name):
        return lambda: self._db_client.read(SQL.format(table_name=table_name))

    def _insert_fn(self, SQL, table_name, column_keys):
        return lambda obj: self._db_client.write(
            SQL.format(table_name=table_name), *
            [obj[key] for key in column_keys])

    def add_user(self, user):
        self.users.add(user)

    def add_project(self, project):
        self.projects.add(project)

    # Followers
    def get_followers(self, task_id):
        return {
            row[0]
            for row in self._db_client.read(
                SELECT_FOLLOWERS.format(
                    table_name=self.followers_table_name()), task_id)
        }

    def add_follower(self, task_id, user):
        self.add_user(user)
        self._db_client.write(
            INSERT_FOLLOWER.format(table_name=self.followers_table_name()),
            (task_id, user["id"]))

    def remove_follower(self, task_id, user_id):
        self._db_client.write(
            DELETE_FOLLOWER.format(table_name=self.followers_table_name()),
            (task_id, user_id))

    # Task Membership
    def task_memberships(self, task_id):
        return [
            row[0] for row in self._db_client.read(
                SELECT_PROJECT_MEMBERSHIPS.format(
                    table_name=self.project_memberships_table_name()), task_id)
        ]

    def add_task_to_project(self, task_id, project):
        self.add_project(project)
        self._db_client.write(
            INSERT_PROJECT_MEMBERSHIP.format(
                table_name=self.project_memberships_table_name()),
            (task_id, project["id"]))

    def remove_task_from_project(self, task_id, project_id):
        self._db_client.write(
            DELETE_PROJECT_MEMBERSHIP.format(
                table_name=self.project_memberships_table_name()),
            (task_id, project_id))

    # Custom fields
    def add_custom_field(self, custom_field_value):
        """Adds a custom field to the database.

        NB: This depends on the data for the custom field being available in
        the custom_field_value parameter, which is true if custom_fields are
        fetched via the task API.
        """
        if custom_field_value["id"] in self._custom_fields_written:
            return

        self._db_client.write(
            INSERT_CUSTOM_FIELD.format(
                table_name=self.custom_fields_table_name()),
            custom_field_value["id"], custom_field_value["name"],
            custom_field_value["type"])

        if custom_field_value["type"] == "enum":
            self.add_custom_field_enum_values(custom_field_value["id"])

        self._custom_fields_written.add(custom_field_value["id"])

    def get_custom_field(self, custom_field_id):
        # NB: The python client doesn't support custom fields yet, so we have
        # to fetch manually.
        return self._asana_client.get(
            "/custom_fields/{}".format(custom_field_id), "")

    # Custom field enum values
    def add_custom_field_enum_values(self, custom_field_id):
        custom_field_def = self.get_custom_field(custom_field_id)
        new_enum_options = custom_field_def.get("enum_options", [])

        old_enum_options = {
            row.id: row
            for row in self.custom_field_enum_values.get(custom_field_id)
            or []
        }

        for enum_option in new_enum_options:
            if enum_option["id"] in old_enum_options:
                old_option = old_enum_options[enum_option["id"]]
                del (old_enum_options[enum_option["id"]])
                if (old_option.name == enum_option["name"]
                        and old_option.enabled == enum_option["enabled"]
                        and old_option.color == enum_option["color"]):
                    continue

            self._db_client.write(
                INSERT_CUSTOM_FIELD_ENUM_VALUE.format(
                    table_name=self.custom_field_enum_values_table_name()),
                custom_field_id, enum_option["id"], enum_option["name"],
                enum_option["enabled"], enum_option["color"])

        for id in old_enum_options.keys():
            self._db_client.write(
                DELETE_CUSTOM_FIELD_ENUM_VALUE.format(
                    table_name=self.custom_field_enum_values_table_name()), id)

    # Custom field values
    def task_custom_field_values(self, task_id):
        return self._db_client.read(
            SELECT_CUSTOM_FIELD_VALUES_FOR_TASK.format(
                table_name=self.custom_field_values_table_name()), task_id)

    def add_custom_field_value(self, task_id, custom_field):
        self.add_custom_field(custom_field)
        self._db_client.write(
            INSERT_CUSTOM_FIELD_VALUE.format(
                table_name=self.custom_field_values_table_name()), task_id,
            custom_field["id"], custom_field.get("text_value"),
            custom_field.get("number_value"),
            custom_field.get("enum_value")
            and custom_field.get("enum_value").get("id"))

    def remove_custom_field_value(self, task_id, custom_field_id):
        self._db_client.write(
            DELETE_CUSTOM_FIELD_VALUE.format(
                table_name=self.custom_field_values_table_name()), task_id,
            custom_field_id)
    def setUp(self):
        self.seed_fn = mock.Mock()
        self.insert_fn = mock.Mock()

        self.cache = Cache(self.seed_fn, self.insert_fn)
class CacheTestCase(unittest.TestCase):
    def setUp(self):
        self.seed_fn = mock.Mock()
        self.insert_fn = mock.Mock()

        self.cache = Cache(self.seed_fn, self.insert_fn)

    def test_add(self):
        self.seed_fn.return_value = [row(id=1), row(id=2)]

        self.assertIsNone(self.cache.get(3))

        self.cache.add({"id": 3})

        self.assertEqual(self.cache.get(3), {"id": 3})

        self.seed_fn.assert_called_once()
        self.insert_fn.assert_called_once_with({"id": 3})

    def test_get(self):
        self.seed_fn.return_value = [row(id=1), row(id=2)]

        self.assertIsNone(self.cache.get(3))

        self.assertEqual(self.cache.get(1), {"id": 1})
        self.assertEqual(self.cache.get(2), {"id": 2})
        self.assertEqual(self.cache.get(1), {"id": 1})
        self.assertEqual(self.cache.get(2), {"id": 2})

        self.seed_fn.assert_called_once()
        self.insert_fn.assert_not_called()

    def test_custom_key(self):
        self.seed_fn.return_value = [row(foo=1), row(foo=2)]
        self.cache = Cache(self.seed_fn, self.insert_fn, key_name="foo")

        self.assertEqual(self.cache.get(1), {"foo": 1})
        self.assertIsNone(self.cache.get(3))

        self.cache.add({"foo": 3})

        self.assertEqual(self.cache.get(3), {"foo": 3})

        self.seed_fn.assert_called_once()
        self.insert_fn.assert_called_once_with({"foo": 3})