예제 #1
0
    def resolve_comments(self, session, event):

        # Sanity check. If there's no comment_id, exit.
        if 'comment_id' not in event:
            return None

        # Retrieve the content of the comment.
        comment = db_api.entity_get(models.Comment,
                                    event['comment_id'],
                                    session=session)
        if not comment:
            return None

        # Retrieve the timeline events.
        timeline_event = session.query(models.TimeLineEvent) \
            .filter(models.TimeLineEvent.comment_id == event['comment_id']) \
            .first()
        if not timeline_event:
            return None

        # Retrieve the story from the timeline event.
        story = db_api.entity_get(models.Story,
                                  timeline_event.story_id,
                                  session=session)
        if not story:
            return None

        # Construct and return the comment's event_info object.
        return {
            'comment_id': comment.id,
            'comment_content': comment.content,
            'story_id': story.id,
            'story_title': story.title
        }
예제 #2
0
    def resolve_comments(self, session, event):

        # Sanity check. If there's no comment_id, exit.
        if 'comment_id' not in event:
            return None

        # Retrieve the content of the comment.
        comment = db_api.entity_get(models.Comment,
                                    event.comment_id,
                                    session=session)
        if not comment:
            return None

        # Retrieve the timeline events.
        timeline_event = session.query(models.TimeLineEvent) \
            .filter(models.TimeLineEvent.comment_id == event.comment_id) \
            .first()
        if not timeline_event:
            return None

        # Retrieve the story from the timeline event.
        story = db_api.entity_get(models.Story,
                                  timeline_event.story_id,
                                  session=session)
        if not story:
            return None

        # Construct and return the comment's event_info object.
        return {
            'comment_id': comment.id,
            'comment_content': comment.content,
            'story_id': story.id,
            'story_title': story.title
        }
예제 #3
0
def update_filter(filter_id, update):
    old_filter = api_base.entity_get(models.WorklistFilter, filter_id)
    if 'filter_criteria' in update:
        # Change the criteria for this filter. If an ID is provided, change
        # the criterion to match the provided criterion. If no ID is provided,
        # create a new criterion and add it to the filter.
        for criterion in update['filter_criteria']:
            criterion_dict = criterion.as_dict(omit_unset=True)
            if 'id' in criterion_dict:
                id = criterion_dict.pop('id')
                api_base.entity_update(models.FilterCriterion,
                                       id, criterion_dict)
            else:
                created = api_base.entity_create(models.FilterCriterion,
                                                 criterion_dict)
                criterion.id = created
                old_filter.criteria.append(created)

        # Remove criteria which aren't in the provided set
        new_ids = [criterion.id for criterion in update['filter_criteria']]
        for criterion in old_filter.criteria:
            if criterion.id not in new_ids:
                old_filter.criteria.remove(criterion)
        del update['filter_criteria']

    return api_base.entity_update(models.WorklistFilter, filter_id, update)
예제 #4
0
    def test_handle(self):
        """Assert that the handle method passes the correct values on."""
        worker_base = MockEmailWorkerBase({})

        with base.HybridSessionManager():
            session = db_api_base.get_session()
            user_1 = db_api_base.entity_get(models.User, 1, session=session)

            worker_base.handle(session=session,
                               author=user_1,
                               method='POST',
                               url='http://localhost/',
                               path='/test',
                               query_string='',
                               status=201,
                               resource='story',
                               resource_id=1)

            self.assertIsInstance(worker_base.handled_values['author'],
                                  models.User)
            self.assertEqual(1, worker_base.handled_values['author'].id)

            self.assertEqual(2, len(worker_base.handled_values['subscribers']))
            self.assertEqual('POST', worker_base.handled_values['method'])
            self.assertEqual(201, worker_base.handled_values['status'])
            self.assertEqual('/test', worker_base.handled_values['path'])
            self.assertEqual('story', worker_base.handled_values['resource'])
            self.assertEqual(1, worker_base.handled_values['resource_id'])
예제 #5
0
    def handle_timeline_events(self, session, resource, author, subscribers):

        for user_id in subscribers:
            user = db_api.entity_get(models.User, user_id, session=session)
            send_notification = get_preference(
                'receive_notifications_worklists', user)
            if (send_notification != 'true'
                    and resource.get('worklist_id') is not None):
                continue

            if resource['event_type'] == 'user_comment':
                event_info = json.dumps(
                    self.resolve_comments(session=session, event=resource))

            else:
                event_info = resource['event_info']

            # Don't send a notification if the user isn't allowed to see the
            # thing this event is about.
            event = events_api.event_get(resource['id'],
                                         current_user=user_id,
                                         session=session)
            if not events_api.is_visible(event, user_id, session=session):
                continue

            db_api.entity_create(models.SubscriptionEvents, {
                "author_id": author.id,
                "subscriber_id": user_id,
                "event_type": resource['event_type'],
                "event_info": event_info
            },
                                 session=session)
예제 #6
0
def user_get(user_id, filter_non_public=False, session=None):
    entity = api_base.entity_get(models.User,
                                 user_id,
                                 filter_non_public=filter_non_public,
                                 session=session)

    return entity
예제 #7
0
    def test_handle(self):
        """Assert that the handle method passes the correct values on."""
        worker_base = MockEmailWorkerBase({})

        with base.HybridSessionManager():
            session = db_api_base.get_session()
            user_1 = db_api_base.entity_get(models.User, 1, session=session)

            worker_base.handle(session=session,
                               author=user_1,
                               method='POST',
                               url='http://localhost/',
                               path='/test',
                               query_string='',
                               status=201,
                               resource='story',
                               resource_id=1)

            self.assertIsInstance(worker_base.handled_values['author'],
                                  models.User)
            self.assertEqual(1, worker_base.handled_values['author'].id)

            self.assertEqual(2, len(worker_base.handled_values['subscribers']))
            self.assertEqual('POST', worker_base.handled_values['method'])
            self.assertEqual(201, worker_base.handled_values['status'])
            self.assertEqual('/test', worker_base.handled_values['path'])
            self.assertEqual('story', worker_base.handled_values['resource'])
            self.assertEqual(1, worker_base.handled_values['resource_id'])
예제 #8
0
    def handle_timeline_events(self, session, resource, author, subscribers):

        for user_id in subscribers:
            user = db_api.entity_get(models.User, user_id, session=session)
            send_notification = get_preference(
                'receive_notifications_worklists', user)
            if (send_notification != 'true' and
                    resource.get('worklist_id') is not None):
                continue

            if resource['event_type'] == 'user_comment':
                event_info = json.dumps(
                    self.resolve_comments(session=session, event=resource)
                )

            else:
                event_info = resource['event_info']

            # Don't send a notification if the user isn't allowed to see the
            # thing this event is about.
            event = events_api.event_get(
                resource['id'], current_user=user_id, session=session)
            if not events_api.is_visible(event, user_id, session=session):
                continue

            db_api.entity_create(models.SubscriptionEvents, {
                "author_id": author.id,
                "subscriber_id": user_id,
                "event_type": resource['event_type'],
                "event_info": event_info
            }, session=session)
예제 #9
0
def comment_update(comment_id, values):
    comment = api_base.entity_get(models.Comment, comment_id)
    old_dict = {
        'comment_id': comment_id,
        'content': comment.content
    }
    api_base.entity_create(models.HistoricalComment, old_dict)
    return api_base.entity_update(models.Comment, comment_id, values)
예제 #10
0
def subscription_get_resource(target_type, target_id, current_user=None):
    if target_type not in SUPPORTED_TYPES:
        return None
    if target_type == 'story':
        return stories_api.story_get(target_id, current_user=current_user)
    elif target_type == 'task':
        return tasks_api.task_get(target_id, current_user=current_user)

    return api_base.entity_get(SUPPORTED_TYPES[target_type], target_id)
예제 #11
0
def subscription_get_resource(target_type, target_id, current_user=None):
    if target_type not in SUPPORTED_TYPES:
        return None
    if target_type == 'story':
        return stories_api.story_get(target_id, current_user=current_user)
    elif target_type == 'task':
        return tasks_api.task_get(target_id, current_user=current_user)

    return api_base.entity_get(SUPPORTED_TYPES[target_type], target_id)
예제 #12
0
    def _apply_pagination(self, model_cls, query, marker=None, limit=None):

        marker_entity = None
        if marker:
            marker_entity = api_base.entity_get(model_cls, marker, True)

        return api_base.paginate_query(query=query,
                                       model=model_cls,
                                       limit=limit,
                                       sort_key="id",
                                       marker=marker_entity)
예제 #13
0
    def test_handle_email(self, get_smtp_client):
        """Make sure that events from the queue are sent as emails."""
        dummy_smtp = mock.Mock(smtplib.SMTP)
        worker_base = SubscriptionEmailWorker({})
        get_smtp_client.return_value.__enter__ = dummy_smtp

        with base.HybridSessionManager():
            session = db_api_base.get_session()
            author = db_api_base.entity_get(models.User, 2, session=session)
            story = db_api_base.entity_get(models.Story, 1, session=session)
            story_dict = story.as_dict()
            story_after_dict = copy.copy(story_dict)
            story_after_dict['title'] = 'New Test Title'

            subscribers = worker_base.get_subscribers(session, 'story', 1)
            self.assertEqual(2, len(subscribers))

            worker_base.handle_email(session=session,
                                     author=author,
                                     subscribers=subscribers,
                                     method='PUT',
                                     url='http://localhost/',
                                     path='/stories/1',
                                     query_string='',
                                     status=200,
                                     resource='story',
                                     resource_id=1,
                                     resource_before=story_dict,
                                     resource_after=story_after_dict)
            # There should be two subscribers, but only one should get an
            # email since the other is a digest receiver.

            subscribed_user = db_api_base.entity_get(models.User,
                                                     3,
                                                     session=session)
            self.assertEqual(dummy_smtp.return_value.sendmail.call_count, 1)
            self.assertEqual(
                dummy_smtp.return_value.sendmail.call_args[1]['to_addrs'],
                subscribed_user.email)
예제 #14
0
    def test_get_preference(self):
        """Assert that the get_preference method functions as expected."""
        worker_base = MockEmailWorkerBase({})

        with base.HybridSessionManager():
            session = db_api_base.get_session()
            user_1 = db_api_base.entity_get(models.User, 1, session=session)

            foo_value = worker_base.get_preference('foo', user_1)
            self.assertEqual('bar', foo_value)

            no_value = worker_base.get_preference('no_value', user_1)
            self.assertIsNone(no_value)
예제 #15
0
def create_filter(worklist_id, filter_dict):
    criteria = filter_dict.pop('filter_criteria')
    filter_dict['list_id'] = worklist_id
    filter = api_base.entity_create(models.WorklistFilter, filter_dict)
    filter = api_base.entity_get(models.WorklistFilter, filter.id)
    filter.criteria = []
    for criterion in criteria:
        criterion_dict = criterion.as_dict()
        criterion_dict['filter_id'] = filter.id
        filter.criteria.append(
            api_base.entity_create(models.FilterCriterion, criterion_dict))

    return filter
예제 #16
0
    def _apply_pagination(self, model_cls, query, marker=None,
                          offset=None, limit=None):

        marker_entity = None
        if marker:
            marker_entity = api_base.entity_get(model_cls, marker, True)

        return api_base.paginate_query(query=query,
                                       model=model_cls,
                                       limit=limit,
                                       sort_key="id",
                                       marker=marker_entity,
                                       offset=offset)
예제 #17
0
    def test_get_preference(self):
        """Assert that the get_preference method functions as expected."""
        worker_base = MockEmailWorkerBase({})

        with base.HybridSessionManager():
            session = db_api_base.get_session()
            user_1 = db_api_base.entity_get(models.User, 1, session=session)

            foo_value = worker_base.get_preference('foo', user_1)
            self.assertEqual('bar', foo_value)

            no_value = worker_base.get_preference('no_value', user_1)
            self.assertIsNone(no_value)
예제 #18
0
def create_filter(worklist_id, filter_dict):
    criteria = filter_dict.pop('filter_criteria')
    filter_dict['list_id'] = worklist_id
    filter = api_base.entity_create(models.WorklistFilter, filter_dict)
    filter = api_base.entity_get(models.WorklistFilter, filter.id)
    filter.criteria = []
    for criterion in criteria:
        criterion_dict = criterion.as_dict()
        criterion_dict['filter_id'] = filter.id
        filter.criteria.append(
            api_base.entity_create(models.FilterCriterion, criterion_dict))

    return filter
예제 #19
0
    def test_handle_email(self, get_smtp_client):
        """Make sure that events from the queue are sent as emails."""
        dummy_smtp = mock.Mock(smtplib.SMTP)
        worker_base = SubscriptionEmailWorker({})
        get_smtp_client.return_value.__enter__ = dummy_smtp

        with base.HybridSessionManager():
            session = db_api_base.get_session()
            author = db_api_base.entity_get(models.User, 2, session=session)
            story = db_api_base.entity_get(models.Story, 1, session=session)
            story_dict = story.as_dict()
            story_after_dict = copy.copy(story_dict)
            story_after_dict['title'] = 'New Test Title'

            subscribers = worker_base.get_subscribers(session, 'story', 1)
            self.assertEqual(2, len(subscribers))

            worker_base.handle_email(session=session,
                                     author=author,
                                     subscribers=subscribers,
                                     method='PUT',
                                     url='http://localhost/',
                                     path='/stories/1',
                                     query_string='',
                                     status=200,
                                     resource='story',
                                     resource_id=1,
                                     resource_before=story_dict,
                                     resource_after=story_after_dict)
            # There should be two subscribers, but only one should get an
            # email since the other is a digest receiver.

            subscribed_user = db_api_base.entity_get(models.User, 3,
                                                     session=session)
            self.assertEqual(dummy_smtp.return_value.sendmail.call_count, 1)
            self.assertEqual(
                dummy_smtp.return_value.sendmail.call_args[1]['to_addrs'],
                subscribed_user.email)
예제 #20
0
def subscription_get_resource(target_type, target_id, current_user=None):
    if target_type not in SUPPORTED_TYPES:
        return None
    if target_type == 'story':
        return stories_api.story_get(target_id, current_user=current_user)
    elif target_type == 'task':
        return tasks_api.task_get(target_id, current_user=current_user)
    elif target_type == 'worklist':
        worklist = worklists_api.get(target_id)
        if worklists_api.visible(worklist, current_user):
            return worklist
        return None

    return api_base.entity_get(SUPPORTED_TYPES[target_type], target_id)
예제 #21
0
def subscription_get_resource(target_type, target_id, current_user=None):
    if target_type not in SUPPORTED_TYPES:
        return None
    if target_type == 'story':
        return stories_api.story_get(target_id, current_user=current_user)
    elif target_type == 'task':
        return tasks_api.task_get(target_id, current_user=current_user)
    elif target_type == 'worklist':
        worklist = worklists_api.get(target_id)
        if worklists_api.visible(worklist, current_user):
            return worklist
        return None

    return api_base.entity_get(SUPPORTED_TYPES[target_type], target_id)
예제 #22
0
def update_filter(filter_id, update):
    old_filter = api_base.entity_get(models.WorklistFilter, filter_id)
    if 'filter_criteria' in update:
        new_ids = [criterion.id for criterion in update['filter_criteria']]
        for criterion in update['filter_criteria']:
            criterion_dict = criterion.as_dict(omit_unset=True)
            if 'id' in criterion_dict:
                existing = api_base.entity_get(models.FilterCriterion,
                                               criterion['id'])
                if existing.as_dict() != criterion_dict:
                    api_base.entity_update(models.FilterCriterion,
                                           criterion_dict['id'],
                                           criterion_dict)
            else:
                created = api_base.entity_create(models.FilterCriterion,
                                                 criterion_dict)
                old_filter.criteria.append(created)
        for criterion in old_filter.criteria:
            if criterion.id not in new_ids:
                old_filter.criteria.remove(criterion)
        del update['filter_criteria']

    return api_base.entity_update(models.WorklistFilter, filter_id, update)
예제 #23
0
    def get_original_resource(self, resource, resource_id):
        """Given a resource name and ID, will load that resource and map it
        to a JSON object.
        """
        if not resource or not resource_id or resource not in class_mappings.keys():
            return None

        model_class, wmodel_class = class_mappings[resource]
        entity = api_base.entity_get(model_class, resource_id)
        if entity:
            return tojson(wmodel_class, wmodel_class.from_db_model(entity))
        else:
            # In the case of a DELETE, the entity will be returned as None
            return None
예제 #24
0
def update_filter(filter_id, update):
    old_filter = api_base.entity_get(models.WorklistFilter, filter_id)
    if 'filter_criteria' in update:
        new_ids = [criterion.id for criterion in update['filter_criteria']]
        for criterion in update['filter_criteria']:
            criterion_dict = criterion.as_dict(omit_unset=True)
            if 'id' in criterion_dict:
                existing = api_base.entity_get(models.FilterCriterion,
                                               criterion['id'])
                if existing.as_dict() != criterion_dict:
                    api_base.entity_update(models.FilterCriterion,
                                           criterion_dict['id'],
                                           criterion_dict)
            else:
                created = api_base.entity_create(models.FilterCriterion,
                                                 criterion_dict)
                old_filter.criteria.append(created)
        for criterion in old_filter.criteria:
            if criterion.id not in new_ids:
                old_filter.criteria.remove(criterion)
        del update['filter_criteria']

    return api_base.entity_update(models.WorklistFilter, filter_id, update)
예제 #25
0
    def get_original_resource(self, resource, resource_id):
        """Given a resource name and ID, will load that resource and map it
        to a JSON object.
        """
        if not resource or not resource_id or resource not in \
                class_mappings.keys():
            return None

        model_class, wmodel_class = class_mappings[resource]
        entity = api_base.entity_get(model_class, resource_id)
        if entity:
            return tojson(wmodel_class, wmodel_class.from_db_model(entity))
        else:
            # In the case of a DELETE, the entity will be returned as None
            return None
예제 #26
0
    def _apply_pagination(self, model_cls, query, marker=None,
                          offset=None, limit=None, sort_field='id',
                          sort_dir='asc'):
        if not sort_field:
            sort_field = 'id'
        if not sort_dir:
            sort_dir = 'asc'

        marker_entity = None
        if marker:
            marker_entity = api_base.entity_get(model_cls, marker, True)

        return api_base.paginate_query(query=query,
                                       model=model_cls,
                                       limit=limit,
                                       sort_key=sort_field,
                                       marker=marker_entity,
                                       offset=offset,
                                       sort_dir=sort_dir)
예제 #27
0
    def handle(self, session, author, method, url, path, query_string, status,
               resource, resource_id, sub_resource=None, sub_resource_id=None,
               resource_before=None, resource_after=None):
        """This worker handles API events and attempts to determine whether
        they correspond to user subscriptions.

        :param session: An event-specific SQLAlchemy session.
        :param author: The author's user record.
        :param method: The HTTP Method.
        :param url: The Referer header from the request.
        :param path: The full HTTP Path requested.
        :param query_string: The HTTP query string from the request.
        :param status: The returned HTTP Status of the response.
        :param resource: The resource type.
        :param resource_id: The ID of the resource.
        :param sub_resource: The subresource type.
        :param sub_resource_id: The ID of the subresource.
        :param resource_before: The resource state before this event occurred.
        :param resource_after: The resource state after this event occurred.
        """
        if resource == 'timeline_event':
            event = db_api.entity_get(models.TimeLineEvent, resource_id,
                                      session=session)
            subscribers = sub_api.subscription_get_all_subscriber_ids(
                'story', event.story_id, session=session)
            self.handle_timeline_events(session, event, author, subscribers)

        elif resource == 'project_group':
            subscribers = sub_api.subscription_get_all_subscriber_ids(
                resource, resource_id, session=session)
            self.handle_resources(session=session,
                                  method=method,
                                  resource_id=resource_id,
                                  sub_resource_id=sub_resource_id,
                                  author=author,
                                  subscribers=subscribers)

        if method == 'DELETE' and not (sub_resource_id or sub_resource):
            self.handle_deletions(session, resource, resource_id)
예제 #28
0
    def _apply_pagination(self,
                          model_cls,
                          query,
                          marker=None,
                          offset=None,
                          limit=None,
                          sort_field='id',
                          sort_dir='asc'):
        if not sort_field:
            sort_field = 'id'
        if not sort_dir:
            sort_dir = 'asc'

        marker_entity = None
        if marker:
            marker_entity = api_base.entity_get(model_cls, marker, True)

        return api_base.paginate_query(query=query,
                                       model=model_cls,
                                       limit=limit,
                                       sort_key=sort_field,
                                       marker=marker_entity,
                                       offset=offset,
                                       sort_dir=sort_dir)
예제 #29
0
def delete_filter(filter_id):
    filter = api_base.entity_get(models.WorklistFilter, filter_id)
    for criterion in filter.criteria:
        api_base.entity_hard_delete(models.FilterCriterion, criterion.id)
    api_base.entity_hard_delete(models.WorklistFilter, filter_id)
예제 #30
0
def access_token_get(access_token_id, session=None):
    return api_base.entity_get(models.AccessToken,
                               access_token_id,
                               session=session)
예제 #31
0
def get_filter(filter_id):
    return api_base.entity_get(models.WorklistFilter, filter_id)
예제 #32
0
def subscription_events_get(subscription_event_id):
    return api_base.entity_get(models.SubscriptionEvents,
                               subscription_event_id)
예제 #33
0
def milestone_get(milestone_id):
    return api_base.entity_get(models.Milestone, milestone_id)
예제 #34
0
def subscription_get_all_subscriber_ids(resource, resource_id, session=None):
    '''Test subscription discovery. The tested algorithm is as follows:

    If you're subscribed to a project_group, you will be notified about
    project_group, project, story, and task changes.

    If you are subscribed to a project, you will be notified about project,
    story, and task changes.

    If you are subscribed to a task, you will be notified about changes to
    that task.

    If you are subscribed to a story, you will be notified about changes to
    that story and its tasks.

    :param resource: The name of the resource.
    :param resource_id: The ID of the resource.
    :return: A list of user id's.
    '''
    affected = {
        'project_group': set(),
        'project': set(),
        'story': set(),
        'task': set()
    }

    # If we accidentally pass a timeline_event, we're actually going to treat
    # it as a story.
    if resource == 'timeline_event':
        event = api_base.entity_get(TimeLineEvent,
                                    resource_id,
                                    session=session)
        if event:
            resource = 'story'
            resource_id = event.story_id
        else:
            return set()

    # Sanity check exit.
    if resource not in affected.keys():
        return set()

    # Make sure the requested resource is going to be handled.
    affected[resource].add(resource_id)

    # Resolve either from story->task or from task->story, so the root
    # resource id remains pristine.
    if resource == 'story':
        # Get this story's tasks
        query = api_base.model_query(models.Task.id, session=session) \
            .filter(models.Task.story_id.in_(affected['story']))

        affected['task'] = affected['task'] \
            .union(r for (r,) in query.all())
    elif resource == 'task':
        # Get this tasks's story
        query = api_base.model_query(models.Task.story_id, session=session) \
            .filter(models.Task.id == resource_id)

        affected['story'].add(query.first().story_id)

    # If there are tasks, there will also be projects.
    if affected['task']:
        # Get all the tasks's projects
        query = api_base.model_query(distinct(models.Task.project_id),
                                     session=session) \
            .filter(models.Task.id.in_(affected['task']))

        affected['project'] = affected['project'] \
            .union(r for (r,) in query.all())

    # If there are projects, there will also be project groups.
    if affected['project']:
        # Get all the projects' groups.
        query = api_base.model_query(
            distinct(models.project_group_mapping.c.project_group_id),
            session=session) \
            .filter(models.project_group_mapping.c.project_id
                    .in_(affected['project']))

        affected['project_group'] = affected['project_group'] \
            .union(r for (r,) in query.all())

    # Load all subscribers.
    subscribers = set()
    for affected_type in affected:
        query = api_base.model_query(distinct(
            models.Subscription.user_id), session=session) \
            .filter(models.Subscription.target_type == affected_type) \
            .filter(models.Subscription.target_id.in_(affected[affected_type]))

        results = query.all()
        subscribers = subscribers.union(r for (r,) in results)

    return subscribers
예제 #35
0
def project_get(project_id):
    return api_base.entity_get(models.Project, project_id)
예제 #36
0
    def resolve_resource_by_name(self, session, resource_name, resource_id):
        if resource_name not in class_mappings:
            return None

        klass = class_mappings[resource_name][0]
        return db_api.entity_get(klass, resource_id, session=session)
예제 #37
0
def user_get(user_id, filter_non_public=False, session=None):
    entity = api_base.entity_get(models.User, user_id,
                                 filter_non_public=filter_non_public,
                                 session=session)

    return entity
예제 #38
0
    def write_bug(self, owner, assignee, priority, status, tags, bug,
                  branches):
        """Writes the story, task, task history, and conversation.

        :param owner: The bug owner SQLAlchemy entity.
        :param tags: The tag SQLAlchemy entities.
        :param bug: The Launchpad Bug record.
        """
        #Checks to make sure that the branch for the bug exists
        for branch in branches:
            if not self.check_branch(branch):
                print('No %s branch found for %s project. Creating one now.' %
                      (branch, self.project.name))
                db_api.entity_create(Branch, {
                    'name': branch,
                    'project_id': self.project.id
                }, session=self.session)

        if hasattr(bug, 'date_created'):
            created_at = bug.date_created
        else:
            created_at = None

        if hasattr(bug, 'date_last_updated'):
            updated_at = bug.date_last_updated
        else:
            updated_at = None

        # Extract the launchpad ID from the self link.
        # example url: https://api.launchpad.net/1.0/bugs/1057477
        url_match = re.search("([0-9]+)$", str(bug.self_link))
        if not url_match:
            print('ERROR: Unable to extract launchpad ID from %s.'
                  % (bug.self_link,))
            print('ERROR: Please file a ticket.')
            return
        launchpad_id = int(url_match.groups()[0])

        # If the title is too long, prepend it to the description and
        # truncate it.
        title = bug.title
        description = bug.description

        if len(title) > 100:
            title = title[:97] + '...'
            description = bug.title + '\n\n' + description

        # Create priority tag
        tags.append(self.build_priority_tag(priority))

        # Sanity check.
        story = {
            'id': launchpad_id,
            'description': description,
            'created_at': created_at,
            'creator': owner,
            'is_bug': True,
            'title': title,
            'updated_at': updated_at,
            'tags': tags
        }
        duplicate = db_api.entity_get(Story, launchpad_id,
                                      session=self.session)
        if not duplicate:
            print("Importing Story: %s" % (bug.self_link,))
            story = db_api.entity_create(Story, story, session=self.session)
        else:
            print("Existing Story: %s" % (bug.self_link,))
            story = duplicate

        # Duplicate check- launchpad import creates one task per story,
        # so if we already have a project task on this story, skip it. This
        # is to properly replay imports in the case where errors occurred
        # during import.
        existing_task = db_api.model_query(Task, session=self.session) \
            .filter(Task.story_id == launchpad_id) \
            .filter(Task.project_id == self.project.id) \
            .first()
        if not existing_task:
            print("- Adding task in project %s" % (self.project.name,))

            for branch in branches:
                task = db_api.entity_create(Task, {
                    'title': title,
                    'assignee_id': assignee.id if assignee else None,
                    'project_id': self.project.id,
                    'branch_id': self.get_branch(branch).id,
                    'story_id': launchpad_id,
                    'created_at': created_at,
                    'updated_at': updated_at,
                    'priority': priority,
                    'status': status
                }, session=self.session)
        else:
            print("- Existing task in %s" % (self.project.name,))
            task = existing_task

        # Duplication Check - If this story already has a creation event,
        # we don't need to create a new one. Otherwise, create it manually so
        # we don't trigger event notifications.
        story_created_event = db_api \
            .model_query(TimeLineEvent, session=self.session) \
            .filter(TimeLineEvent.story_id == launchpad_id) \
            .filter(TimeLineEvent.event_type == event_types.STORY_CREATED) \
            .first()
        if not story_created_event:
            print("- Generating story creation event")
            db_api.entity_create(TimeLineEvent, {
                'story_id': launchpad_id,
                'author_id': owner.id,
                'event_type': event_types.STORY_CREATED,
                'created_at': created_at
            }, session=self.session)

        # Create the creation event for the task, but only if we just created
        # a new task.
        if not existing_task:
            print("- Generating task creation event")
            db_api.entity_create(TimeLineEvent, {
                'story_id': launchpad_id,
                'author_id': owner.id,
                'event_type': event_types.TASK_CREATED,
                'created_at': created_at,
                'event_info': json.dumps({
                    'task_id': task.id,
                    'task_title': title
                })
            }, session=self.session)

        # Create the discussion, loading any existing comments first.
        current_count = db_api \
            .model_query(TimeLineEvent, session=self.session) \
            .filter(TimeLineEvent.story_id == launchpad_id) \
            .filter(TimeLineEvent.event_type == event_types.USER_COMMENT) \
            .count()
        desired_count = len(bug.messages)
        print("- %s of %s comments already imported." % (current_count,
                                                         desired_count))
        for i in range(current_count, desired_count):
            print('- Importing comment %s of %s' % (i + 1, desired_count))
            message = bug.messages[i]
            message_created_at = message.date_created
            message_owner = self.write_user(message.owner)

            comment = db_api.entity_create(Comment, {
                'content': message.content,
                'created_at': message_created_at
            }, session=self.session)

            db_api.entity_create(TimeLineEvent, {
                'story_id': launchpad_id,
                'author_id': message_owner.id,
                'event_type': event_types.USER_COMMENT,
                'comment_id': comment.id,
                'created_at': message_created_at
            }, session=self.session)
예제 #39
0
def subscription_get_resource(target_type, target_id):
    if target_type not in SUPPORTED_TYPES:
        return None

    return api_base.entity_get(SUPPORTED_TYPES[target_type], target_id)
예제 #40
0
    def write_bug(self, owner, assignee, priority, status, tags, bug):
        """Writes the story, task, task history, and conversation.

        :param owner: The bug owner SQLAlchemy entity.
        :param tags: The tag SQLAlchemy entities.
        :param bug: The Launchpad Bug record.
        """

        if hasattr(bug, 'date_created'):
            created_at = bug.date_created
        else:
            created_at = None

        if hasattr(bug, 'date_last_updated'):
            updated_at = bug.date_last_updated
        else:
            updated_at = None

        # Extract the launchpad ID from the self link.
        # example url: https://api.launchpad.net/1.0/bugs/1057477
        url_match = re.search("([0-9]+)$", str(bug.self_link))
        if not url_match:
            print 'ERROR: Unable to extract launchpad ID from %s.' \
                  % (bug.self_link,)
            print 'ERROR: Please file a ticket.'
            return
        launchpad_id = int(url_match.groups()[0])

        # If the title is too long, prepend it to the description and
        # truncate it.
        title = bug.title
        description = bug.description

        if len(title) > 100:
            title = title[:97] + '...'
            description = bug.title + '\n\n' + description

        # Sanity check.
        story = {
            'id': launchpad_id,
            'description': description,
            'created_at': created_at,
            'creator': owner,
            'is_bug': True,
            'title': title,
            'updated_at': updated_at,
            'tags': tags
        }
        duplicate = db_api.entity_get(Story,
                                      launchpad_id,
                                      session=self.session)
        if not duplicate:
            print "Importing Story: %s" % (bug.self_link, )
            story = db_api.entity_create(Story, story, session=self.session)
        else:
            print "Existing Story: %s" % (bug.self_link, )
            story = duplicate

        # Duplicate check- launchpad import creates one task per story,
        # so if we already have a project task on this story, skip it. This
        # is to properly replay imports in the case where errors occurred
        # during import.
        existing_task = db_api.model_query(Task, session=self.session) \
            .filter(Task.story_id == launchpad_id) \
            .filter(Task.project_id == self.project.id) \
            .first()
        if not existing_task:
            print "- Adding task in project %s" % (self.project.name, )
            task = db_api.entity_create(
                Task, {
                    'title': title,
                    'assignee_id': assignee.id if assignee else None,
                    'project_id': self.project.id,
                    'story_id': launchpad_id,
                    'created_at': created_at,
                    'updated_at': updated_at,
                    'priority': priority,
                    'status': status
                },
                session=self.session)
        else:
            print "- Existing task in %s" % (self.project.name, )
            task = existing_task

        # Duplication Check - If this story already has a creation event,
        # we don't need to create a new one. Otherwise, create it manually so
        # we don't trigger event notifications.
        story_created_event = db_api \
            .model_query(TimeLineEvent, session=self.session) \
            .filter(TimeLineEvent.story_id == launchpad_id) \
            .filter(TimeLineEvent.event_type == event_types.STORY_CREATED) \
            .first()
        if not story_created_event:
            print "- Generating story creation event"
            db_api.entity_create(TimeLineEvent, {
                'story_id': launchpad_id,
                'author_id': owner.id,
                'event_type': event_types.STORY_CREATED,
                'created_at': created_at
            },
                                 session=self.session)

        # Create the creation event for the task, but only if we just created
        # a new task.
        if not existing_task:
            print "- Generating task creation event"
            db_api.entity_create(TimeLineEvent, {
                'story_id':
                launchpad_id,
                'author_id':
                owner.id,
                'event_type':
                event_types.TASK_CREATED,
                'created_at':
                created_at,
                'event_info':
                json.dumps({
                    'task_id': task.id,
                    'task_title': title
                })
            },
                                 session=self.session)

        # Create the discussion, loading any existing comments first.
        current_count = db_api \
            .model_query(TimeLineEvent, session=self.session) \
            .filter(TimeLineEvent.story_id == launchpad_id) \
            .filter(TimeLineEvent.event_type == event_types.USER_COMMENT) \
            .count()
        desired_count = len(bug.messages)
        print "- %s of %s comments already imported." % (current_count,
                                                         desired_count)
        for i in range(current_count, desired_count):
            print '- Importing comment %s of %s' % (i + 1, desired_count)
            message = bug.messages[i]
            message_created_at = message.date_created
            message_owner = self.write_user(message.owner)

            comment = db_api.entity_create(Comment, {
                'content': message.content,
                'created_at': message_created_at
            },
                                           session=self.session)

            db_api.entity_create(TimeLineEvent, {
                'story_id': launchpad_id,
                'author_id': message_owner.id,
                'event_type': event_types.USER_COMMENT,
                'comment_id': comment.id,
                'created_at': message_created_at
            },
                                 session=self.session)
예제 #41
0
def branch_get(branch_id):
    return api_base.entity_get(models.Branch, branch_id)
예제 #42
0
def user_get(user_id, filter_non_public=False):
    entity = api_base.entity_get(models.User, user_id,
                                 filter_non_public=filter_non_public)

    return entity
예제 #43
0
def refresh_token_get(refresh_token_id, session=None):
    return api_base.entity_get(models.RefreshToken,
                               refresh_token_id,
                               session=session)
예제 #44
0
def event_get(event_id):
    return api_base.entity_get(models.TimeLineEvent, event_id)
예제 #45
0
    def resolve_resource_by_name(self, session, resource_name, resource_id):
        if resource_name not in class_mappings:
            return None

        klass = class_mappings[resource_name][0]
        return db_api.entity_get(klass, resource_id, session=session)
예제 #46
0
def milestone_get(milestone_id):
    return api_base.entity_get(models.Milestone, milestone_id)
예제 #47
0
def comment_get(comment_id):
    return api_base.entity_get(models.Comment, comment_id)
예제 #48
0
def subscription_get_all_subscriber_ids(resource, resource_id, session=None):
    '''Test subscription discovery. The tested algorithm is as follows:

    If you're subscribed to a project_group, you will be notified about
    project_group, project, story, and task changes.

    If you are subscribed to a project, you will be notified about project,
    story, and task changes.

    If you are subscribed to a task, you will be notified about changes to
    that task.

    If you are subscribed to a story, you will be notified about changes to
    that story and its tasks.

    :param resource: The name of the resource.
    :param resource_id: The ID of the resource.
    :return: A list of user id's.
    '''
    affected = {
        'project_group': set(),
        'project': set(),
        'story': set(),
        'task': set()
    }

    # If we accidentally pass a timeline_event, we're actually going to treat
    # it as a story.
    if resource == 'timeline_event':
        event = api_base.entity_get(TimeLineEvent,
                                    resource_id,
                                    session=session)
        if event:
            resource = 'story'
            resource_id = event.story_id
        else:
            return set()

    # Sanity check exit.
    if resource not in affected.keys():
        return set()

    # Make sure the requested resource is going to be handled.
    affected[resource].add(resource_id)

    users = None

    # Resolve either from story->task or from task->story, so the root
    # resource id remains pristine.
    if resource == 'story':
        # If the story is private, make a whitelist of users to notify.
        story = api_base.model_query(models.Story, session) \
            .options(subqueryload(models.Story.permissions)) \
            .filter_by(id=resource_id).first()

        if story.private:
            users = [user.id for user in story.permissions[0].users]

        # Get this story's tasks
        query = api_base.model_query(models.Task.id, session=session) \
            .filter(models.Task.story_id.in_(affected['story']))

        affected['task'] = affected['task'] \
            .union(r for (r,) in query.all())
    elif resource == 'task':
        # Get this tasks's story
        query = api_base.model_query(models.Task.story_id, session=session) \
            .filter(models.Task.id == resource_id)

        affected['story'].add(query.first().story_id)

        story = api_base.model_query(models.Story, session) \
            .options(subqueryload(models.Story.permissions)) \
            .filter_by(id=query.first().story_id).first()

        if story.private:
            users = [user.id for user in story.permissions[0].users]

    # If there are tasks, there will also be projects.
    if affected['task']:
        # Get all the tasks's projects
        query = api_base.model_query(distinct(models.Task.project_id),
                                     session=session) \
            .filter(models.Task.id.in_(affected['task']))

        affected['project'] = affected['project'] \
            .union(r for (r,) in query.all())

    # If there are projects, there will also be project groups.
    if affected['project']:
        # Get all the projects' groups.
        query = api_base.model_query(
            distinct(models.project_group_mapping.c.project_group_id),
            session=session) \
            .filter(models.project_group_mapping.c.project_id
                    .in_(affected['project']))

        affected['project_group'] = affected['project_group'] \
            .union(r for (r,) in query.all())

    # Load all subscribers.
    subscribers = set()
    for affected_type in affected:
        query = api_base.model_query(distinct(
            models.Subscription.user_id), session=session) \
            .filter(models.Subscription.target_type == affected_type) \
            .filter(models.Subscription.target_id.in_(affected[affected_type]))

        if users is not None:
            query = query.filter(models.Subscription.user_id.in_(users))

        results = query.all()
        subscribers = subscribers.union(r for (r, ) in results)

    return subscribers
예제 #49
0
def subscription_get(subscription_id):
    return api_base.entity_get(models.Subscription, subscription_id)
예제 #50
0
def subscription_get(subscription_id):
    return api_base.entity_get(models.Subscription, subscription_id)
예제 #51
0
def task_get(task_id):
    return api_base.entity_get(models.Task, task_id)
예제 #52
0
def project_get(project_id):
    return api_base.entity_get(models.Project, project_id)
예제 #53
0
def refresh_token_get(refresh_token_id, session=None):
    return api_base.entity_get(models.RefreshToken, refresh_token_id,
                               session=session)
예제 #54
0
def event_get(event_id):
    return api_base.entity_get(models.TimeLineEvent, event_id)
예제 #55
0
def delete_filter(filter_id):
    filter = api_base.entity_get(models.WorklistFilter, filter_id)
    for criterion in filter.criteria:
        api_base.entity_hard_delete(models.FilterCriterion, criterion.id)
    api_base.entity_hard_delete(models.WorklistFilter, filter_id)