예제 #1
0
    def _get_label_id(self, name):
        """
        Get or create a database label object, return the ID.
        """
        print('Label name to add:{}'.format(name), flush=True)
        db_session = worker.get_session()
        redis = worker.get_redis()
        label = db_session.query(Label).filter_by(name=name.lower().strip()).first()

        if label:
            print('Found Label :{}'.format(label.id), flush=True)
            return label.id
        else:
            label = Label(name=name.lower().strip())
            db_session.add(label)

            try:
                db_session.commit()
            except IntegrityError:
                db_session.rollback()
                raise
            except AssertionError:
                db_session.rollback()
                raise ValueError(
                    'Label "{}" contains non-alphanumeric character'
                    .format(name)
                )

            redis.publish('label', json.dumps(label.as_dict()))
            print('Created label :{}'.format(label.id), flush=True)
            return label.id
예제 #2
0
    def put(self, id_):
        '''
        Update the profile identified by `id` with submitted data.
        The following attribute are modifiable:
           * is_interesting
           * lables

        **Example Request**

        .. sourcecode:: json

            {
                "is_interesting": true,
                "labels": [
                    {"name": "male"},
                    {"name": "british"},
                    ...
                ],
                ...
            }

        **Example Response**

        .. sourcecode:: json

            {
                "avatar_url": "https://quickpin/api/file/1",
                "avatar_thumb_url": "https://quickpin/api/file/2",
                "description": "A human being.",
                "follower_count": 71,
                "friend_count": 28,
                "id": 1,
                "is_stub": false,
                "is_interesting": true,
                "join_date": "2012-01-30T15:11:35",
                "labels": [
                    {
                        "id": 1,
                        "name": "male"
                    },
                    {
                        "id": 2,
                        "name": "british"
                    },
                ],
                "last_update": "2015-08-18T10:51:16",
                "location": "Washington, DC",
                "name": "John Doe",
                "post_count": 1666,
                "private": false,
                "site": "twitter",
                "site_name": "Twitter",
                "time_zone": "Central Time (US & Canada)",
                "upstream_id": "11009418",
                "url": "https://quickpin/api/profile/1",
                "username": "******",
                "usernames": [
                    {
                        "end_date": "2012-06-30T15:00:00",
                        "start_date": "2012-01-01T12:00:00",
                        "username": "******"
                    },
                    ...
                ]
            }

        :<header Content-Type: application/json
        :<header X-Auth: the client's auth token
        :>json bool is_interesting: whether profile is marked as interesting
        :>json list labels: whether profile is marked as interesting

        :>header Content-Type: application/json
        :>json str avatar_url: URL to the user's current avatar
        :>json str avatar_thumb_url: URL to a 32x32px thumbnail of the user's
            current avatar
        :>json str description: profile description
        :>json int follower_count: number of followers
        :>json int friend_count: number of friends (a.k.a. followees)
        :>json int id: unique identifier for profile
        :>json bool is_stub: indicates that this is a stub profile, e.g.
            related to another profile but has not been fully imported
        :>json bool is_interesting: indicates whether this profile has been
            marked as interesting. The value can be null.
        :>json str join_date: the date this profile joined its social network
            (ISO-8601)
        :>json list labels: list of labels for this profile
        :>json int label[n].id: the unique id for this label
        :>json str label[n].name: the label
        :>json str last_update: the last time that information about this
            profile was retrieved from the social media site (ISO-8601)
        :>json str location: geographic location provided by the user, as free
            text
        :>json str name: the full name provided by this user
        :>json int post_count: the number of posts made by this profile
        :>json bool private: true if this is a private account (i.e. not world-
            readable)
        :>json str site: machine-readable site name that this profile belongs to
        :>json str site_name: human-readable site name that this profile belongs
            to
        :>json str time_zone: the user's provided time zone as free text
        :>json str upstream_id: the user ID assigned by the social site
        :>json str url: URL endpoint for retriving more data about this profile
        :>json str username: the current username for this profile
        :>json list usernames: list of known usernames for this profile
        :>json str usernames[n].end_date: the last known date this username was
            used for this profile
        :>json str usernames[n].start_date: the first known date this username
            was used for this profile
        :>json str usernames[n].username: a username used for this profile

        :status 202: accepted for background processing
        :status 400: invalid request body
        :status 401: authentication required

        '''

        redis = worker.get_redis()

        # Get profile.
        id_ = get_int_arg('id_', id_)

        current_avatar_id = self._current_avatar_subquery()

        profile, avatar = g.db.query(Profile, Avatar) \
                              .outerjoin(Avatar, Avatar.id == current_avatar_id) \
                              .filter(Profile.id == id_).first()

        if profile is None:
            raise NotFound("Profile '%s' does not exist." % id_)

        request_json = request.get_json()

        # Validate put data and set attributes
        # Only 'is_interesting' and 'labels' are modifiable
        if 'is_interesting' in request_json:
            if isinstance(request_json['is_interesting'], bool):
                profile.is_interesting = request_json['is_interesting']
            elif request_json['is_interesting'] is None:
                profile.is_interesting = None
            else:
                raise BadRequest("Attribute 'is_interesting' is type boolean,"
                                 " or can be set as null")

        # labels expects the string 'name' rather than id, to avoid the need to
        # create labels before adding them.
        if 'labels' in request_json:
            labels = []
            if isinstance(request_json['labels'], list):
                for label_json in request_json['labels']:
                    if 'name' in label_json:
                        label = g.db.query(Label) \
                                    .filter(Label.name==label_json['name']) \
                                    .first()
                        if label is None:
                            try:
                                label = Label(
                                    name=label_json['name'].lower().strip()
                                )

                                g.db.add(label)
                                g.db.flush()

                                redis.publish(
                                    'label',
                                    json.dumps(label.as_dict())
                                )
                            except IntegrityError:
                                g.db.rollback()
                                raise BadRequest('Label could not be saved')
                            except AssertionError:
                                g.db.rollback()
                                raise BadRequest(
                                    '"{}" contains non-alphanumeric character'
                                    .format(
                                        label_json['name']
                                    )
                                )

                        labels.append(label)
                    else:
                        raise BadRequest("Label 'name' is required")

                profile.labels = labels
            else:
                raise BadRequest("'labels' must be a list")


        response = profile.as_dict()
        response['url'] = url_for('ProfileView:get', id_=profile.id)

        # Save the profile
        try:
            g.db.commit()
            redis.publish('profile_update', json.dumps(profile.as_dict()))
        except DBAPIError as e:
            g.db.rollback()
            raise BadRequest('Profile could not be saved')

        # Create usernames list.
        usernames = list()

        for username in profile.usernames:
            if username.end_date is not None:
                end_date = username.end_date.isoformat()
            else:
                end_date = None

            if username.start_date is not None:
                start_date = username.start_date.isoformat()
            else:
                start_date = None

            usernames.append({
                'end_date': end_date,
                'username': username.username,
                'start_date': start_date,
            })

        response['usernames'] = usernames

        # Create avatar attributes.
        if avatar is not None:
            response['avatar_url'] = url_for(
                'FileView:get',
                id_=avatar.file.id
            )
            response['avatar_thumb_url'] = url_for(
                'FileView:get',
                id_=avatar.thumb_file.id
            )
        else:
            response['avatar_url'] = url_for(
                'static',
                filename='img/default_user.png'
            )
            response['avatar_thumb_url'] = url_for(
                'static',
                filename='img/default_user_thumb.png'
            )

        # Send response.
        return jsonify(**response)
예제 #3
0
    def put(self, id_):
        '''
        Update the profile identified by `id` with submitted data.

        The following attributes are modifiable:

           * is_interesting
           * labels
           * score

        **Example Request**

        .. sourcecode:: json

            {
                "is_interesting": true,
                "labels": [
                    {"name": "male"},
                    {"name": "british"},
                    ...
                ],
                "score": 2323.0,
                ...
            }

        **Example Response**

        .. sourcecode:: json

            {
                "avatar_url": "https://quickpin/api/file/1",
                "avatar_thumb_url": "https://quickpin/api/file/2",
                "description": "A human being.",
                "follower_count": 71,
                "friend_count": 28,
                "id": 1,
                "is_stub": false,
                "is_interesting": true,
                "join_date": "2012-01-30T15:11:35",
                "labels": [
                    {
                        "id": 1,
                        "name": "male"
                    },
                    {
                        "id": 2,
                        "name": "british"
                    },
                ],
                "last_update": "2015-08-18T10:51:16",
                "location": "Washington, DC",
                "name": "John Doe",
                "post_count": 1666,
                "private": false,
                "score": "-2.0621606863",
                "site": "twitter",
                "site_name": "Twitter",
                "time_zone": "Central Time (US & Canada)",
                "upstream_id": "11009418",
                "url": "https://quickpin/api/profile/1",
                "username": "******",
                "usernames": [
                    {
                        "end_date": "2012-06-30T15:00:00",
                        "start_date": "2012-01-01T12:00:00",
                        "username": "******"
                    },
                    ...
                ]
            }

        :<header Content-Type: application/json
        :<header X-Auth: the client's auth token
        :<json bool is_interesting: whether profile is marked as interesting
        :<json list labels: list of profile labels
        :<json float score: profile score

        :>header Content-Type: application/json
        :>json str avatar_url: URL to the user's current avatar
        :>json str avatar_thumb_url: URL to a 32x32px thumbnail of the user's
            current avatar
        :>json str description: profile description
        :>json int follower_count: number of followers
        :>json int friend_count: number of friends (a.k.a. followees)
        :>json int id: unique identifier for profile
        :>json bool is_stub: indicates that this is a stub profile, e.g.
            related to another profile but has not been fully imported
        :>json bool is_interesting: indicates whether this profile has been
            marked as interesting. The value can be null.
        :>json str join_date: the date this profile joined its social network
            (ISO-8601)
        :>json list labels: list of labels for this profile
        :>json int label[n].id: the unique id for this label
        :>json str label[n].name: the label
        :>json str last_update: the last time that information about this
            profile was retrieved from the social media site (ISO-8601)
        :>json str location: geographic location provided by the user, as free
            text
        :>json str name: the full name provided by this user
        :>json int note[n].id: the unique id for this note
        :>json int note[n].category: the user-defined category of this note
        :>json int note[n].body: the user-defined text-body of this note
        :>json int post_count: the number of posts made by this profile
        :>json bool private: true if this is a private account (i.e. not world-
            readable)
        :>json str score: user-defined score for this profile. Can be null.
        :>json str site: machine-readable site name that this profile belongs to
        :>json str site_name: human-readable site name that this profile belongs
            to
        :>json str time_zone: the user's provided time zone as free text
        :>json str upstream_id: the user ID assigned by the social site
        :>json str url: URL endpoint for retriving more data about this profile
        :>json str username: the current username for this profile
        :>json list usernames: list of known usernames for this profile
        :>json str usernames[n].end_date: the last known date this username was
            used for this profile
        :>json str usernames[n].start_date: the first known date this username
            was used for this profile
        :>json str usernames[n].username: a username used for this profile

        :status 202: accepted for background processing
        :status 400: invalid request body
        :status 401: authentication required
        '''

        redis = worker.get_redis()

        # Get profile.
        id_ = get_int_arg('id_', id_)

        profile, avatar = g.db.query(Profile, Avatar) \
                              .outerjoin(Profile.current_avatar) \
                              .filter(Profile.id == id_).first()

        if profile is None:
            raise NotFound("Profile '%s' does not exist." % id_)

        request_json = request.get_json()

        # Validate put data and set attributes
        # Only 'is_interesting', 'score', and 'labels' are modifiable
        if 'is_interesting' in request_json:
            if isinstance(request_json['is_interesting'], bool):
                profile.is_interesting = request_json['is_interesting']
            elif request_json['is_interesting'] is None:
                profile.is_interesting = None
            else:
                raise BadRequest("'is_interesting' is a boolean (true or false.")

        if 'score' in request_json:
            if request_json['score'] is None:
                profile.score = None
            else:
                try:
                    profile.score = float(request_json['score'])
                except:
                    raise BadRequest("'score' must be a decimal number.")


        # labels expects the string 'name' rather than id, to avoid the need to
        # create labels before adding them.
        if 'labels' in request_json:
            labels = []
            if isinstance(request_json['labels'], list):
                for label_json in request_json['labels']:
                    if 'name' in label_json:
                        label = g.db.query(Label) \
                                    .filter(Label.name==label_json['name']) \
                                    .first()
                        if label is None:
                            try:
                                label = Label(
                                    name=label_json['name'].lower().strip()
                                )

                                g.db.add(label)
                                g.db.flush()

                                redis.publish(
                                    'label',
                                    json.dumps(label.as_dict())
                                )
                            except IntegrityError:
                                g.db.rollback()
                                raise BadRequest('Label could not be saved')
                            except AssertionError:
                                g.db.rollback()
                                raise BadRequest(
                                    '"{}" contains non-alphanumeric character'
                                    .format(
                                        label_json['name']
                                    )
                                )

                        labels.append(label)
                    else:
                        raise BadRequest("Label 'name' is required")

                profile.labels = labels
            else:
                raise BadRequest("'labels' must be a list")

        response = profile.as_dict()
        response['url'] = url_for('ProfileView:get', id_=profile.id)

        # Save the profile
        try:
            g.db.commit()
            redis.publish('profile', json.dumps(profile.as_dict()))
        except DBAPIError as e:
            g.db.rollback()
            raise BadRequest('Profile could not be saved')

        # Create usernames list.
        usernames = list()

        for username in profile.usernames:
            if username.end_date is not None:
                end_date = username.end_date.isoformat()
            else:
                end_date = None

            if username.start_date is not None:
                start_date = username.start_date.isoformat()
            else:
                start_date = None

            usernames.append({
                'end_date': end_date,
                'username': username.username,
                'start_date': start_date,
            })

        response['usernames'] = usernames

        # Create avatar attributes.
        if avatar is not None:
            response['avatar_url'] = url_for(
                'FileView:get',
                id_=avatar.file.id
            )
            response['avatar_thumb_url'] = url_for(
                'FileView:get',
                id_=avatar.thumb_file.id
            )
        else:
            response['avatar_url'] = url_for(
                'static',
                filename='img/default_user.png'
            )
            response['avatar_thumb_url'] = url_for(
                'static',
                filename='img/default_user_thumb.png'
            )

        # Send response.
        return jsonify(**response)