Пример #1
0
 def test_unbind(self):
     dbStub.addResult([])
     c = CQDECategoryStore()
     self.assertTrue(c.unbind_category_project(1, 2))
     self.assertTrue(dbStub.cursors[0].query.lower().startswith(
         "delete from project_categories"))
     self.assertTrue(dbStub.closed)
Пример #2
0
    def _show_regular_page(self, req):
        req_data = {}
        # Resolve some static data needed on page (categories and project count)
        cs = CQDECategoryStore()
        all_categories = cs.get_all_categories()

        all_contexts = cs.get_contexts()
        combined_context_ids, combined_contexts, non_combined_contexts = self.get_combined_contexts(
            all_contexts)

        # Total count of projects and count of projects in categories
        direct_category_counts = Projects().get_project_counts_per_category(
            req.authname)

        req_data['direct_category_counts'] = direct_category_counts
        req_data['combined_context_ids'] = combined_context_ids
        req_data['non_combined_contexts'] = non_combined_contexts

        self.set_category_data_into_request_data(all_categories, req_data)

        add_stylesheet(req, 'multiproject/css/multiproject.css')
        add_script(req, 'multiproject/js/jquery.ba-bbq.js')
        add_script(req, 'multiproject/js/explore.js')

        return "find.html", {
            'top_categories': req_data['top_categories'],
            'categories': req_data['root_categories_of_combined_contexts'],
            'nonbrowsable_cntxt': req_data['non_empty_non_combined_contexts'],
            'selected_tab': self.DEFAULT_TAB,
            'tabs': self.TABS}, \
            None
Пример #3
0
    def process_request(self, req):
        """ Process request for listing, creating and removing projects
        """
        categories = []
        q_filter = req.args.get('q')
        limit = req.args.get('limit')
        contexts = req.args.get('contexts[]')
        if contexts:
            if not isinstance(contexts, list):
                contexts = [contexts]

        # Second option: provide contexts in comma separated list
        if not contexts:
            contexts = req.args.get('contexts')
            if contexts:
                contexts = contexts.split(',')

        if q_filter:
            catstore = CQDECategoryStore()
            categories = catstore.get_category_by_name_filter(q_filter, limit, contexts)

        # Construct response in JSON list format
        data = ''
        try:
            data = json.dumps([category.name.encode("utf-8").split("#")[0] for category in categories])
        except Exception, e:
            self.log.exception('Returning JSON from categories failed')
Пример #4
0
    def _show_regular_page(self, req):
        req_data = {}
        # Resolve some static data needed on page (categories and project count)
        cs = CQDECategoryStore()
        all_categories = cs.get_all_categories()

        all_contexts = cs.get_contexts()
        combined_context_ids, combined_contexts, non_combined_contexts = self.get_combined_contexts(all_contexts)

        # Total count of projects and count of projects in categories
        direct_category_counts = Projects().get_project_counts_per_category(req.authname)

        req_data['direct_category_counts'] = direct_category_counts
        req_data['combined_context_ids'] = combined_context_ids
        req_data['non_combined_contexts'] = non_combined_contexts

        self.set_category_data_into_request_data(all_categories, req_data)

        add_stylesheet(req, 'multiproject/css/multiproject.css')
        add_script(req, 'multiproject/js/jquery.ba-bbq.js')
        add_script(req, 'multiproject/js/explore.js')

        return "find.html", {
            'top_categories': req_data['top_categories'],
            'categories': req_data['root_categories_of_combined_contexts'],
            'nonbrowsable_cntxt': req_data['non_empty_non_combined_contexts'],
            'selected_tab': self.DEFAULT_TAB,
            'tabs': self.TABS}, \
            None
Пример #5
0
 def test_fetch_context_data_error(self):
     dbStub.addResult(categories)
     dbStub.addResult(subcategories)
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     data = c.fetch_context_data()
     self.assertFalse(data)
     self.assertTrue(dbStub.closed)
Пример #6
0
 def test_fetch_available_categories_error(self):
     dbStub.addResult(categories)
     dbStub.addResult(subcategories)
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     data = c.fetch_available_categories(1, 2, 3)
     self.assertFalse(data)
     self.assertTrue(dbStub.closed)
 def test_fetch_context_data_error(self):
     dbStub.addResult(categories)
     dbStub.addResult(subcategories)
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     data = c.fetch_context_data()
     self.assertFalse(data)
     self.assertTrue(dbStub.closed)
 def test_fetch_available_categories_error(self):
     dbStub.addResult(categories)
     dbStub.addResult(subcategories)
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     data = c.fetch_available_categories(1, 2, 3)
     self.assertFalse(data)
     self.assertTrue(dbStub.closed)
Пример #9
0
    def _get_project_categories(self, project):
        """
        Create list of categories. License, Language and Development status are shown separately
        and are therefore taken out from the category listing.
        """
        # Get api
        cs = CQDECategoryStore()

        # Get categories and specialcase contexts
        categories = cs.get_all_project_categories(project.id)
        contexts = cs.get_contexts()

        combined_categories = []
        separated_categories_per_context = {}

        categories_by_any_context_id = {}
        context_by_id = {}

        # Setup the contexts dicts
        for context in contexts:
            context_by_id[context.context_id] = context
            if context.summary_name:
                # If context has summary name, it is shown as separated in the summary page
                new_list = []
                categories_by_any_context_id[context.context_id] = new_list
                separated_categories_per_context[context.context_id] = new_list
            else:
                categories_by_any_context_id[context.context_id] = combined_categories

        sort_opts = {'key':lambda c:c.name, 'cmp':lambda x,y: cmp(x.lower(), y.lower())}

        # Now, categories_by_any_context_id has key for each context id
        for category in categories:
            categories_by_any_context_id[category.context].append(category)
        for context_id in separated_categories_per_context:
            separated_categories_per_context[context_id].sort(**sort_opts)

        combined_categories.sort(**sort_opts)

        # Sort alphabetically, case-insensitively
        context_order = [c.context_id for c in
                         sorted(map(lambda id:context_by_id[id],
                             separated_categories_per_context.keys()),
                             key=lambda c:c.summary_name)]

        languages = []
        for context_id in separated_categories_per_context:
            context = context_by_id[context_id]
            if (context.summary_name
                and context.summary_name.lower() in ('language','natural language')):
                # Here, we expect that the description contains the language tag.
                # http://www.w3schools.com/tags/att_meta_http_equiv.asp
                # http://www.w3.org/TR/2011/WD-html-markup-20110113/meta.http-equiv.content-language.html
                languages = [c.description for c in separated_categories_per_context[context_id]]


        return (combined_categories, separated_categories_per_context, context_by_id,
               context_order, languages)
 def test_fetch_selected_categories_error(self):
     dbStub.addResult(contextlist) # 2 contexts
     dbStub.addResult(cat_list)
     dbStub.addResult([])
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     data = c.fetch_selected_categories(2)
     self.assertFalse(data)
     self.assertTrue(dbStub.closed)
Пример #11
0
 def test_fetch_selected_categories_error(self):
     dbStub.addResult(contextlist)  # 2 contexts
     dbStub.addResult(cat_list)
     dbStub.addResult([])
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     data = c.fetch_selected_categories(2)
     self.assertFalse(data)
     self.assertTrue(dbStub.closed)
Пример #12
0
    def get_project_categories(self, projects):
        """ Returns a map of project categories
            project_id => category string
        """
        cs = CQDECategoryStore()
        project_categories = {}

        for project in projects:
            cats = cs.get_all_project_categories(project.id, ordered=True)
            cats_joined = ', '.join([cat.name.split("#")[0] for cat in cats])
            project_categories[project.id] = cats_joined

        return project_categories
Пример #13
0
    def get_project_categories(self, projects):
        """ Returns a map of project categories
            project_id => category string
        """
        cs = CQDECategoryStore()
        project_categories = {}

        for project in projects:
            cats = cs.get_all_project_categories(project.id, ordered=True)
            cats_joined = ', '.join([cat.name.split("#")[0] for cat in cats])
            project_categories[project.id] = cats_joined

        return project_categories
Пример #14
0
 def test_fetch_available_categories(self):
     dbStub.addResult(categories)
     dbStub.addResult(subcategories)
     dbStub.addResult([])  # end recursion
     c = CQDECategoryStore()
     data = c.fetch_available_categories(1, 2, 3)
     self.assertTrue(data)
     self.assertEquals(len(data), 3)
     self.assertEquals(data[3]['name'], 'cats')
     childs = data[3]['childs']
     self.assertTrue(childs)
     self.assertEquals(len(childs), 2)
     self.assertEquals(childs[6]['name'], 'subcat1')
     self.assertTrue(dbStub.closed)
 def test_fetch_available_categories(self):
     dbStub.addResult(categories)
     dbStub.addResult(subcategories)
     dbStub.addResult([]) # end recursion
     c = CQDECategoryStore()
     data = c.fetch_available_categories(1, 2, 3)
     self.assertTrue(data)
     self.assertEquals(len(data), 3)
     self.assertEquals(data[3]['name'], 'cats')
     childs = data[3]['childs']
     self.assertTrue(childs)
     self.assertEquals(len(childs), 2)
     self.assertEquals(childs[6]['name'], 'subcat1')
     self.assertTrue(dbStub.closed)
Пример #16
0
 def test_fetch_selected_categories(self):
     dbStub.addResult(contextlist)  # 2 contexts
     dbStub.addResult(cat_list)
     dbStub.addResult([])
     c = CQDECategoryStore()
     data = c.fetch_selected_categories(2)
     self.assertTrue(data)
     self.assertEquals(len(data), 2)
     self.assertEquals(len(data[1]), 3)
     self.assertEquals(len(data[2]), 0)
     self.assertEquals(data[1][0].name, 'cats')
     self.assertEquals(data[1][1].name, 'dogs')
     self.assertEquals(data[1][2].name, 'bugs')
     self.assertTrue(dbStub.closed)
 def test_fetch_selected_categories(self):
     dbStub.addResult(contextlist) # 2 contexts
     dbStub.addResult(cat_list)
     dbStub.addResult([])
     c = CQDECategoryStore()
     data = c.fetch_selected_categories(2)
     self.assertTrue(data)
     self.assertEquals(len(data), 2)
     self.assertEquals(len(data[1]), 3)
     self.assertEquals(len(data[2]), 0)
     self.assertEquals(data[1][0].name, 'cats')
     self.assertEquals(data[1][1].name, 'dogs')
     self.assertEquals(data[1][2].name, 'bugs')
     self.assertTrue(dbStub.closed)
 def setUp(self):
     dbStub.addResult([[1]])
     dbStub.addResult([])
     self.cm = trac.core.ComponentManager()
     self.req = DummyReq()
     self.path = 'path'  # dummy env.path
     self.project_name = "path"
     self.bind_called = False
     self.unbind_called = False
     self.orig_bind = CQDECategoryStore(
     ).bind_category_project  #@UndefinedVariable
     self.orig_unbind = CQDECategoryStore(
     ).unbind_category_project  #@UndefinedVariable
     CQDECategoryStore().bind_category_project = self.bind
     CQDECategoryStore().unbind_category_project = self.unbind
Пример #19
0
    def test_fetch_context_data(self):
        dbStub.addResult(contextlist)  # 2 contexts
        dbStub.addResult(category_tree)  # 3 categories
        dbStub.addResult([])  # no subcategories for each of the 3 categ.
        dbStub.addResult([])
        dbStub.addResult([])
        dbStub.addResult(subcategories)  # 2 project_categories
        dbStub.addResult([])

        c = CQDECategoryStore()
        data = c.fetch_context_data()
        self.assertTrue(data)
        self.assertEquals(len(data), 2)
        self.assertEquals(data[1]['name'], 'context1')
        self.assertEquals(len(data[1]['categories']), 3)
        self.assertTrue(dbStub.closed)
Пример #20
0
    def getProjectCategories(self, req):
        """ Returns projects categorizations
        """
        categorylist = []

        store = CQDECategoryStore()

        for context in store.get_contexts():
            contextcategories = []
            categories = store.get_categories(context.context_id)
            for category in categories:
                contextcategories.append(category.name)
            categorylist.append(context.name)
            categorylist.append(contextcategories)

        return categorylist
    def test_fetch_context_data(self):
        dbStub.addResult(contextlist) # 2 contexts
        dbStub.addResult(category_tree) # 3 categories
        dbStub.addResult([]) # no subcategories for each of the 3 categ.
        dbStub.addResult([])
        dbStub.addResult([])
        dbStub.addResult(subcategories) # 2 project_categories
        dbStub.addResult([])

        c = CQDECategoryStore()
        data = c.fetch_context_data()
        self.assertTrue(data)
        self.assertEquals(len(data), 2)
        self.assertEquals(data[1]['name'], 'context1')
        self.assertEquals(len(data[1]['categories']), 3)
        self.assertTrue(dbStub.closed)
Пример #22
0
    def getProjectCategories(self, req):
        """ Returns projects categorizations
        """
        categorylist = []

        store = CQDECategoryStore()

        for context in store.get_contexts():
            contextcategories = []
            categories = store.get_categories(context.context_id)
            for category in categories:
                contextcategories.append(category.name)
            categorylist.append(context.name)
            categorylist.append(contextcategories)

        return categorylist
Пример #23
0
    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require("TRAC_ADMIN")
        self.categorystore = CQDECategoryStore()

        data = {}
        if req.method == 'POST':
            if req.args.get('add'):
                self.add_category(req)
            if req.args.get('remove'):
                self.remove_category(req)
            if req.args.get('edit'):
                self.edit_category(req)
            if req.args.get('move'):
                self.move_category_to_new_context(req)
            if req.args.get('merge'):
                self.merge_to_category(req)
            if req.args.get('reposition'):
                self.move_category_to_new_parent(req)

        contexts = self.categorystore.get_contexts()
        context_by_id = {}
        categories_per_context = {}
        for context in contexts:
            context_by_id[context.context_id] = context
            categories_per_context[context.context_id] = \
                self.categorystore.get_all_categories_in_context(context.context_id)
        is_rdf_description = lambda x: x.startswith('http://')

        data['context_by_id'] = context_by_id
        data['categories_per_context'] = categories_per_context
        data['envurl'] = req.base_path
        data['invalid_categories'] = self.categorystore.get_categories_with_invalid_context()
        data['is_rdf_description'] = is_rdf_description
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        return 'admin_categoryeditor.html', data
Пример #24
0
    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require("TRAC_ADMIN")
        self.categorystore = CQDECategoryStore()

        data = {}
        if req.method == 'POST':
            if req.args.get('add'):
                self.add_category(req)
            if req.args.get('remove'):
                self.remove_category(req)
            if req.args.get('edit'):
                self.edit_category(req)
            if req.args.get('move'):
                self.move_category_to_new_context(req)
            if req.args.get('merge'):
                self.merge_to_category(req)
            if req.args.get('reposition'):
                self.move_category_to_new_parent(req)

        contexts = self.categorystore.get_contexts()
        context_by_id = {}
        categories_per_context = {}
        for context in contexts:
            context_by_id[context.context_id] = context
            categories_per_context[context.context_id] = \
                self.categorystore.get_all_categories_in_context(context.context_id)
        is_rdf_description = lambda x: x.startswith('http://')

        data['context_by_id'] = context_by_id
        data['categories_per_context'] = categories_per_context
        data['envurl'] = req.base_path
        data['invalid_categories'] = self.categorystore.get_categories_with_invalid_context()
        data['is_rdf_description'] = is_rdf_description
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        return 'admin_categoryeditor.html', data
Пример #25
0
 def __init__(self):
     self.categ = CQDECategoryStore()
     self.first_context = None
Пример #26
0
class CategorizationAdminPanel(Component):
    """ Trac admin panel component for categorizing project
    """
    implements(IAdminPanelProvider, IRequestHandler)

    def __init__(self):
        self.categ = CQDECategoryStore()
        self.first_context = None

    # IAdminPanelProvider interface requirement
    def get_admin_panels(self, req):
        """ Admin panel navigation items
        """
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', 'General', 'categorization', 'Categorization')

    # IAdminPanelProvider interface requirement
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders categorization admin panel
        """
        req.perm.require('TRAC_ADMIN')


        context_data = self._get_context_data()
        if req.args.get('add_and_bind'):
            self.add_and_bind_category(req, context_data)

        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')

        all_contexts = context_data['all_contexts']
        not_separate_context_ids = context_data['not_separate_context_ids']
        context_by_id = context_data['context_by_id']
        combined_context_ids = context_data['combined_context_ids']

        # First index is reserved for main context
        contexts = [None]
        main_context_id = context_data['main_context_id']
        for context in all_contexts:
            if context.context_id in not_separate_context_ids:
                continue
            contexts.append(context)
        if main_context_id != -1:
            contexts[0] = context_by_id[main_context_id]
        else:
            # No main context id -> drop the first one
            contexts = contexts[1:]

        main_addable_contexts = []
        for context_id in not_separate_context_ids:
            if context_by_id[context_id].edit_type == Context.EDIT_TYPE_ADD:
                main_addable_contexts.append(context_by_id[context_id])

        cats_per_context, project_cats_per_context = self._get_categories_data(context_data)

        data = {}
        data['env'] = req.base_path
        data['contexts'] = contexts
        data['categories_per_context'] = cats_per_context
        data['project_categories_per_context'] = project_cats_per_context
        data['main_addable_contexts'] = main_addable_contexts
        data['not_separate_context_ids'] = not_separate_context_ids
        return 'admin_categories.html', data

    def match_request(self, req):
        """ Path to categorization for ajax requests
        """
        return re.match(r'/categories(?:_trac)?(?:/.*)?$', req.path_info)

    def process_request(self, req):
        """ Ajax request processing. Binding and unbinding categories
        """
        req.perm.require('TRAC_ADMIN')


        # Get parameters
        action = req.args.get('action')
        context_key = req.args.get('context_key')
        category_key = req.args.get('category_key')

        context_data = self._get_context_data(context_key)
        project_key = context_data['project_key']

        # Select action
        if "bind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.bind_category_project(project_key, category_key)
        elif "unbind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.unbind_category_project(project_key, category_key)

        return self.show_categories(req, context_key, context_data)

    def show_categories(self, req, context_key, context_data):
        """ Shows those categories that belongs into project and context
            that is given.
        """
        context_key = safe_int(context_key)

        if (context_key in context_data['combined_context_ids']
            or context_key in context_data['autocomplete_context_ids']):
            raise TracError('Cannot show combined categories')
        context_id = context_key
        # categories for main context are stored to index -1
        if context_key == context_data['main_context_id']:
            context_id = -1

        cats_per_context, project_cats_per_context = self._get_categories_data(context_data)
        data = {
            'context': context_data['context_by_id'][context_key],
            'categories': cats_per_context[context_id],
            'project_categories': project_cats_per_context[context_id],
            'env': req.base_path
        }

        return 'categories.html', data, None

    def add_and_bind_category(self, req, context_data):
        """
        Called by render_admin_panel
        """
        req.perm.require('TRAC_ADMIN')
        if req.method != 'POST':
            raise TracError('POST request required')
        category_name = req.args.get('category_name', '').strip()

        cat = self.categ.get_category_by_name(category_name)
        if not cat:
            context_id = safe_int(req.args.get('context_id', '').strip())

            if not context_id or context_id not in context_data['context_by_id']:
                add_warning(req, _("Invalid context"))
                return

            if not category_name:
                add_warning(req, _("Invalid category"))
                return

            context = context_data['context_by_id'][context_id]
            if context.edit_type != context.EDIT_TYPE_ADD:
                add_warning(req, _("Adding category into context %(context_name) is not allowed",
                    context_name=context.name))
                return

            try:
                self.categ.add_category(category_name, category_name, context_id, None)
                cat = self.categ.get_category_by_name(category_name)
            except Exception as e:
                add_warning(req, _('Category %(what)s cannot be added.',
                    what = category_name) + _(str(e)))
                return

        project_key = context_data['project_key']
        if cat and project_key and self.categ.bind_category_project(project_key, cat.category_id):
            add_notice(req, _('Category %(what)s has been added.',
                               what = category_name))
        else:
            add_warning(req, _('Category %(what)s cannot be added.',
                               what = category_name))

    def _get_context_data(self, context_id = None):
        """

        Returns dict with keys and values (type means here context.admin_type)::
            combined_context_ids: ids of contexts of type combined but not autocomplete
            not_separate_context_ids: set of ids of contexts of type combined, main, or autocomplete
            autocomplete_context_ids: ids of contexts of type autocomplete
            main_context_id: id of the context with type main, defaults to -1
            context_by_id: context_id => context mapping
            all_contexts: all contexts
            involved_context_ids: ids of contexts for which the categories are needed to be fetched
            context_id: safe context id of the request

        :returns: dict with data
        """
        main_context_id = -1

        combined_context_ids = []
        autocomplete_context_ids = []
        not_separate_context_ids = set([])
        context_by_id = {}
        all_contexts = self.categ.get_contexts()
        context_id = safe_int(context_id)
        project_key = Project.get(self.env).id

        for context in all_contexts:
            context_by_id[context.context_id] = context
            if context.admin_type == context.ADMIN_TYPE_MAIN:
                if main_context_id != -1:
                    self.log.warning("Invalid category data: Duplicate main contexts")
                    context.admin_type = context.ADMIN_TYPE_COMBINED
                else:
                    main_context_id = context.context_id
                    not_separate_context_ids.add(context.context_id)
            if context.admin_type == context.ADMIN_TYPE_COMBINED:
                combined_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                autocomplete_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
        if main_context_id == -1:
            self.log.warning('Invalid category data: Main context not set!')

        involved_context_ids = []
        if context_id:
            if context_id not in context_by_id:
                raise TracError('Invalid context id provided %s' %context_id.__class__)
            if context_id == main_context_id:
                involved_context_ids = [main_context_id]+combined_context_ids+autocomplete_context_ids
            else:
                involved_context_ids = [context_id]
        else:
            involved_context_ids = [context.context_id for context in all_contexts]
        data = {'project_key': project_key,
           'autocomplete_context_ids': autocomplete_context_ids,
           'combined_context_ids': combined_context_ids,
           'not_separate_context_ids': not_separate_context_ids,
           'main_context_id': main_context_id,
           'context_by_id': context_by_id,
           'all_contexts': all_contexts,
           'involved_context_ids': involved_context_ids,
           'context_id': context_id}
        return data

    def _get_categories_data(self, context_data):
        """
        Returns 'categories_per_context' and 'project_categories_per_context',
        both being dictionaries with context_id keys and values, which are
        dictionaries with category_id keys and category values.
        """
        context_by_id = context_data['context_by_id']
        main_context_id = context_data['main_context_id']
        project_key = context_data['project_key']
        categories_per_context = {-1: {}}
        project_categories_per_context = {-1: {}}
        for i_context_id in context_data['involved_context_ids']:
            context = context_by_id[i_context_id]
            if context.admin_type in (context.ADMIN_TYPE_MAIN, context.ADMIN_TYPE_COMBINED):
                categories_per_context[-1].update(
                    self.categ.get_all_categories_in_context(i_context_id))
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id)
                    )
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                # categories_per_context[i_context_id] -- don't show anywhere!
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id))
            else:
                categories_per_context[i_context_id] = \
                    self.categ.get_all_categories_in_context(i_context_id)
                project_categories_per_context[i_context_id] = \
                    self._get_categories_by_project(project_key, i_context_id)
        categories_per_context[main_context_id] = categories_per_context[-1]
        project_categories_per_context[main_context_id] = project_categories_per_context[-1]

        return categories_per_context, project_categories_per_context

    def _get_categories_by_project(self, project_key, context_id):
        category_dict = {}
        for category in self.categ.get_categories_by_project(project_key, context_id):
            category_dict[category.category_id] = category
        return category_dict

    def _get_context_name(self, context_key, all_contexts = None):
        if not all_contexts:
            all_contexts = self.categ.get_contexts()

        for c in all_contexts:
            if c.context_id == int(context_key):
                return c.name
        return ""
Пример #27
0
class CategoryEditorAdminPanel(Component):
    """ Trac component for editing category structure
    """
    implements(IAdminPanelProvider)

    def get_admin_panels(self, req):
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', _('General'), 'categoryeditor', _('Edit categories'))

    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require("TRAC_ADMIN")
        self.categorystore = CQDECategoryStore()

        data = {}
        if req.method == 'POST':
            if req.args.get('add'):
                self.add_category(req)
            if req.args.get('remove'):
                self.remove_category(req)
            if req.args.get('edit'):
                self.edit_category(req)
            if req.args.get('move'):
                self.move_category_to_new_context(req)
            if req.args.get('merge'):
                self.merge_to_category(req)
            if req.args.get('reposition'):
                self.move_category_to_new_parent(req)

        contexts = self.categorystore.get_contexts()
        context_by_id = {}
        categories_per_context = {}
        for context in contexts:
            context_by_id[context.context_id] = context
            categories_per_context[context.context_id] = \
                self.categorystore.get_all_categories_in_context(context.context_id)
        is_rdf_description = lambda x: x.startswith('http://')

        data['context_by_id'] = context_by_id
        data['categories_per_context'] = categories_per_context
        data['envurl'] = req.base_path
        data['invalid_categories'] = self.categorystore.get_categories_with_invalid_context()
        data['is_rdf_description'] = is_rdf_description
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        return 'admin_categoryeditor.html', data

    def add_category(self, req):
        req.perm.require('TRAC_ADMIN')
        context = req.args.get('context', '').strip()
        category = req.args.get('category', '').strip()
        parent = req.args.get('parent', '').strip()

        if not context or not category:
            return
        if parent == "NONE" or parent == '':
            parent = None

        try:
            self.categorystore.add_category(category, category, context, parent)
            add_notice(req, _('Category %(what)s has been added.',
                              what=category))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be added. ',
                               what=category) + _(str(e)))

    def remove_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category = self._translate_req_to_category(req, 'removed');

        if not category:
            return

        try:
            self.categorystore.remove_category(category.category_id)
            add_notice(req, _('Category has been removed.'))
        except Exception as e:
            add_warning(req, _('Category was not removed. ') + _(str(e)))

    def edit_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category_id = req.args.get('edited_category_id', '')
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(category_id)
        if not category_id or not category:
            add_warning(req, _('Invalid category id provided.'))
            return
        category_name = req.args.get('edited_category_name', '')
        category_description = req.args.get('edited_category_description', '')
        if not category_name or not category_description:
            add_warning(req, _('Category name and description cannot be empty. "%s" "%s"'%(category_name, category_description)))
            return

        if category.name == category_name and category.description == category_description:
            add_warning(req, _('Category name and description are already as requested.'))
            return

        try:
            self.categorystore.edit_category(category.category_id, category_name, category_description)
            add_notice(req, _('Category has been edited.'))
        except Exception as e:
            add_warning(req, _('Category was not edited. ') + _(str(e)))


    def move_category_to_new_context(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'moved_category_id' or 'moved_category_name'
        category = self._translate_req_to_category(req, 'moved')

        if not new_context or not category:
            return

        try:
            self.categorystore.move_category_to_root_of_context(category.category_id, new_context)
            add_notice(req, _('Category %(what)s has been moved.',
                              what=category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be moved. ',
                               what=category.name) + _(str(e)))

    def move_category_to_new_parent(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'repositioned_category_id' or 'repositioned_category_name'
        category = self._translate_req_to_category(req, 'repositioned')
        root_category =  req.args.get('root_category')
        parent_category_id = None
        parent_category = None
        if not root_category:
            # possible params involved in req: 'new_parent_category_id' or 'new_parent_category_name'
            parent_category = self._translate_req_to_category(req, 'new_parent')
            if parent_category:
                parent_category_id = parent_category.category_id

        if not category or (not root_category and not parent_category):
            add_warning(req, _('Invalid parameters, nothing done. '))
            return

        if root_category:
            try:
                self.categorystore.move_category_to_root_of_context(category.category_id,
                    category.context)
                add_notice(req, _('Category %(what)s has been moved to be root category.',
                    what=category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to be root category. ',
                    what=category.name) + _(str(e)))
        else:
            try:
                self.categorystore.move_category_to_new_parent(category.category_id,
                    parent_category_id)
                add_notice(req, _('Category %(what)s has been moved to parent %(parent)s.',
                    what=category.name, parent=parent_category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to parent %(parent)s. ',
                    what=category.name, parent=parent_category.name) + _(str(e)))

    def merge_to_category(self, req):
        req.perm.require('TRAC_ADMIN')

        # possible params involved in req: target_category_name, target_category_id
        target_category = self._translate_req_to_category(req, 'target')
        # possible params involved in req: merged_category_id, merged_category_name
        merged_category = self._translate_req_to_category(req, 'merged')
        if not target_category or not merged_category:
            add_warning(req, _('Invalid merge category parameters: No corresponding category found.'))
            return

        try:
            self.categorystore.merge_category_to_category(merged_category.category_id,
                target_category.category_id)
            add_notice(req, _('Category %(merged)s has been merged to %(target)s.',
                merged = merged_category.name, target = target_category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s was not merged. ',
                what=merged_category.name) + _(str(e)))

    def _translate_req_to_category(self, req, prefix):
        """
        :returns: :py:class:`multiproject.core.categories.Category` or None
        """
        category_id = req.args.get(prefix + '_category_id', '').strip()
        category_name = req.args.get(prefix + '_category_name','').strip()
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(int(category_id))
        else:
            self.log.debug("searching %s with: %s " % (prefix, category_name))
            category = self.categorystore.get_category_by_name(category_name)
        return category
Пример #28
0
    def search(self, keywords, category_ids, username='******', order_by='newest', sub_page=1, limit=5, all_categories=None):
        """
        Search for projects with fulltext and categories for explore projects.

        .. Note::

            Order by featured is not supported anymore.

        :param order_by: str either 'newest' or 'active'
        :param all_categories: equal to CQDECategoryStore.get_all_categories()
        """
        if not all_categories:
            all_categories = CQDECategoryStore().get_all_categories()

        limit = safe_int(limit)
        limit_attr = {'limit_start': (int(sub_page) - 1) * limit, 'limit': limit}

        wheres = [" u.username IN ('anonymous')"]
        if username != 'anonymous':
            # Alternative implementation by using EXISTS sub query
            wheres = [" u.username IN ('anonymous', '%s')" % safe_string(username)]

        joins = []

        activity_statement = self._activity_statement()

        # Sorting by featured is no longer supported.

        select_columns = """
        DISTINCT p.project_id, p.project_name, p.environment_name, p.description,
        p.author, p.created, p.updated, p.published, p.parent_id, p.icon_name,
        p.trac_environment_key,
        {activity_statement} AS a
        """.format(activity_statement=activity_statement)

        select_count = """COUNT(DISTINCT p.project_id)"""

        if category_ids:
            # Given categories

            search_cat_list = []
            cat_ids_in_search = set([])
            list_of_or_category_ids = []
            and_category_ids = []

            for cat_id in category_ids:
                if not all_categories.has_key(cat_id):
                    continue
                if cat_id in cat_ids_in_search:
                    continue
                cat_ids_in_search.add(cat_id)
                category = all_categories[cat_id]

                if category.has_children():
                    categories_to_go = []
                    categories_to_go.extend(category.children)
                    current_categories = [cat_id]
                    # Counter for avoiding infinite loops
                    counter = len(all_categories) + 1
                    while categories_to_go and counter > 0:
                        child_cat = categories_to_go.pop()
                        if child_cat.category_id in cat_ids_in_search:
                            # categories are wrong somehow. Should not happen when used properly.
                            continue
                        categories_to_go.extend(child_cat.children)
                        cat_ids_in_search.add(child_cat.category_id)
                        current_categories.append(child_cat.category_id)
                        counter -= 1
                    if counter == 0:
                        conf.log.critical("Infinite loop when searching all sub categories of %s " % cat_id)
                    list_of_or_category_ids.append(current_categories)
                else:
                    and_category_ids.append(cat_id)

            # Important special case when searching with one category:
            if len(and_category_ids) == 1:
                joins += ["INNER JOIN project_categories AS pc ON pc.project_key = p.project_id "]
                wheres += ["pc.category_key = %s" % safe_int(and_category_ids[0])]
            elif and_category_ids:
                # Join id:s into in clause wrapped in quotes
                id_list_str = ', '.join([str(safe_int(id)) for id in and_category_ids])
                wheres += [
                    """
                    p.project_id IN (SELECT project_categories.project_key
                                       FROM project_categories
                                      WHERE category_key IN (%s)
                                   GROUP BY project_key
                                     HAVING COUNT(*) = %s)"""  % (id_list_str, len(and_category_ids))
                ]
            for or_category_ids in list_of_or_category_ids:
                id_list_str = ', '.join([str(safe_int(id)) for id in or_category_ids])
                wheres += [
                    """
                    EXISTS (SELECT project_categories.project_key
                              FROM project_categories
                             WHERE project_key = p.project_id
                               AND category_key IN (%s))"""  % id_list_str
                ]


        if keywords:
            wheres += [
            "CONCAT(p.project_name COLLATE utf8_general_ci,p.environment_name, p.description) LIKE '%{0}%'"
            .format(safe_string(kw.replace('%','\\%'))) for kw in keywords.split() if kw]

        # Merge where statements
        where_str = '\n           AND '.join(wheres)

        # Merge join statements
        join_str = " ".join(joins)

        order_by_str = ""
        if order_by == "active":
            order_by_str = " ORDER BY a DESC "
        elif order_by == "recent":
            order_by_str = " ORDER BY IFNULL(p.published, p.created) DESC "

        # Note: If project_activity is not calculated, the project will not be shown here!
        query_template = """
        SELECT {select_clause}
        FROM project_user_visibility AS v
        INNER JOIN projects AS p ON p.project_id = v.project_id
        INNER JOIN user AS u ON u.user_id = v.user_id
        INNER JOIN project_activity AS pa ON pa.project_key = p.project_id
        {join_str}
        WHERE {where_str}
        {order_by}
        {limit}
        """
        # The count_query doesn't have order_by, and limit parts.
        # Here, if we would form query_template so that it already contained the parts
        # common to both query and count_query, i.e., where_str, join_str, wher_str),
        # there would be possibility to put ' {anything} ' or ' %(anything)s ' into where_str.
        # Thus, safer to do the formatting once for all in both cases.
        query = query_template.format(join_str=join_str, where_str=where_str,
                select_clause=select_columns, order_by=order_by_str,
                limit= "LIMIT %(limit_start)d, %(limit)d " % limit_attr)
        count_query = query_template.format(join_str=join_str, where_str=where_str,
            select_clause = select_count, order_by = '', limit = '')

        conf.log.debug("Explore projects search query: %s",query)
        conf.log.debug("Explore projects search count_query: %s",count_query)

        query_count = self._get_single_result(count_query) or 0
        projects, activities = self.queryProjectObjectsForSearch(query)
        return projects, activities, query_count
Пример #29
0
 def test_unbind_error(self):
     dbStub.addResult([])
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     self.assertFalse(c.unbind_category_project(1, 2))
     self.assertTrue(dbStub.closed)
 def tearDown(self):
     dbStub.reset()
     self.cm = None
     self.req = None
     CQDECategoryStore().bind_category_project = self.orig_bind
     CQDECategoryStore().unbind_category_project = self.orig_unbind
Пример #31
0
class CategoryEditorAdminPanel(Component):
    """ Trac component for editing category structure
    """
    implements(IAdminPanelProvider)

    def get_admin_panels(self, req):
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', _('General'), 'categoryeditor', _('Edit categories'))

    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.require("TRAC_ADMIN")
        self.categorystore = CQDECategoryStore()

        data = {}
        if req.method == 'POST':
            if req.args.get('add'):
                self.add_category(req)
            if req.args.get('remove'):
                self.remove_category(req)
            if req.args.get('edit'):
                self.edit_category(req)
            if req.args.get('move'):
                self.move_category_to_new_context(req)
            if req.args.get('merge'):
                self.merge_to_category(req)
            if req.args.get('reposition'):
                self.move_category_to_new_parent(req)

        contexts = self.categorystore.get_contexts()
        context_by_id = {}
        categories_per_context = {}
        for context in contexts:
            context_by_id[context.context_id] = context
            categories_per_context[context.context_id] = \
                self.categorystore.get_all_categories_in_context(context.context_id)
        is_rdf_description = lambda x: x.startswith('http://')

        data['context_by_id'] = context_by_id
        data['categories_per_context'] = categories_per_context
        data['envurl'] = req.base_path
        data['invalid_categories'] = self.categorystore.get_categories_with_invalid_context()
        data['is_rdf_description'] = is_rdf_description
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        return 'admin_categoryeditor.html', data

    def add_category(self, req):
        req.perm.require('TRAC_ADMIN')
        context = req.args.get('context', '').strip()
        category = req.args.get('category', '').strip()
        parent = req.args.get('parent', '').strip()
        license_url = req.args.get('license_url', '').strip()

        if not context or not category:
            return
        if license_url:
            category = category + "#" + license_url

        
        if parent == "NONE" or parent == '':
            parent = None

        try:
            self.categorystore.add_category(category, category, context, parent)
            add_notice(req, _('Category %(what)s has been added.',
                              what=category))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be added. ',
                               what=category) + _(str(e)))

    def remove_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category = self._translate_req_to_category(req, 'removed');

        if not category:
            return

        try:
            self.categorystore.remove_category(category.category_id)
            add_notice(req, _('Category has been removed.'))
        except Exception as e:
            add_warning(req, _('Category was not removed. ') + _(str(e)))

    def edit_category(self, req):
        req.perm.require('TRAC_ADMIN')
        category_id = req.args.get('edited_category_id', '')
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(category_id)
        if not category_id or not category:
            add_warning(req, _('Invalid category id provided.'))
            return
        category_name = req.args.get('edited_category_name', '')
        category_description = req.args.get('edited_category_description', '')
        if len(category_description.split("#")) == 2:
            category_name = category_name + "#" + category_description.split("#")[1]
        if not category_name or not category_description:
            add_warning(req, _('Category name and description cannot be empty. "%s" "%s"'%(category_name, category_description)))
            return

        if category.name == category_name and category.description == category_description:
            add_warning(req, _('Category name and description are already as requested.'))
            return

        try:
            self.categorystore.edit_category(category.category_id, category_name, category_description)
            add_notice(req, _('Category has been edited.'))
        except Exception as e:
            add_warning(req, _('Category was not edited. ') + _(str(e)))


    def move_category_to_new_context(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'moved_category_id' or 'moved_category_name'
        category = self._translate_req_to_category(req, 'moved')

        if not new_context or not category:
            return

        try:
            self.categorystore.move_category_to_root_of_context(category.category_id, new_context)
            add_notice(req, _('Category %(what)s has been moved.',
                              what=category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s cannot be moved. ',
                               what=category.name) + _(str(e)))

    def move_category_to_new_parent(self, req):
        req.perm.require('TRAC_ADMIN')
        new_context = req.args.get('newcontext', '').strip()
        # possible params involved in req: 'repositioned_category_id' or 'repositioned_category_name'
        category = self._translate_req_to_category(req, 'repositioned')
        root_category =  req.args.get('root_category')
        parent_category_id = None
        parent_category = None
        if not root_category:
            # possible params involved in req: 'new_parent_category_id' or 'new_parent_category_name'
            parent_category = self._translate_req_to_category(req, 'new_parent')
            if parent_category:
                parent_category_id = parent_category.category_id

        if not category or (not root_category and not parent_category):
            add_warning(req, _('Invalid parameters, nothing done. '))
            return

        if root_category:
            try:
                self.categorystore.move_category_to_root_of_context(category.category_id,
                    category.context)
                add_notice(req, _('Category %(what)s has been moved to be root category.',
                    what=category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to be root category. ',
                    what=category.name) + _(str(e)))
        else:
            try:
                self.categorystore.move_category_to_new_parent(category.category_id,
                    parent_category_id)
                add_notice(req, _('Category %(what)s has been moved to parent %(parent)s.',
                    what=category.name, parent=parent_category.name))
            except Exception as e:
                add_warning(req, _('Category %(what)s was not moved to parent %(parent)s. ',
                    what=category.name, parent=parent_category.name) + _(str(e)))

    def merge_to_category(self, req):
        req.perm.require('TRAC_ADMIN')

        # possible params involved in req: target_category_name, target_category_id
        target_category = self._translate_req_to_category(req, 'target')
        # possible params involved in req: merged_category_id, merged_category_name
        merged_category = self._translate_req_to_category(req, 'merged')
        if not target_category or not merged_category:
            add_warning(req, _('Invalid merge category parameters: No corresponding category found.'))
            return

        try:
            self.categorystore.merge_category_to_category(merged_category.category_id,
                target_category.category_id)
            add_notice(req, _('Category %(merged)s has been merged to %(target)s.',
                merged = merged_category.name, target = target_category.name))
        except Exception as e:
            add_warning(req, _('Category %(what)s was not merged. ',
                what=merged_category.name) + _(str(e)))

    def _translate_req_to_category(self, req, prefix):
        """
        :returns: :py:class:`multiproject.core.categories.Category` or None
        """
        category_id = req.args.get(prefix + '_category_id', '').strip()
        category_name = req.args.get(prefix + '_category_name','').strip()
        if category_id.isdigit():
            category = self.categorystore.get_category_by_id(int(category_id))
        else:
            self.log.debug("searching %s with: %s " % (prefix, category_name))
            category = self.categorystore.get_category_by_name(category_name)
        return category
Пример #32
0
class CategorizationAdminPanel(Component):
    """ Trac admin panel component for categorizing project
    """
    implements(IAdminPanelProvider, IRequestHandler)

    def __init__(self):
        self.categ = CQDECategoryStore()
        self.first_context = None

    # IAdminPanelProvider interface requirement
    def get_admin_panels(self, req):
        """ Admin panel navigation items
        """
        if 'TRAC_ADMIN' in req.perm:
            yield ('general', 'General', 'categorization', 'Categorization')

    # IAdminPanelProvider interface requirement
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders categorization admin panel
        """
        req.perm.require('TRAC_ADMIN')

        context_data = self._get_context_data()
        if req.args.get('add_and_bind'):
            self.add_and_bind_category(req, context_data)

        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/categorization.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')

        all_contexts = context_data['all_contexts']
        not_separate_context_ids = context_data['not_separate_context_ids']
        context_by_id = context_data['context_by_id']
        combined_context_ids = context_data['combined_context_ids']

        # First index is reserved for main context
        contexts = [None]
        main_context_id = context_data['main_context_id']
        for context in all_contexts:
            if context.context_id in not_separate_context_ids:
                continue
            contexts.append(context)
        if main_context_id != -1:
            contexts[0] = context_by_id[main_context_id]
        else:
            # No main context id -> drop the first one
            contexts = contexts[1:]

        main_addable_contexts = []
        for context_id in not_separate_context_ids:
            if context_by_id[context_id].edit_type == Context.EDIT_TYPE_ADD:
                main_addable_contexts.append(context_by_id[context_id])

        cats_per_context, project_cats_per_context = self._get_categories_data(
            context_data)

        data = {}
        data['env'] = req.base_path
        data['contexts'] = contexts
        data['categories_per_context'] = cats_per_context
        data['project_categories_per_context'] = project_cats_per_context
        data['main_addable_contexts'] = main_addable_contexts
        data['not_separate_context_ids'] = not_separate_context_ids
        return 'admin_categories.html', data

    def match_request(self, req):
        """ Path to categorization for ajax requests
        """
        return re.match(r'/categories(?:_trac)?(?:/.*)?$', req.path_info)

    def process_request(self, req):
        """ Ajax request processing. Binding and unbinding categories
        """
        req.perm.require('TRAC_ADMIN')

        # Get parameters
        action = req.args.get('action')
        context_key = req.args.get('context_key')
        category_key = req.args.get('category_key')

        context_data = self._get_context_data(context_key)
        project_key = context_data['project_key']

        # Select action
        if "bind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.bind_category_project(project_key, category_key)
        elif "unbind" == action:
            #if req.method != 'POST':
            #    raise TracError('POST request required')
            self.categ.unbind_category_project(project_key, category_key)

        return self.show_categories(req, context_key, context_data)

    def show_categories(self, req, context_key, context_data):
        """ Shows those categories that belongs into project and context
            that is given.
        """
        context_key = safe_int(context_key)

        if (context_key in context_data['combined_context_ids']
                or context_key in context_data['autocomplete_context_ids']):
            raise TracError('Cannot show combined categories')
        context_id = context_key
        # categories for main context are stored to index -1
        if context_key == context_data['main_context_id']:
            context_id = -1

        cats_per_context, project_cats_per_context = self._get_categories_data(
            context_data)
        data = {
            'context': context_data['context_by_id'][context_key],
            'categories': cats_per_context[context_id],
            'project_categories': project_cats_per_context[context_id],
            'env': req.base_path
        }

        return 'categories.html', data, None

    def add_and_bind_category(self, req, context_data):
        """
        Called by render_admin_panel
        """
        req.perm.require('TRAC_ADMIN')
        if req.method != 'POST':
            raise TracError('POST request required')

        category_name = req.args.get('category_name', '')
        l_url = req.args.get('license_url')
        if category_name and l_url:
            category_name = category_name.strip()
            l_url = l_url.strip()
            category_name = category_name + "#" + l_url

        cat = self.categ.get_category_by_name(category_name)
        if not cat:
            context_id = safe_int(req.args.get('context_id', '').strip())

            if not context_id or context_id not in context_data[
                    'context_by_id']:
                add_warning(req, _("Invalid context"))
                return

            if not category_name:
                add_warning(req, _("Invalid category"))
                return

            context = context_data['context_by_id'][context_id]

            if context.edit_type != context.EDIT_TYPE_ADD:
                add_warning(
                    req,
                    _("Adding category into context %(context_name) is not allowed",
                      context_name=context.name))
                return

            try:
                self.categ.add_category(category_name, category_name,
                                        context_id, None)
                cat = self.categ.get_category_by_name(category_name)
            except Exception as e:
                add_warning(
                    req,
                    _('Category %(what)s cannot be added.', what=category_name)
                    + _(str(e)))
                return

        project_key = context_data['project_key']
        if cat and project_key and self.categ.bind_category_project(
                project_key, cat.category_id):
            add_notice(
                req,
                _('Category %(what)s has been added.',
                  what=category_name.split("#")[0]))
        else:
            add_warning(
                req,
                _('Category %(what)s cannot be added.',
                  what=category_name.split("#")[0]))

    def _get_context_data(self, context_id=None):
        """

        Returns dict with keys and values (type means here context.admin_type)::
            combined_context_ids: ids of contexts of type combined but not autocomplete
            not_separate_context_ids: set of ids of contexts of type combined, main, or autocomplete
            autocomplete_context_ids: ids of contexts of type autocomplete
            main_context_id: id of the context with type main, defaults to -1
            context_by_id: context_id => context mapping
            all_contexts: all contexts
            involved_context_ids: ids of contexts for which the categories are needed to be fetched
            context_id: safe context id of the request

        :returns: dict with data
        """
        main_context_id = -1

        combined_context_ids = []
        autocomplete_context_ids = []
        not_separate_context_ids = set([])
        context_by_id = {}
        all_contexts = self.categ.get_contexts()
        context_id = safe_int(context_id)
        project_key = Project.get(self.env).id

        for context in all_contexts:
            context_by_id[context.context_id] = context
            if context.admin_type == context.ADMIN_TYPE_MAIN:
                if main_context_id != -1:
                    self.log.warning(
                        "Invalid category data: Duplicate main contexts")
                    context.admin_type = context.ADMIN_TYPE_COMBINED
                else:
                    main_context_id = context.context_id
                    not_separate_context_ids.add(context.context_id)
            if context.admin_type == context.ADMIN_TYPE_COMBINED:
                combined_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                autocomplete_context_ids.append(context.context_id)
                not_separate_context_ids.add(context.context_id)
        if main_context_id == -1:
            self.log.warning('Invalid category data: Main context not set!')

        involved_context_ids = []
        if context_id:
            if context_id not in context_by_id:
                raise TracError('Invalid context id provided %s' %
                                context_id.__class__)
            if context_id == main_context_id:
                involved_context_ids = [
                    main_context_id
                ] + combined_context_ids + autocomplete_context_ids
            else:
                involved_context_ids = [context_id]
        else:
            involved_context_ids = [
                context.context_id for context in all_contexts
            ]
        data = {
            'project_key': project_key,
            'autocomplete_context_ids': autocomplete_context_ids,
            'combined_context_ids': combined_context_ids,
            'not_separate_context_ids': not_separate_context_ids,
            'main_context_id': main_context_id,
            'context_by_id': context_by_id,
            'all_contexts': all_contexts,
            'involved_context_ids': involved_context_ids,
            'context_id': context_id
        }
        return data

    def _get_categories_data(self, context_data):
        """
        Returns 'categories_per_context' and 'project_categories_per_context',
        both being dictionaries with context_id keys and values, which are
        dictionaries with category_id keys and category values.
        """
        context_by_id = context_data['context_by_id']
        main_context_id = context_data['main_context_id']
        project_key = context_data['project_key']
        categories_per_context = {-1: {}}
        project_categories_per_context = {-1: {}}
        for i_context_id in context_data['involved_context_ids']:
            context = context_by_id[i_context_id]
            if context.admin_type in (context.ADMIN_TYPE_MAIN,
                                      context.ADMIN_TYPE_COMBINED):
                categories_per_context[-1].update(
                    self.categ.get_all_categories_in_context(i_context_id))
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id))
            elif context.admin_type == context.ADMIN_TYPE_AUTOCOMPLETE:
                # categories_per_context[i_context_id] -- don't show anywhere!
                project_categories_per_context[-1].update(
                    self._get_categories_by_project(project_key, i_context_id))
            else:
                categories_per_context[i_context_id] = \
                    self.categ.get_all_categories_in_context(i_context_id)
                project_categories_per_context[i_context_id] = \
                    self._get_categories_by_project(project_key, i_context_id)
        categories_per_context[main_context_id] = categories_per_context[-1]
        project_categories_per_context[
            main_context_id] = project_categories_per_context[-1]

        return categories_per_context, project_categories_per_context

    def _get_categories_by_project(self, project_key, context_id):
        category_dict = {}
        for category in self.categ.get_categories_by_project(
                project_key, context_id):
            category_dict[category.category_id] = category
        return category_dict

    def _get_context_name(self, context_key, all_contexts=None):
        if not all_contexts:
            all_contexts = self.categ.get_contexts()

        for c in all_contexts:
            if c.context_id == int(context_key):
                return c.name
        return ""
 def test_unbind(self):
     dbStub.addResult([])
     c = CQDECategoryStore()
     self.assertTrue(c.unbind_category_project(1, 2))
     self.assertTrue(dbStub.cursors[0].query.lower().startswith("delete from project_categories"))
     self.assertTrue(dbStub.closed)
 def test_unbind_error(self):
     dbStub.addResult([])
     dbStub.setExceptions(True)
     c = CQDECategoryStore()
     self.assertFalse(c.unbind_category_project(1, 2))
     self.assertTrue(dbStub.closed)
Пример #35
0
 def __init__(self):
     self.categ = CQDECategoryStore()
     self.first_context = None
Пример #36
0
    def _get_project_categories(self, project):
        """
        Create list of categories. License, Language and Development status are shown separately
        and are therefore taken out from the category listing.
        """
        # Get api
        cs = CQDECategoryStore()

        # Get categories and specialcase contexts
        categories = cs.get_all_project_categories(project.id)
        contexts = cs.get_contexts()

        combined_categories = []
        separated_categories_per_context = {}

        categories_by_any_context_id = {}
        context_by_id = {}

        # Setup the contexts dicts
        for context in contexts:
            context_by_id[context.context_id] = context
            if context.summary_name:
                # If context has summary name, it is shown as separated in the summary page
                new_list = []
                categories_by_any_context_id[context.context_id] = new_list
                separated_categories_per_context[context.context_id] = new_list
            else:
                categories_by_any_context_id[
                    context.context_id] = combined_categories

        sort_opts = {
            'key': lambda c: c.name,
            'cmp': lambda x, y: cmp(x.lower(), y.lower())
        }

        # Now, categories_by_any_context_id has key for each context id
        for category in categories:
            categories_by_any_context_id[category.context].append(category)
        for context_id in separated_categories_per_context:
            separated_categories_per_context[context_id].sort(**sort_opts)

        combined_categories.sort(**sort_opts)

        # Sort alphabetically, case-insensitively
        context_order = [
            c.context_id
            for c in sorted(map(lambda id: context_by_id[id],
                                separated_categories_per_context.keys()),
                            key=lambda c: c.summary_name)
        ]

        languages = []
        for context_id in separated_categories_per_context:
            context = context_by_id[context_id]
            if (context.summary_name and context.summary_name.lower()
                    in ('language', 'natural language')):
                # Here, we expect that the description contains the language tag.
                # http://www.w3schools.com/tags/att_meta_http_equiv.asp
                # http://www.w3.org/TR/2011/WD-html-markup-20110113/meta.http-equiv.content-language.html
                languages = [
                    c.description
                    for c in separated_categories_per_context[context_id]
                ]

        return (combined_categories, separated_categories_per_context,
                context_by_id, context_order, languages)