Author: andrej
Date: Fri Apr 26 12:17:58 2013
New Revision: 1476162

URL: http://svn.apache.org/r1476162
Log:
adding bhrelation api draft implementation, removing trac-ticket-links 
dependencies

Added:
    bloodhound/trunk/bloodhound_relations/bhrelations/db_default.py
    bloodhound/trunk/bloodhound_relations/bhrelations/model.py
    bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py
Removed:
    bloodhound/trunk/bloodhound_relations/bhrelations/tests/bhrelations_links.py
    bloodhound/trunk/bloodhound_relations/bhrelations/ticket_links_other.py
    bloodhound/trunk/bloodhound_relations/trac_ticket_links/
Modified:
    bloodhound/trunk/.rat-ignore
    bloodhound/trunk/bloodhound_relations/README
    bloodhound/trunk/bloodhound_relations/bhrelations/api.py
    bloodhound/trunk/bloodhound_relations/setup.py

Modified: bloodhound/trunk/.rat-ignore
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/.rat-ignore?rev=1476162&r1=1476161&r2=1476162&view=diff
==============================================================================
--- bloodhound/trunk/.rat-ignore (original)
+++ bloodhound/trunk/.rat-ignore Fri Apr 26 12:17:58 2013
@@ -10,7 +10,6 @@ doc/html-templates/js/jquery-1.8.2.js
 doc/wireframes/src/
 installer/README.rst
 trac/
-bloodhound_relations/bhrelations/trac/
 bloodhound_relations/bhrelations/default-pages/
 bloodhound_multiproduct/tests/admin/*.txt
 bloodhound_multiproduct/tests/*.txt
\ No newline at end of file

Modified: bloodhound/trunk/bloodhound_relations/README
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/README?rev=1476162&r1=1476161&r2=1476162&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/README (original)
+++ bloodhound/trunk/bloodhound_relations/README Fri Apr 26 12:17:58 2013
@@ -28,8 +28,4 @@ If you have any issues, please create a 
 
 == The Trac ticket-links branch
 Bloodhound Relations plugin contains the code from the Trac ticket-links 
branch, which
-is licensed under the same license as Trac 
(http://trac.edgewall.org/wiki/TracLicense).
-The plugin trac_ticket_links directory represents an updated copy of the 
combined vendor branch
-located on 
https://svn.apache.org/repos/asf/bloodhound/vendor/trac-ticket-links.
-The combined vendor branch represents a source tree merged from the several 
original
-vendor branches. For more information on the original vendor branches, see 
//svn.apache.org/repos/asf/bloodhound/vendor/README
+is licensed under the same license as Trac 
(http://trac.edgewall.org/wiki/TracLicense).
\ No newline at end of file

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/api.py?rev=1476162&r1=1476161&r2=1476162&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/api.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/api.py Fri Apr 26 
12:17:58 2013
@@ -17,5 +17,280 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
+from bhrelations import db_default
+from bhrelations.model import Relation
+from multiproduct.env import ProductEnvironment
+from trac.core import Component, implements, TracError
+from trac.env import IEnvironmentSetupParticipant
+from trac.db import DatabaseManager
+from trac.resource import manager_for_neighborhood, ResourceSystem
+from trac.ticket import Ticket
 
+PLUGIN_NAME = 'Bloodhound Relations Plugin'
+
+
+class EnvironmentSetup(Component):
+    implements(IEnvironmentSetupParticipant)
+
+    def environment_created(self):
+        self.found_db_version = 0
+        self.upgrade_environment(self.env.db_transaction)
+
+    def environment_needs_upgrade(self, db):
+        """Detects if the installed db version matches the running system"""
+        db_installed_version = self._get_version()
+
+        db_version = db_default.DB_VERSION
+        if db_installed_version > db_version:
+            raise TracError('''Current db version (%d) newer than supported by
+            this version of the %s (%d).''' % (db_installed_version,
+                                               PLUGIN_NAME,
+                                               db_version))
+        needs_upgrade = db_installed_version < db_version
+        return needs_upgrade
+
+    def upgrade_environment(self, db):
+        self.log.debug("upgrading existing environment for %s plugin." %
+                       PLUGIN_NAME)
+        db_installed_version = self._get_version()
+        with self.env.db_direct_transaction as db:
+            if db_installed_version < 1:
+                self._initialize_db(db)
+                self._update_db_version(db, 1)
+            #add upgrade logic later if needed
+
+    def _get_version(self):
+        """Finds the current version of the bloodhound database schema"""
+        rows = self.env.db_direct_query("""
+            SELECT value FROM system WHERE name = %s
+            """, (db_default.DB_SYSTEM_KEY,))
+        return int(rows[0][0]) if rows else -1
+
+    def _update_db_version(self, db, version):
+        old_version = self._get_version()
+        if old_version != -1:
+            self.log.info(
+                "Updating %s database schema from version %d to %d",
+                PLUGIN_NAME, old_version, version)
+            db("""UPDATE system SET value=%s
+                      WHERE name=%s""", (version, db_default.DB_SYSTEM_KEY))
+        else:
+            self.log.info(
+                "Initial %s database schema set to version %d",
+                PLUGIN_NAME, version)
+            db("""
+                INSERT INTO system (name, value) VALUES ('%s','%s')
+                """  % (db_default.DB_SYSTEM_KEY, version))
+        return version
+
+    def _initialize_db(self, db):
+        self.log.debug("creating initial db schema for %s.", PLUGIN_NAME)
+        db_connector, dummy = DatabaseManager(self.env)._get_connector()
+        for table in db_default.SCHEMA:
+            for statement in db_connector.to_sql(table):
+                db(statement)
+
+
+class RelationsSystem(Component):
+
+    RELATIONS_CONFIG_NAME = 'bhrelations_links'
+    RESOURCE_ID_DELIMITER = u":"
+    RELATION_ID_DELIMITER = u","
+
+    def __init__(self):
+        self._links, self._labels, \
+        self._validators, self._blockers, \
+        self._copy_fields = self._get_links_config()
+
+        self.link_ends_map = {}
+        for end1, end2 in self.get_ends():
+            self.link_ends_map[end1] = end2
+            if end2 is not None:
+                self.link_ends_map[end2] = end1
+
+    def get_ends(self):
+        return self._links
+
+    def add_relation(
+            self,
+            source_resource_instance,
+            destination_resource_instance,
+            relation_type,
+            comment = None,
+            ):
+        source = self.get_resource_id(source_resource_instance)
+        destination = self.get_resource_id(destination_resource_instance)
+        relation = Relation(self.env)
+        relation.source = source
+        relation.destination = destination
+        relation.type = relation_type
+        relation.comment = comment
+        self._add_relation_instance(relation)
+
+    def _add_relation_instance(self, relation):
+        #TBD: add changes in source and destination ticket history
+        with self.env.db_transaction:
+            relation.insert()
+            other_end = self.link_ends_map[relation.type]
+            if other_end:
+                reverted_relation = relation.clone_reverted(other_end)
+                reverted_relation.insert()
+
+    def delete_relation_by_id(
+            self,
+            relation_id,
+        ):
+        source, destination, relation_type = self._parse_relation_id(
+            relation_id)
+        #TODO: some optimization can be introduced here to not load relations
+        #before actual DELETE SQL
+        relation = Relation(self.env, keys=dict(
+            source=source,
+            destination=destination,
+            type=relation_type
+            ))
+        self._delete_relation_instance(relation)
+
+    def _delete_relation_instance(self, relation):
+        source = relation.source
+        destination = relation.destination
+        relation_type = relation.type
+        with self.env.db_transaction:
+            relation.delete()
+            other_end = self.link_ends_map[relation_type]
+            if other_end:
+                reverted_relation = Relation(self.env, keys=dict(
+                    source=destination,
+                    destination=source,
+                    type=other_end,
+                ))
+                reverted_relation.delete()
+
+    def _debug_select(self):
+        """The method is used for debug purposes"""
+        sql = "SELECT id, source, destination, type FROM bloodhound_relations"
+        with self.env.db_query as db:
+            return [db(sql)]
+
+    def get_relations_by_resource(self, resource):
+        source = self.get_resource_id(resource)
+        return self.get_relations_by_resource_id(source)
+
+    def get_relations_by_resource_id(self, resource):
+        #todo: add optional paging for possible umbrella tickets with
+        #a lot of child tickets
+        source = self.get_resource_id(resource)
+        return Relation.select(
+            self.env,
+            where=dict(source=source),
+            order_by=["type", "destination"]
+            )
+
+    def get_relations(self, resource_instance):
+        source = self.get_resource_id(resource_instance)
+        relations =  Relation.select(self.env, where=dict(source=source))
+        relation_list = []
+        for relation in relations:
+            relation_list.append(dict(
+                relation_id = self._create_relation_id(relation),
+                destination_id = relation.destination,
+                destination=self.get_resource_by_id(relation.destination),
+                type = relation.type,
+                comment = relation.comment
+            ))
+        return relation_list
+
+    def _create_relation_id(self, relation):
+        return self.RELATION_ID_DELIMITER.join((
+            relation.source,
+            relation.destination,
+            relation.type))
+
+    def _parse_relation_id(self, relation_id):
+        source, destination, relation_type = relation_id.split(
+            self.RELATION_ID_DELIMITER)
+        return source, destination, relation_type
+
+    # Copied from trac/ticket/links.py, ticket-links-trunk branch
+    def _get_links_config(self):
+        links = []
+        labels = {}
+        validators = {}
+        blockers = {}
+        copy_fields = {}
+
+        config = self.config[self.RELATIONS_CONFIG_NAME]
+        for name in [option for option, _ in config.options()
+                     if '.' not in option]:
+            ends = [e.strip() for e in config.get(name).split(',')]
+            if not ends:
+                continue
+            end1 = ends[0]
+            end2 = None
+            if len(ends) > 1:
+                end2 = ends[1]
+            links.append((end1, end2))
+
+            label1 = config.get(end1 + '.label') or end1.capitalize()
+            labels[end1] = label1
+            if end2:
+                label2 = config.get(end2 + '.label') or end2.capitalize()
+                labels[end2] = label2
+
+            validator = config.get(name + '.validator')
+            if validator:
+                validators[end1] = validator
+                if end2:
+                    validators[end2] = validator
+
+            blockers[end1] = config.getbool(end1 + '.blocks', default=False)
+            if end2:
+                blockers[end2] = config.getbool(end2 + '.blocks', 
default=False)
+
+            # <end>.copy_fields may be absent or intentionally set empty.
+            # config.getlist() will return [] in either case, so check that
+            # the key is present before assigning the value
+            for end in [end1, end2]:
+                if end:
+                    cf_key = '%s.copy_fields' % end
+                    if cf_key in config:
+                        copy_fields[end] = config.getlist(cf_key)
+
+        return links, labels, validators, blockers, copy_fields
+
+    def get_resource_id(self, resource_instance):
+        resource = resource_instance.resource
+        rsys = ResourceSystem(manager_for_neighborhood(
+            self.env, resource.neighborhood))
+        nbhprefix = rsys.neighborhood_prefix(resource.neighborhood)
+        resource_full_id = self.RESOURCE_ID_DELIMITER.join(
+            (nbhprefix, resource.realm, unicode(resource.id))
+        )
+        return resource_full_id
+
+    def get_resource_by_id(self, resource_full_id):
+        """
+        Expects resource_full_id in format "product:ticket:123". In case of
+        global environment: ":ticket:123"
+        """
+        nbhprefix, realm, id = resource_full_id.split(
+            self.RESOURCE_ID_DELIMITER)
+        return self._get_resource_instance(nbhprefix, realm, id)
+
+    def _get_resource_instance(self, nbhprefix, realm, id):
+        env = self._get_env_by_prefix(nbhprefix)
+        if realm == "ticket":
+            return Ticket(env, id)
+        else:
+            raise TracError("Resource type %s is not supported by " +
+                            "Bloodhound Relations" % realm)
+
+    def _get_env_by_prefix(self, nbhprefix):
+        if nbhprefix:
+            env = ProductEnvironment(nbhprefix)
+        elif hasattr(self.env, "parent") and self.env.parent:
+            env = self.env.parent
+        else:
+            env = self.env
+        return env
 

Added: bloodhound/trunk/bloodhound_relations/bhrelations/db_default.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/db_default.py?rev=1476162&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/db_default.py (added)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/db_default.py Fri Apr 26 
12:17:58 2013
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+from bhrelations.model import Relation
+
+DB_SYSTEM_KEY = 'bhrelations'
+DB_VERSION = 1
+
+SCHEMA = [mcls._get_schema() for mcls in (Relation, )]
+
+migrations = [
+]
\ No newline at end of file

Added: bloodhound/trunk/bloodhound_relations/bhrelations/model.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/model.py?rev=1476162&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/model.py (added)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/model.py Fri Apr 26 
12:17:58 2013
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+from bhdashboard.model import ModelBase
+from trac.resource import Resource
+
+
+class Relation(ModelBase):
+    """The Relation table"""
+    _meta = {'table_name':'bloodhound_relations',
+            'object_name':'Relation',
+            'key_fields':['source', 'destination', 'type'],
+            'non_key_fields':['comment'],
+            'no_change_fields':['source', 'destination', 'type'],
+            'unique_fields':[],
+            # 'auto_inc_fields': ['id'],
+            }
+
+    # _meta = {'table_name':'bloodhound_relations',
+    #         'object_name':'Relation',
+    #         'key_fields':['id'],
+    #         'non_key_fields':['source', 'destination', 'type', 'comment'],
+    #         'no_change_fields':['source', 'destination', 'type'],
+    #         'unique_fields':['source', 'destination', 'type'],
+    #         'auto_inc_fields': ['id'],
+    #         }
+
+    @property
+    def resource(self):
+        """Allow Relation to be treated as a Resource"""
+        return Resource('relation', self.prefix)
+
+    def clone_reverted(self, type):
+        relation = Relation(self._env)
+        relation.source = self.destination
+        relation.destination = self.source
+        relation.comment = self.comment
+        relation.type = type
+        return relation
+

Added: bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py?rev=1476162&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py (added)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/tests/api.py Fri Apr 26 
12:17:58 2013
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+from _sqlite3 import OperationalError, IntegrityError
+from bhrelations.api import EnvironmentSetup, RelationsSystem
+from trac.ticket.model import Ticket
+from trac.test import EnvironmentStub, Mock
+from trac.core import TracError
+from trac.util.datefmt import utc
+import unittest
+
+try:
+    from babel import Locale
+    locale_en = Locale.parse('en_US')
+except ImportError:
+    locale_en = None
+
+class ApiTestCase(unittest.TestCase):
+    def setUp(self):
+        self.env = EnvironmentStub(
+            default_data=True,
+            enable=['trac.*', 'bhrelations.*']
+        )
+        config_name = RelationsSystem.RELATIONS_CONFIG_NAME
+        self.env.config.set(config_name, 'dependency', 'dependson,dependent')
+        self.env.config.set(config_name, 'dependency.validator', 'no_cycle')
+        self.env.config.set(config_name, 'parent_children','parent,children')
+        self.env.config.set(config_name, 'parent_children.validator',
+                                                            'parent_child')
+        self.env.config.set(config_name, 'children.blocks', 'true')
+        self.env.config.set(config_name, 'children.label', 'Overridden')
+        self.env.config.set(config_name, 'parent.copy_fields',
+                                                            'summary, foo')
+        self.env.config.set(config_name, 'oneway', 'refersto')
+        self.req = Mock(href=self.env.href, authname='anonymous', tz=utc,
+                        args=dict(action='dummy'),
+                        locale=locale_en, lc_time=locale_en)
+        self.relations_system = RelationsSystem(self.env)
+        self._upgrade_env()
+
+    def tearDown(self):
+        # shutil.rmtree(self.env.path)
+        self.env.reset_db()
+
+    def _upgrade_env(self):
+        environment_setup = EnvironmentSetup(self.env)
+        try:
+            environment_setup.upgrade_environment(self.env.db_transaction)
+        except OperationalError:
+            # table remains but database version is deleted
+            pass
+
+    def _insert_ticket(self, summary, **kw):
+        """Helper for inserting a ticket into the database"""
+        ticket = Ticket(self.env)
+        for k,v in kw.items():
+            ticket[k] = v
+        return ticket.insert()
+
+    def _insert_and_load_ticket(self, summary, **kw):
+        return Ticket(self.env, self._insert_ticket(summary, **kw))
+
+    def test_can_add_two_ways_relations(self):
+        #arrange
+        ticket = self._insert_and_load_ticket("A1")
+        dependent = self._insert_and_load_ticket("A2")
+        #act
+        relations_system = self.relations_system
+        relations_system.add_relation(
+            ticket, dependent, "dependent")
+        #assert
+        relations = relations_system.get_relations(ticket)
+        self.assertEqual("dependent", relations[0]["type"])
+        self.assertEqual(dependent.id, relations[0]["destination"].id)
+
+        relations = relations_system.get_relations(dependent)
+        self.assertEqual("dependson", relations[0]["type"])
+        self.assertEqual(ticket.id, relations[0]["destination"].id)
+
+    def test_can_add_single_way_relations(self):
+        #arrange
+        ticket = self._insert_and_load_ticket("A1")
+        referred = self._insert_and_load_ticket("A2")
+        #act
+        relations_system = self.relations_system
+        relations_system.add_relation(ticket, referred, "refersto")
+        #assert
+        relations = relations_system.get_relations(ticket)
+        self.assertEqual("refersto", relations[0]["type"])
+        self.assertEqual(referred.id, relations[0]["destination"].id)
+
+        relations = relations_system.get_relations(referred)
+        self.assertEqual(0, len(relations))
+
+    def test_can_add_multiple_relations(self):
+        #arrange
+        ticket = self._insert_and_load_ticket("A1")
+        dependent1 = self._insert_and_load_ticket("A2")
+        dependent2 = self._insert_and_load_ticket("A3")
+        #act
+        relations_system = self.relations_system
+        relations_system.add_relation(
+            ticket, dependent1, "dependent")
+        relations_system.add_relation(
+            ticket, dependent2, "dependent")
+        #assert
+        relations = relations_system.get_relations(ticket)
+        self.assertEqual(2, len(relations))
+
+    def test_will_not_create_more_than_one_identical_relations(self):
+        #arrange
+        ticket = self._insert_and_load_ticket("A1")
+        dependent1 = self._insert_and_load_ticket("A2")
+        #act
+        relations_system = self.relations_system
+        relations_system.add_relation(
+            ticket, dependent1, "dependent")
+        self.assertRaisesRegexp(
+            TracError,
+            "already exists",
+            relations_system.add_relation,
+            ticket, dependent1, "dependent")
+
+    def test_will_not_create_more_than_one_identical_relations_db_level(self):
+        sql = """INSERT INTO bloodhound_relations (source, destination, type)
+                    VALUES (%s, %s, %s)"""
+        with self.env.db_transaction as db:
+            db(sql, ["1", "2", "dependson"])
+            self.assertRaises(
+                IntegrityError, db, sql, ["1", "2", "dependson"])
+
+    def test_can_add_one_way_relations(self):
+        #arrange
+        ticket = self._insert_and_load_ticket("A1")
+        referred_ticket = self._insert_and_load_ticket("A2")
+        #act
+        relations_system = self.relations_system
+        relations_system.add_relation(
+            ticket, referred_ticket, "refersto")
+        #assert
+        relations = relations_system.get_relations(ticket)
+        self.assertEqual("refersto", relations[0]["type"])
+        self.assertEqual(referred_ticket.id, relations[0]["destination"].id)
+
+        relations = relations_system.get_relations(referred_ticket)
+        self.assertEqual(0, len(relations))
+
+    def test_can_delete_two_ways_relation(self):
+        #arrange
+        ticket = self._insert_and_load_ticket("A1")
+        dependent_ticket = self._insert_and_load_ticket("A2")
+        relations_system = self.relations_system
+        relations_system.add_relation(
+            ticket, dependent_ticket, "dependson")
+        relations = relations_system.get_relations(ticket)
+        self.assertEqual(1, len(relations))
+        #act
+        relation_to_delete = relations[0]
+        
relations_system.delete_relation_by_id(relation_to_delete["relation_id"])
+        #assert
+        relations = relations_system.get_relations(ticket)
+        self.assertEqual(0, len(relations))
+
+    def test_can_delete_single_way_relation(self):
+        #arrange
+        ticket = self._insert_and_load_ticket("A1")
+        referred = self._insert_and_load_ticket("A2")
+        #act
+        relations_system = self.relations_system
+        relations_system.add_relation(ticket, referred, "refersto")
+
+
+        ticket = self._insert_and_load_ticket("A1")
+        dependent_ticket = self._insert_and_load_ticket("A2")
+        relations_system = self.relations_system
+        relations_system.add_relation(
+            ticket, dependent_ticket, "dependson")
+        relations = relations_system.get_relations(ticket)
+
+        self.assertEqual(1, len(relations))
+        reverted_relations = relations_system.get_relations(dependent_ticket)
+        self.assertEqual(1, len(reverted_relations))
+        #act
+        # self._debug_select()
+        relation_to_delete = relations[0]
+        
relations_system.delete_relation_by_id(relation_to_delete["relation_id"])
+        #assert
+        relations = relations_system.get_relations(ticket)
+        self.assertEqual(0, len(relations))
+        reverted_relations = relations_system.get_relations(dependent_ticket)
+        self.assertEqual(0, len(reverted_relations))
+
+    # def _find_relation(self, relations, destination, relation_type):
+    #     destination_id = self.relations_system.get_resource_id(destination)
+    #     for relation in relations:
+    #         if relation["destination_id"] == destination_id and \
+    #             relation["type"] == relation_type:
+    #             return relation
+    #     raise Exception("Relation was not found for destination_id: %s,"+
+    #                     " relation_type: %s" % (destination_id, 
relation_type))
+
+    def _debug_select(self):
+        # sql = "SELECT source, destination, type FROM bloodhound_relations"
+        print " id, source, destination, type"
+        sql = "SELECT id, source, destination, type FROM bloodhound_relations"
+        with self.env.db_query as db:
+            # for row in db(sql, ("source", "destination", "type")):
+            for row in db(sql):
+                print row

Modified: bloodhound/trunk/bloodhound_relations/setup.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/setup.py?rev=1476162&r1=1476161&r2=1476162&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/setup.py (original)
+++ bloodhound/trunk/bloodhound_relations/setup.py Fri Apr 26 12:17:58 2013
@@ -99,10 +99,6 @@ PKG_INFO = {'bhrelations': ('bhrelations
                           ),
             'bhrelations.tests': (
                 'bhrelations/tests', ['data/*.*']),
-            'bhrelations.trac.ticket': (
-                'bhrelations/trac/ticket', []),
-            'bhrelations.trac.ticket.tests' : (
-                'bhrelations/trac/ticket/tests', []),
             }
 
 ENTRY_POINTS = {


Reply via email to