コード例 #1
0
    def move_category_to_new_parent(self, category_id, new_parent_id, all_categories = None):
        """
        Move a category (and its child categories) to new context,
        possibly setting the parent category to null.

        :param int category: category moved
        :param int new_parent_id: new parent id
        :param all_categories: equal to self.get_all_categories()
        """
        if not all_categories:
            all_categories = self.get_all_categories()
        # all_categories comes from database

        # Validate category_id
        category_id = safe_int(category_id)
        if not all_categories.has_key(category_id):
            raise Exception("No repositioned category found.")
        category = all_categories[category_id]
        parent_category = None
        # Validate new_parent_id
        new_parent_id = safe_int(new_parent_id)
        if not all_categories.has_key(new_parent_id):
            raise Exception("No new parent category found.")
        parent_category = all_categories[new_parent_id]

        must_update_context = False
        if category.parent == new_parent_id and parent_category.context == category.context:
            raise Exception("Category's context and parent are already as required.")

        # Prevent making eternal loops.
        is_sub_category = self._is_sub_category_or_self(new_parent_id, category_id, all_categories)

        if is_sub_category:
            raise Exception("Cannot move category under its sub category.")

        change_context_query = ''
        if parent_category.context != category.context:
            must_update_context = True
            change_context_query = self._change_context_query(category_id, all_categories)

        try:
            with admin_transaction() as cursor:
                if must_update_context:
                    cursor.execute(change_context_query, parent_category.context)
                cursor.execute("UPDATE `categories` "
                               "    SET `parent_id` = %s "
                               "  WHERE `category_id` = %s ", (new_parent_id, category_id))
        except Exception as e:
            conf.log.exception("Failed to change parent category of %s to be %d: %s",
                category.name, new_parent_id, e)
            raise Exception("Error when updating parent.")
        finally:
            cache = CategoryCache.instance()
            cache.clearAllCategories()
コード例 #2
0
    def move_category_to_root_of_context(self, category_id, new_context_id, all_categories = None):
        """
        Move a category (and its child categories) to new context (if not already there)
        and set the parent category to null.

        :param int category: category moved
        :param int new_context_id: new context
        :param all_categories: equal to self.get_all_categories()
        """

        if not all_categories:
            all_categories = self.get_all_categories()
        # all_categories comes from database

        # Validate new_context_id
        new_context_id = safe_int(new_context_id)
        context = self.get_context_by_id(new_context_id)
        if not context:
            raise Exception("Context was invalid.")

        # Validate category_id
        category_id = safe_int(category_id)
        if not all_categories.has_key(category_id):
            raise Exception("Category not found.")
        category = all_categories[category_id]

        must_change_context = True
        if category.context == new_context_id:
            if category.parent is None:
                raise Exception("Category is already a root category and has the required context.")
            else:
                must_change_context = False

        change_context_query = self._change_context_query(category_id, all_categories)

        try:
            with admin_transaction() as cursor:
                if must_change_context:
                    cursor.execute(change_context_query,
                        new_context_id)
                cursor.execute("UPDATE `categories` "
                               "    SET `parent_id` = NULL "
                               "  WHERE `category_id` = %s ", category_id)
        except:
            conf.log.exception("Failed to move category %s into context %d",
                category.name, new_context_id)
            raise Exception("Error when doing database transaction.")
        finally:
            cache = CategoryCache.instance()
            cache.clearAllCategories()
コード例 #3
0
    def get_all_categories_in_context(self, context_id):
        """
        :returns:
            A flat dictionary of categories in context with category id as index::

                int category_id => Category category

        .. Note::

            All child categories are populated so that it's easy to
            start walking from any category through children.
            Also, the root categories (not child categories) are ordered by
            category_name.

        """
        # Try from cache
        cache = CategoryCache.instance()
        categories = cache.get_categories_in_context(context_id)
        if categories:
            return categories

        # Try from db
        query = "SELECT * FROM categories WHERE context_id = %s ORDER BY category_name"

        # First pass, Read all categories to hash id => categories
        categories = {}
        with admin_query() as cursor:
            try:
                cursor.execute(query, context_id)
                for row in cursor:
                    cat = Category.from_sql_row(row)
                    categories[cat.category_id] = cat
            except:
                conf.log.exception("Failed on building category tree")

        # Second pass, create hierarchy
        for id, category in categories.items():
            # Add category as a parent category's child category
            if category.parent:
                try:
                    categories[category.parent].add_child(category)
                except:
                    conf.log.warning("Inconsistent data. No parent category %d found" % category.parent)

        if categories:
            cache.set_categories_in_context(categories, context_id)

        return categories
コード例 #4
0
    def remove_category(self, category_id):
        """
        Remove a category.
        Raises Exception on error.
        """
        category_id = safe_int(category_id)

        if not category_id:
            raise Exception("Invalid category provided.")

        query = "DELETE FROM categories WHERE category_id = %s"

        with admin_transaction() as cursor:
            try:
                cursor.execute(query, category_id)
            except:
                conf.log.exception("Failed to delete category %d" % category_id)
                raise Exception("Error when removing category.")
            finally:
                cache = CategoryCache.instance()
                cache.clearAllCategories()
コード例 #5
0
    def get_all_project_categories(self, project_key, ordered = False):
        """ Returns a list of all project categories of the project.
        """
        cc = CategoryCache.instance()

        project_key = safe_int(project_key)

        categories = cc.getProjectCategories(project_key)
        if categories:
            return categories

        order_by = ''
        if ordered:
            order_by = 'ORDER BY categories.context_id, categories.category_name'

        query = """
            SELECT categories.* FROM categories
            INNER JOIN project_categories
                ON project_categories.category_key = categories.category_id
            WHERE project_categories.project_key = %s
            {order_by}""".format(order_by=order_by)

        categories = []
        with admin_query() as cursor:
            try:
                cursor.execute(query, (project_key,))
                for row in cursor:
                    cat = Category.from_sql_row(row)
                    categories.append(cat)
                if len(categories) > 0:
                    cc.setProjectCategories(project_key, categories)
            except:
                conf.log.exception("Exception. Query failed when searching project categories. Query('%s')." %
                                   str(query))

        return categories
コード例 #6
0
    def edit_category(self, category_id, category_name, category_description):
        """
        Edits a category name and description.
        Raises Exception on error.
        """
        category_id = safe_int(category_id)

        if not category_id:
            raise Exception("Invalid category provided.")
        existing_category = self.get_category_by_name(category_name)
        if existing_category and existing_category.category_id != category_id:
            raise Exception("A category with the same name already exists.")

        try:
            with admin_transaction() as cursor:
                cursor.execute("UPDATE `categories`"
                               "   SET `category_name` = %s, `description` = %s "
                               " WHERE `category_id` = %s", (category_name, category_description, category_id))
        except:
            conf.log.exception("Failed to edit category %d" % category_id)
            raise Exception("Error when editing category.")
        finally:
            cache = CategoryCache.instance()
            cache.clearAllCategories()
コード例 #7
0
    def add_category(self, name, description, context, parent):
        """
        Add a new category.
        Raises Exception on error.
        """
        parent = safe_int(parent)
        name = str(name)
        context = safe_int(context)

        query = ("INSERT INTO `categories` (`category_name`, `description`, `parent_id`, `context_id`)"
                " VALUES(%s, %s, %s, %s);")
        existing_category = self.get_category_by_name(name)
        if existing_category:
            raise Exception("A category with the same name already exists.")

        try:
            with admin_transaction() as cursor:
                cursor.execute(query, (name, description, parent, context))
        except:
            conf.log.exception("Error when adding category.")
            raise Exception("Error when adding category.")
        finally:
            cache = CategoryCache.instance()
            cache.clearAllCategories()
コード例 #8
0
    def merge_category_to_category(self, category_id, target_category_id, all_categories = None):
        """
        Updates projects pointing to category to use target_category,
        and removes category. Updates also contexts of category and its sub categories.
        Raises exception on error.
        """
        if not all_categories:
            all_categories = self.get_all_categories()
            # all_categories comes from database

        # Validate category_id
        category_id = safe_int(category_id)
        if not all_categories.has_key(category_id):
            raise Exception("Invalid merged category")
        category = all_categories[category_id]
        # Validate target_category_id
        target_category_id = safe_int(target_category_id)
        if not all_categories.has_key(target_category_id):
            raise Exception("Invalid target category")
        target_category = all_categories[target_category_id]
        # If the categories are the same, already fixed.
        if category_id == target_category_id:
            raise Exception("Two same categories given.")

        # Prevent making eternal loops by checking that
        # target category is not sub category of merged category.
        is_sub_category = self._is_sub_category_or_self(target_category_id,
            category_id, all_categories)
        if is_sub_category:
            raise Exception("Cannot merge category to sub category.")

        must_update_context = False
        change_context_query = ''
        # Check whether to update contexts
        if target_category.context != category.context:
            must_update_context = True
            change_context_query = self._change_context_query(category_id, all_categories)

        get_project_ids_with_both_categories = (""
        " SELECT `project_key` FROM `project_categories` pk1 "
        "  WHERE pk1.`category_key` = %s "
        "    AND EXISTS (SELECT pk2.`project_key` FROM `project_categories` pk2 "
        "                 WHERE pk1.`project_key` = pk2.`project_key` AND pk2.`category_key` = %s) ")

        update_project_categories =   (" UPDATE `project_categories` "
                                        "    SET `category_key` = %(target_category_id)s "
                                        "  WHERE `category_key` = %(category_id)s "
                                        "    AND `project_key` NOT IN (%(project_ids)s)")

        update_sub_category_parents = ("UPDATE `categories` "
                                       "   SET `parent_id` = %(target_category_id)s "
                                       " WHERE `parent_id` = %(category_id)s ")

        try:
            with admin_transaction() as cursor:
                # Get projects which have both target and merged categories
                cursor.execute(get_project_ids_with_both_categories, (category_id, target_category_id))
                ids = []
                for row in cursor:
                    ids.append(str(row[0]))

                # Update existing projects to use target_category
                cursor.execute(update_project_categories,
                        {'target_category_id' : target_category_id, 'category_id' : category_id,
                         'project_ids' : (', '.join(ids))})
                # Update the parent of child categories
                cursor.execute(update_sub_category_parents,
                        {'target_category_id' : target_category_id, 'category_id' : category_id})
                # Update the context of sub categories
                if must_update_context:
                    cursor.execute(change_context_query,
                        target_category.context)
                cursor.execute("DELETE FROM `categories` WHERE `category_id` = %s ", category_id)
        except Exception as e:
            conf.log.exception("Failed to merge category '%s' to '%s'" % (str(category.name), str(target_category.name)))
            raise Exception("Error when merging categories.")
        finally:
            cache = CategoryCache.instance()
            cache.clearAllCategories()