コード例 #1
0
ファイル: user.py プロジェクト: hennas/RevMusic
    def post(self):
        """
        Responds to POST request by adding a new user item to the collection. 
        If no errors happens while adding the user, the location of the new item is returned in the 'Location' response header.
        Otherwise an appropriate error code with a human-readable error message is returned.
        """
        if not request.json:
            return create_error_response(415, 'Unsupported media type',
                                         'Use JSON')
        try:
            validate(request.json, User.get_schema())
        except ValidationError as e:
            return create_error_response(400, 'Invalid JSON document', str(e))

        # Get arguments from the request
        username = request.json['username'].lower()  # Lowercase the username
        email = request.json['email']
        password = request.json['password']

        # Check that username not taken
        if User.query.filter_by(username=username).count() > 0:
            return create_error_response(
                409, 'Already exists',
                'User with username "{}" already exists'.format(username))
        # Check that email not taken
        if User.query.filter_by(email=email).count() > 0:
            return create_error_response(
                409, 'Already exists',
                'User with email "{}" already exists'.format(email))

        # Create the new user entry
        user = User(username=username, email=email, password=password)
        # Attempt to add to database
        try:
            db.session.add(user)
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return create_error_response(
                409, 'Unexpected conflict',
                'An unexpected conflict happened while committing to the database'
            )

        # Respond to successful request
        return Response(
            status=201,
            headers={
                'Location':
                url_for('api.useritem',
                        user=username)  # Location of the added item
            })
コード例 #2
0
    def get(self, album, review):
        """
        Responds to GET request with the representation of the requested review item (JSON document with added hypermedia controls (MASON))
        If the requested review or the album for which the review should have been submitted does not exist in the API, 404 error code returned.
        : param str album: the unique name of the album for which the requested review have been submitted, provided in the request URL
        : param str review: the identifier of the requested review, provided in the request URL
        """
        # Fetch the album and review items from the database and check if they exist
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')
        review_item = Review.query.filter(Review.identifier == review).filter(
            Review.album == album_item).first()
        if not review_item:
            return create_error_response(404, 'Review not found')

        # Create response
        user = review_item.user.username
        body = RevMusicBuilder(identifier=review,
                               user=user,
                               album=album_item.title,
                               artist=album_item.artist,
                               title=review_item.title,
                               content=review_item.content,
                               star_rating=review_item.star_rating,
                               submission_date=datetime.datetime.strftime(
                                   review_item.submission_date,
                                   '%Y-%m-%d %H:%M:%S'))
        body.add_namespace('revmusic', LINK_RELATIONS_URL)
        body.add_control('self',
                         url_for('api.reviewitem', album=album, review=review))
        body.add_control('profile', REVIEW_PROFILE)
        body.add_control('author',
                         url_for('api.useritem', user=user),
                         title='The user who has submitted the review')
        body.add_control(
            'about',
            url_for('api.albumitem', album=album),
            title='The album for which the review has been written')
        body.add_control_reviews_by(user)
        body.add_control_reviews_for(album)
        body.add_control_reviews_all()
        body.add_control_edit_review(album, review)
        body.add_control_delete_review(album, review)

        return Response(json.dumps(body), 200, mimetype=MASON)
コード例 #3
0
    def delete(self, album, review):
        """
        Responds to DELETE request by deleting the requested review item.
        If the requested review or the album for which the review should have been submitted does not exist in the API, 404 error code returned.
        : param str album: the unique name of the album for which the requested review have been submitted, provided in the request URL
        : param str review: the identifier of the requested review, provided in the request URL
        """
        # Fetch the album and review items from the database and check if they exist
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')
        review_item = Review.query.filter(Review.identifier == review).filter(
            Review.album == album_item).first()
        if not review_item:
            return create_error_response(404, 'Review not found')

        db.session.delete(review_item)
        db.session.commit()
        return Response(status=204)
コード例 #4
0
ファイル: user.py プロジェクト: hennas/RevMusic
    def put(self, user):
        """
        Responds to PUT request by replacing the user item's representation with the provided new one. 
        If an error happens while handling the request, an appropriate error code with a human-readable error message is returned.
        : param str user: the username of the requested user, provided in the request URL
        """
        if not request.json:
            return create_error_response(415, 'Unsupported media type',
                                         'Use JSON')
        try:
            validate(request.json, User.get_schema())
        except ValidationError as e:
            return create_error_response(400, 'Invalid JSON document', str(e))

        # Fetch the requested user from the database and check that it exists
        db_user = User.query.filter_by(username=user).first()
        if not db_user:
            return create_error_response(404, 'User not found')

        username = request.json['username'].lower()  # Lowercase the username
        email = request.json['email']
        password = request.json['password']

        # Check that possible new username is not taken
        if username != user:
            if User.query.filter_by(username=username).count() > 0:
                return create_error_response(
                    409, 'Already exists',
                    'User with username "{}" already exists'.format(username))

        # Check that possible new email is not taken
        if email != db_user.email:
            if User.query.filter_by(email=email).count() > 0:
                return create_error_response(
                    409, 'Already exists',
                    'User with email "{}" already exists'.format(email))

        # Updated user entry
        db_user.username = username
        db_user.email = email
        db_user.password = password
        # Commit changes
        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return create_error_response(
                409, 'Unexpected conflict',
                'An unexpected conflict happened while committing to the database'
            )

        return Response(
            status=201,
            headers={
                'Location':
                url_for('api.useritem',
                        user=username)  # Location of the updated item
            })
コード例 #5
0
ファイル: user.py プロジェクト: hennas/RevMusic
    def delete(self, user):
        """
        Responds to DELETE request by deleting the requested user item.
        If the requested user does not exist in the API, 404 error code returned.
        : param str user: the username of the requested user, provided in the request URL
        """
        # Fetch the requested user from the database and check that it exists
        db_user = User.query.filter_by(username=user).first()
        if not db_user:
            return create_error_response(404, 'User not found')

        db.session.delete(db_user)
        db.session.commit()
        return Response(status=204)
コード例 #6
0
    def delete(self, album):
        """
        Responds to DELETE request by deleting the requested album item.
        If requested album does not exist in the API, 404 error code returned.
        : param str album: the unique name of the requested album, provided in the request URL
        """
        # Fetch requested album item from database and check that it exists
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')

        db.session.delete(album_item)
        db.session.commit()
        return Response(status=204)
コード例 #7
0
    def get(self, album):
        """
        Responds to GET request with a listing of all reviews for the specified album (JSON document with added hypermedia controls (MASON))
        If the specified album does not exist in the API, 404 error code returned.
        : param str album: the unique name of the album the reviews of which are requested, provided in the request URL
        """
        # Fetch the album item from the database and check whether it exists
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')

        body = RevMusicBuilder()
        body.add_namespace('revmusic', LINK_RELATIONS_URL)
        body.add_control('self', url_for('api.reviewsbyalbum', album=album))
        body.add_control(
            'up',
            url_for('api.albumitem', album=album),
            title='Album item for which the reviews have been submitted')
        body.add_control_reviews_all()
        body.add_control_add_review(album)

        # Fetch all the reviews from the database for the specified album
        reviews = Review.query.filter(Review.album == album_item).order_by(
            Review.submission_date.desc()).all()
        body['items'] = []
        for review in reviews:
            item = RevMusicBuilder(identifier=review.identifier,
                                   user=review.user.username,
                                   title=review.title,
                                   star_rating=review.star_rating,
                                   submission_date=datetime.datetime.strftime(
                                       review.submission_date,
                                       '%Y-%m-%d %H:%M:%S'))
            item.add_control(
                'self',
                url_for('api.reviewitem',
                        album=album,
                        review=review.identifier))
            item.add_control('profile', REVIEW_PROFILE)
            body['items'].append(item)

        return Response(json.dumps(body), 200, mimetype=MASON)
コード例 #8
0
ファイル: user.py プロジェクト: hennas/RevMusic
    def get(self, user):
        """
        Responds to GET request with the representation of the requested user item (JSON document with added hypermedia controls (MASON))
        If the requested user does not exist in the API, 404 error code returned.
        : param str user: the username of the requested user, provided in the request URL
        """
        # Fetch the requested user from the database and check that it exists
        db_user = User.query.filter_by(username=user).first()
        if not db_user:
            return create_error_response(404, 'User not found')

        body = RevMusicBuilder(username=db_user.username, email=db_user.email)
        body.add_namespace('revmusic', LINK_RELATIONS_URL)
        body.add_control('self', url_for('api.useritem',
                                         user=db_user.username))
        body.add_control('profile', USER_PROFILE)
        body.add_control_users_all('collection')
        body.add_control_reviews_by(user)
        body.add_control_edit_user(user)
        body.add_control_delete_user(user)
        return Response(json.dumps(body), 200, mimetype=MASON)
コード例 #9
0
    def get(self, album):
        """
        Responds to GET request with the information of the requested album item (JSON document with added hypermedia controls (MASON))
        If requested album does not exist in the API, 404 error code returned.
        : param str album: the unique name of the requested album, provided in the request URL
        """
        # Fetch requested album item from database and check that it exists
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')

        # Handle optional data (date and time objects to string, if exists)
        release = album_item.publication_date
        duration = album_item.duration
        if release:
            release = release.strftime('%Y-%m-%d')
        if duration:
            duration = duration.strftime('%H:%M:%S')

        body = RevMusicBuilder(unique_name=album,
                               title=album_item.title,
                               artist=album_item.artist,
                               release=release,
                               duration=duration,
                               genre=album_item.genre)
        body.add_namespace('revmusic', LINK_RELATIONS_URL)
        body.add_control('self', url_for('api.albumitem', album=album))
        body.add_control('profile', ALBUM_PROFILE)
        body.add_control('collection',
                         url_for('api.albumcollection'),
                         title='All albums')
        body.add_control_reviews_for(album)
        body.add_control_edit_album(album)
        body.add_control_delete_album(album)

        return Response(json.dumps(body), 200, mimetype=MASON)
コード例 #10
0
    def post(self):
        """
        Responds to POST request by adding a new album item to the collection. 
        If no errors happens while adding the album, the location of the new item is returned in the 'Location' response header.
        Otherwise an appropriate error code with a human-readable error message is returned.
        """
        if not request.json:
            return create_error_response(415, 'Unsupported media type',
                                         'Use JSON')
        try:
            validate(request.json, Album.get_schema())
        except ValidationError as e:
            return create_error_response(400, 'Invalid JSON document', str(e))

        # Get arguments from request
        unique_name = request.json['unique_name'].lower()
        title = request.json['title']
        artist = request.json['artist']
        # Handle optional arguments
        release = None
        if 'release' in request.json:
            release = to_date(request.json['release'])
            if release is None:  # None returned in case of an error
                return create_error_response(
                    400, 'Invalid release date',
                    'The release date you provided is an invalid date')
        duration = None
        if 'duration' in request.json:
            duration = to_time(request.json['duration'])
            # Schema forces the duration to be in the right form, so following check for error is not necessary
            #if duration is None:# None returned in case of an error
            #    return create_error_response(400, 'Invalid duration',
            #    'The album duration you provided is an invalid time')
        genre = None
        if 'genre' in request.json:
            genre = request.json['genre']

        # Check that unique_name is not in use
        if Album.query.filter_by(unique_name=unique_name).count() > 0:
            return create_error_response(
                409, 'Already exists',
                'Unique name "{}" is already in use'.format(unique_name))

        # Create album entry and commit
        album = Album(unique_name=unique_name,
                      title=title,
                      artist=artist,
                      publication_date=release,
                      duration=duration,
                      genre=genre)
        try:
            db.session.add(album)
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return create_error_response(
                409, 'Already exists',
                'Album with title "{}" already exists with artist "{}"'.format(
                    title, artist))

        return Response(
            status=201,
            headers={
                'Location':
                url_for('api.albumitem',
                        album=unique_name)  # Location of the added item
            })
コード例 #11
0
    def put(self, album):
        """
        Responds to PUT request by replacing the album item's representation with the provided new one. 
        If an error happens while handling the request, an appropriate error code with a human-readable error message is returned.
        : param str album: the unique name of the requested album, provided in the request URL
        """
        if not request.json:
            return create_error_response(415, 'Unsupported media type',
                                         'Use JSON')

        try:
            validate(request.json, Album.get_schema())
        except ValidationError as e:
            return create_error_response(400, 'Invalid JSON document', str(e))

        # Fetch requested album item from database and check that it exists
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')

        # Get arguments from the request
        unique_name = request.json['unique_name'].lower()
        title = request.json['title']
        artist = request.json['artist']

        # Handle optional arguments; will be set to null if not provided
        release = None
        if 'release' in request.json:
            release = to_date(request.json['release'])
            if release is None:  # None returned in case of an error
                return create_error_response(
                    400, 'Invalid release date',
                    'The release date you provided is an invalid date')
        duration = None
        if 'duration' in request.json:
            duration = to_time(request.json['duration'])
            # Schema forces the duration to be in the right form, so following check for error is not necessary
            #if duration is None: # None returned in case of an error
            #    return create_error_response(400, 'Invalid duration',
            #    'The album duration you provided is an invalid time')
        genre = None
        if 'genre' in request.json:
            genre = request.json['genre']

        # Check that unique_name is not in use in case that it is changed
        if unique_name != album and Album.query.filter_by(
                unique_name=unique_name).count() > 0:
            return create_error_response(
                409, 'Already exists',
                'Unique name "{}" is already in use'.format(unique_name))

        # Update album values and commit
        album_item.unique_name = unique_name
        album_item.title = title
        album_item.artist = artist
        album_item.publication_date = release
        album_item.duration = duration
        album_item.genre = genre

        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return create_error_response(
                409, 'Already exists',
                'Album with title "{}" already exists with artist "{}"'.format(
                    title, artist))

        return Response(
            status=201,
            headers={
                'Location':
                url_for('api.albumitem',
                        album=unique_name)  # Location of the updated item
            })
コード例 #12
0
    def put(self, album, review):
        """
        Responds to PUT request by replacing the review item's representation with the provided new one. 
        If an error happens while handling the request, an appropriate error code with a human-readable error message is returned.
        : param str album: the unique name of the album for which the requested review have been submitted, provided in the request URL
        : param str review: the identifier of the requested review, provided in the request URL
        """
        if not request.json:
            return create_error_response(415, 'Unsupported media type',
                                         'Use JSON')
        try:
            validate(request.json, Review.get_schema())
        except ValidationError as e:
            return create_error_response(400, 'Invalid JSON document', str(e))

        # Fetch the album and review items from the database and check if they exist
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')
        review_item = Review.query.filter(Review.identifier == review).filter(
            Review.album == album_item).first()
        if not review_item:
            return create_error_response(404, 'Review not found')

        # Create an updated unique identifier and get the updated submission datetime for the review
        # Note: the probability of creating an identifier that already exist is practically zero,
        # but just to be absolutely sure the creation is tried until unique identifier is created.
        # Infinite looping extremely unlikely.
        while True:
            identifier, submission_dt = create_identifier('review_')
            if Review.query.filter_by(identifier=identifier).count() == 0:
                break
        original_user = review_item.user.username
        # Get the arguments from the request
        user = request.json['user'].lower()  # Lowercase just in case
        title = request.json['title']
        content = request.json['content']
        star_rating = request.json['star_rating']

        # Check whether provided user in the request matches the current writer of the review
        if user != original_user:
            return create_error_response(
                409, 'Username does not match',
                'Provided user "{}" has not submitted this review'.format(
                    user))

        # Update review values and commit
        review_item.identifier = identifier
        review_item.title = title
        review_item.content = content
        review_item.star_rating = star_rating
        review_item.submission_date = submission_dt

        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return create_error_response(
                409, 'Unexpected conflict',
                'An unexpected conflict happened while committing to the database'
            )

        return Response(
            status=201,
            headers={
                'Location':
                url_for('api.reviewitem', album=album,
                        review=identifier)  # The location of the updated item
            })
コード例 #13
0
    def get(self):
        """
        Responds to GET request with a listing of review items known to the API (JSON document with added hypermedia controls (MASON))
        Query parameters in the request URL can be used to filter the returned reviews.
        """
        body = RevMusicBuilder()
        body.add_namespace('revmusic', LINK_RELATIONS_URL)
        body.add_control_reviews_all('self')
        body.add_control_users_all()
        body.add_control_albums_all()

        # Obtain query parameters sent by user
        args = self.parse.parse_args()
        reviews = []
        foreign_keys = []
        timeframe = []
        nlatest = args['nlatest']  # None or int

        if args['timeframe'] is not None:
            try:
                temp = args['timeframe'].split('_')
                if not temp[0][4:].isnumeric() or not temp[0][2:4].isnumeric(
                ) or not temp[0][0:2].isnumeric():
                    return create_error_response(415,
                                                 'Incorrect timeframe format')
                try:
                    datetime.date(int(temp[0][4:]), int(temp[0][2:4]),
                                  int(temp[0][0:2]))
                except ValueError:
                    return create_error_response(415,
                                                 'Incorrect timeframe format')
                timeframe.append('{}-{}-{}'.format(temp[0][4:], temp[0][2:4],
                                                   temp[0][0:2]))
                # Check for possible second time
                if len(temp) is 2:
                    if not temp[1][4:].isnumeric() or not temp[1][
                            2:4].isnumeric() or not temp[1][0:2].isnumeric():
                        return create_error_response(
                            415, 'Incorrect timeframe format')
                    try:
                        datetime.date(int(temp[1][4:]), int(temp[1][2:4]),
                                      int(temp[1][0:2]))
                    except ValueError:
                        return create_error_response(
                            415, 'Incorrect timeframe format')
                    timeframe.append('{}-{}-{}'.format(temp[1][4:],
                                                       temp[1][2:4],
                                                       temp[1][0:2]))
                # Make sure no more than 2 timeframes were provided
                if len(temp) > 2:
                    raise Exception('More than two timeframe parameters')
            except Exception as e:
                # Return an error if an exception occurred
                return create_error_response(
                    415, 'Incorrect timeframe format',
                    'You provided an incorrect timeframe format. Please fix that >:( {}'
                    .format(e))

        # Error handling for nlatest is implemented by flask, since the type has been set to int

        # Handle filtering
        if not args['searchword']:
            # No searchword
            if len(timeframe) < 1:
                # No timeframe provided, return all or nlatest
                reviews = Review.query.order_by(
                    Review.submission_date.desc()).limit(nlatest).all()
            elif len(timeframe) == 1:
                # One time provided, return all or nlatest after that
                reviews = Review.query.filter(func.date(Review.submission_date) >= timeframe[0])\
                    .order_by(Review.submission_date.desc()).limit(nlatest).all()
            else:
                # Two times provided, return all or nlatest between them
                reviews = Review.query.filter(func.date(Review.submission_date) >= timeframe[0])\
                    .filter(func.date(Review.submission_date) <= timeframe[1]).order_by(Review.submission_date.desc()).limit(nlatest).all()

        else:
            # Handle filtering
            if args['filterby'] == 'album':
                foreign_keys = Album.query.filter(
                    Album.title.contains(args['searchword'])).all()
            elif args['filterby'] == 'artist':
                foreign_keys = Album.query.filter(
                    Album.artist.contains(args['searchword'])).all()
            elif args['filterby'] == 'genre':
                foreign_keys = Album.query.filter(
                    Album.genre.contains(args['searchword'])).all()
            else:  # Filter by users
                foreign_keys = User.query.filter(
                    User.username.contains(args['searchword'])).all()

            if len(timeframe) < 1:
                reviews = Review.query.filter(
                    Review.album_id.in_([
                        fk.id for fk in foreign_keys
                    ])).order_by(
                        Review.submission_date.desc()).limit(nlatest).all()
            elif len(timeframe) == 1:
                reviews = Review.query.filter(Review.album_id.in_([fk.id for fk in foreign_keys])).filter(func.date(Review.submission_date) >= timeframe[0])\
                    .order_by(Review.submission_date.desc()).limit(nlatest).all()
            else:
                reviews = Review.query.filter(Review.album_id.in_([fk.id for fk in foreign_keys])).filter(func.date(Review.submission_date) >= timeframe[0])\
                    .filter(func.date(Review.submission_date) <= timeframe[1]).order_by(Review.submission_date.desc()).limit(nlatest).all()

        body['items'] = []
        for review in reviews:
            item = RevMusicBuilder(identifier=review.identifier,
                                   user=review.user.username,
                                   album=review.album.title,
                                   title=review.title,
                                   star_rating=review.star_rating,
                                   submission_date=datetime.datetime.strftime(
                                       review.submission_date,
                                       '%Y-%m-%d %H:%M:%S'))
            item.add_control(
                'self',
                url_for('api.reviewitem',
                        album=review.album.unique_name,
                        review=review.identifier))
            item.add_control('profile', REVIEW_PROFILE)
            body['items'].append(item)
        return Response(json.dumps(body), 200, mimetype=MASON)
コード例 #14
0
    def post(self, album):
        """
        Responds to POST request by adding a new review item to the specified album's review collection.
        If no errors happens while adding the review, the location of the new item is returned in the 'Location' response header.
        Otherwise an appropriate error code with a human-readable error message is returned.
        : param str album: the unique name of the album to which a new review is added, provided in the request URL
        """
        if not request.json:
            return create_error_response(415, 'Unsupported media type',
                                         'Use JSON')
        try:
            validate(request.json, Review.get_schema())
        except ValidationError as e:
            return create_error_response(400, 'Invalid JSON document', str(e))

        # Does the album for which the review is being submitted exist
        album_item = Album.query.filter_by(unique_name=album).first()
        if not album_item:
            return create_error_response(404, 'Album not found')

        # Create an unique identifier and get the submission datetime for the review
        # Note: the probability of creating an identifier that already exist is practically zero,
        # but just to be absolutely sure the creation is tried until unique identifier is created.
        # Infinite looping extremely unlikely.
        while True:
            identifier, submission_dt = create_identifier('review_')
            if Review.query.filter_by(identifier=identifier).count() == 0:
                break

        # Get the arguments from the request
        user = request.json['user'].lower()  # Lowercase just in case
        title = request.json['title']
        content = request.json['content']
        star_rating = request.json['star_rating']

        # Does the user by which the review is being submitted exist (provided in the request body)
        user_item = User.query.filter_by(username=user).first()
        if not user_item:
            return create_error_response(404, 'User not found')

        # Create a new review entry
        review = Review(identifier=identifier,
                        user=user_item,
                        album=album_item,
                        title=title,
                        content=content,
                        star_rating=star_rating,
                        submission_date=submission_dt)

        # Attempt to add to database
        try:
            db.session.add(review)
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return create_error_response(
                409, 'Already exists',
                'User "{}" has already submitted a review to album with title "{}"'
                .format(user, album_item.title))

        # Respond to successful request
        return Response(
            status=201,
            headers={
                'Location':
                url_for('api.reviewitem', album=album,
                        review=identifier)  # The location of the added item
            })