class UserView:
    user_app = Blueprint('user_app', __name__, url_prefix='/user')

    @user_app.route("", methods=["POST"], endpoint='sign_up')
    @validate_params(
        Param('full_name', JSON, str, required=True),
        Param('email', JSON, str, required=True,
              rules=[Pattern(r'^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$')]),
        Param('password', JSON, str,
              rules=[MaxLength(80)]),
        Param('password', JSON, str,
              rules=[MinLength(4)]),
        Param('auth_type_id', JSON, int)
    )
    def sign_up(*args):
        if args[4] not in range(1, 3):
            return jsonify({'message': 'INVALID_AUTH_TYPE'}), 400

        user_info = {
            'full_name': args[0],
            'email': args[1],
            'password': args[3],
            'auth_type_id': args[4]
        }
        user_service = UserService()
        result = user_service.sigh_up(user_info)
        return result

    @user_app.route("/sign-in", methods=["POST"], endpoint='sign_in')
    @validate_params(
        Param('email', JSON, str, required=True,
              rules=[Pattern(r'^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$')]),
        Param('password', JSON, str,
              rules=[MaxLength(80)]),
        Param('password', JSON, str,
              rules=[MinLength(4)])
    )
    def sign_in(*args):
        user_info = {
            'email': args[0],
            'password': args[2]
        }
        user_service = UserService()
        result = user_service.sign_in(user_info)
        return result

    @user_app.route("/log-out", methods=["POST"], endpoint='log_out')
    @validate_params(
        Param('key', JSON, str)
    )
    def log_out(*args):
        redis_key = args[0]
        user_service = UserService()
        result = user_service.log_out(redis_key)
        return result
def run_app():
    app = Flask(__name__)
    default_request_validator = validate_params(
        Param('arg1', JSON, str,
              rules=[Pattern(r'^\-?\d{1,}(\.?\d{1,6}$)?$')]),
        Param('arg2', JSON, str,
              rules=[Pattern(r'^\-?\d{1,}(\.?\d{1,6}$)?$')]))

    @app.errorhandler(InvalidRequest)
    def handle_exception(e):
        response = json.dumps({"error": e.message})
        return Response(response, mimetype='application/json'), 400

    @app.route('/v1/add', methods=['POST'])
    @default_request_validator
    def addition(arg1, arg2):
        return prepare_response(Decimal(arg1) + Decimal(arg2))

    @app.route('/v1/diff', methods=['POST'])
    @default_request_validator
    def difference(arg1, arg2):
        return prepare_response(Decimal(arg1) - Decimal(arg2))

    @app.route('/v1/multi', methods=['POST'])
    @default_request_validator
    def multiplication(arg1, arg2):
        return prepare_response(Decimal(arg1) * Decimal(arg2))

    @app.route('/v1/div', methods=['POST'])
    @default_request_validator
    def division(arg1, arg2):
        arg2 = Decimal(arg2)
        if arg2 == 0:
            raise InvalidRequest('Division by zero!')
        return prepare_response(Decimal(arg1) / arg2)

    return app
    try:
        datetime.datetime.strptime(date_string, '%Y-%m-%d')
    except ValueError:
        return False
    return True

def get_JWT_user():
    user_id = get_jwt_identity()
    return User.query.get(user_id)

def try_later():
    return {"error": "Something wrong. Try later."}, 400

@app.route('/api/register', methods=['POST'])
@validate_params(
    Param('login', JSON, str, rules=[Pattern(r'^[a-z_0-9\-.]{5,64}$')]),
    Param('password', JSON, str, rules=[Pattern(r'^[a-z_0-9\-.!$&]{5,15}$')])
)
def register(login, password):
    if User.query.filter_by(login=login).first():
        #To prevent iterating over existing values
        return try_later()
    user = User(login=login)
    user.set_password(password)
    db.session.add(user)
    try:
        db.session.commit()
    except Exception as err:
        return try_later()
    return {'user': {"id": user.id, "login": user.login}}, 201
Exemple #4
0
def create_product_endpoints(product_service):
    product_app = Blueprint('products', __name__, url_prefix='/products')

    @product_app.route('/product_list', methods=['GET'])
    #@login_required
    @validate_params(
        Param('registerStatus', GET, bool, required=False),
        Param('filterDateFrom',
              GET,
              str,
              rules=[Pattern(r"^\d\d\d\d-\d{1,2}-\d{1,2}$")],
              required=False),
        Param('filterDAteTo',
              GET,
              str,
              rules=[Pattern(r"^\d\d\d\d-\d{1,2}-\d{1,2}$")],
              required=False),
        Param('sellerName', GET, required=False),
        Param('productName', GET, required=False),
        Param('product_id', GET, list, required=False),
        Param('productCode', GET, required=False),
        Param('sellerType', GET, list, required=False),  #str이 아니라 int
        Param('salesStatus', GET, int, rules=[Enum(1, 2, 3)], required=False),
        Param('displayStatus', GET, int, rules=[Enum(1, 2, 3)],
              required=False),
        Param('discountStatus',
              GET,
              int,
              rules=[Enum(1, 2, 3)],
              required=False),
        Param('offset', GET, int, required=False),
        Param('limit', GET, int, required=False))
    def get_products(*args):
        """상품 정보 리스트 전달 API

        쿼리 파라미터로 필터링에 사용될 값을 받아 필터링된 상품의 데이터 리스트를 보내줍니다.

        args:
            *args:
                filterDateFrom : 조회기간 시작
                filterDateTo   : 조회기간 끝
                sellerName     : 셀러 이름 검색을 위한 파라미터
                productName    : SelectFilter 안에 있는 목록 중 상품이름
                productNo      : SelectFilter 안에 있는 목록 중 상품번호
                productCode    : SelectFilter 안에 있는 목록 중 상품코드
                sellerType     : 셀러속성 id
                salesStatus    : 판매여부
                displayStatus  : 진열여부
                discountStatus : 할인여부
            }
        
        returns :
            200: 상품리스트
            500: Exception
        
        Author:
            김성진 
        
        History:
            2020-11-01 (김성진): 초기 생성

        """

        try:
            if args[1]:
                filterDateFrom = datetime.datetime.strptime(
                    args[1], '%Y-%m-%d')
            else:
                filterDateFrom = ""

            if args[2]:
                filterDateTo = datetime.datetime.strptime(args[2], '%Y-%m-%d')
            else:
                filterDateTo = ""

            db_connection = get_connection()

            filter_dict = {
                'registerStatus': args[0],
                'filterDateFrom': args[1],  #조회기간 시작
                'filterDateTo': args[2],  #조회기간 끝
                'sellerName': args[3],  #셀러명
                'productName': args[4],  #상품이름
                'product_id': args[
                    5],  #상품번호                                                                       
                'productCode': args[6],  #상품코드
                'sellerType': args[7],  #셀러속성
                'salesStatus': args[8],  #판매여부
                'displayStatus': args[9],  #진열여부
                'discountStatus': args[10],  #할인여부
                'offset':
                0 if args[11] is None else args[11],  #데이터를 어디서부터 받아올것인지
                'limit': 10 if args[12] is None else args[12]  #페이지의 숫자
            }

            total_number = product_service.get_total_number(
                filter_dict, db_connection)
            products = product_service.get_products(filter_dict, db_connection)

            return jsonify({
                'products': products,
                'total_number': total_number
            })

        except InvalidProductInformationError as e:
            db_connection.rollback()
            message = internal_code_sheet[e.code]
            return jsonify(message), (message['code'])

        except InvalidSellerInformationError as e:
            db_connection.rollback()
            message = internal_code_sheet[e.code]
            return jsonify(message), (message['code'])

        finally:
            db_connection.close()

    @product_app.route('/excel', methods=['GET'])
    #@login_required
    @validate_params(
        Param('product_id', GET, list, required=False), )
    def make_excel(*args):
        """ 상품 정보 엑셀 다운로드 API

        전체 상품 또는 선택 상품의 정보를 excel 파일로 다운로드 합니다.

        args:
            product_id : 상품의 id 리스트

        returns:
            200: Excel 파일 다운
            500: Exception
        
        Author:
            김성진
        
        History:
            2020-11-04 (김성진): 초기 생성
        """

        db_connection = get_connection()
        try:
            # 선택한 상품들의 id를 list로 받는다.
            product_id_list = {
                'product_id': args[0],
            }

            excels = product_service.create_excel(product_id_list,
                                                  db_connection)
            return jsonify(excels)

        except ProgrammingError as e:
            db_connection.rollback()
            message = internal_code_sheet[e.code]
            return jsonify(message), (message['code'])

        finally:
            db_connection.close()

    @product_app.route('/modify', methods=['POST'])
    #@login_required
    @validate_params(
        Param('salesStatusModify',
              GET,
              int,
              rules=[Enum(1, 2, 3)],
              required=False),  #안에 NULL=True 해도 되나?
        Param('displayStatusModify',
              GET,
              int,
              rules=[Enum(1, 2, 3)],
              required=False),
        Param('product_id', GET, list, required=False))
    def status_update(*args):
        """ 상품의 판매여부 및 진열여부 수정 API
        
        선택한 상품의 판매여부 및 진열여부 수정이 있을시 수정.

        args:
            product_id: 상품의 id 리스트
        
        returns:
            200: 지정된 상품의 판매 및 진열여부 수정
            500: Exception
        
        Author:
            김성진
        
        History:
            2020-11-04 (김성진): 초기 생성
        """
        try:
            db_connection = get_connection()

            product_id_list = {
                'salesStatusModify': args[0],
                'displayStatusModify': args[1],
                'product_id': args[2]
            }

            fixed_products = product_service.product_status_change(
                product_id_list, db_connection)

            db_connection.commit()

        except InvalidChoiceMade as e:
            db_connection.rollback()
            message = internal_code_sheet[e.code]
            return jsonify(message), (message['code'])

        else:
            return jsonify(fixed_products)

        finally:
            db_connection.close()

    return product_app
class ProductView:
    product_app = Blueprint('product_app', __name__, url_prefix='/products')

    @product_app.route('', methods=['GET'])
    @login_validator
    @validate_params(
        Param('started_date',
              GET,
              str,
              required=False,
              rules=[
                  Pattern(
                      r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$")
              ]),
        Param('ended_date',
              GET,
              str,
              required=False,
              rules=[
                  Pattern(
                      r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$")
              ]),
        Param('seller_name', GET, str, required=False),
        Param('product_name', GET, str, required=False),
        Param('product_number', GET, str, required=False),
        Param('product_code', GET, str, required=False),
        Param('seller_subcategory_id', GET, list, required=False),
        Param('is_selling', GET, bool, rules=[Enum(0, 1)], required=False),
        Param('is_visible', GET, bool, rules=[Enum(0, 1)], required=False),
        Param('is_discount', GET, bool, rules=[Enum(0, 1)], required=False),
        Param('limit', GET, int, rules=[Enum(10, 20, 50)], required=False),
        Param('page', GET, int, required=False),
    )
    def get_product_list(*args):
        connection = None
        """상품 리스트 엔드포인트
        상품 관리 페이지에서 필터링된 상품 리스트를 표출
        쿼리 파라미터로 필터링에 사용할 파라미터 값을 받음     
        
        Return:
            200: 상품 리스트
            403: NO_AUTHORIZATION
            500: NO_DATABASE_CONNECTION, DB_CURSOR_ERROR
                 NO_DATABASE_CONNECTION
        History:
            2020-11-28 : 초기 생성
            2020-11-19 : pagination 수정
                 
        수정할 사항
        @login_validator로 g.account_info 받을 예정
            g.account_info ={
                'account_id' :  ,
                'account_type_id' :  ,
                'seller_id' : id or None
            }
        """

        #유효성 검사 완료한 쿼리 값 저장
        filter_data = {
            'started_date': args[0],
            'ended_date': args[1],
            'seller_name': args[2],
            'product_name': args[3],
            'product_number': args[4],
            'product_code': args[5],
            'seller_subcategory_id': args[6],
            'is_selling': args[7],
            'is_visible': args[8],
            'is_discount': args[9],
            'limit': args[10],
            'page': args[11],
            'account_type_id': g.token_info['account_type_id']
        }

        try:
            connection = connect_db()

            if connection:
                product_service = ProductService()
                products = product_service.get_product_list(
                    filter_data, connection)
                return jsonify(products), 200
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 400

        finally:
            try:
                connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route('/category', methods=['GET'])
    @login_validator
    @validate_params(Param('seller_name', GET, str, required=False))
    def get_main_category(*args):
        """ 상품 분류별 main 카테고리 표출 엔드포인트
        - master이면 seller검색
        - seller이면 validator 본인 id

        Return:
            200: 셀러가 속한 상품 분류에 따른 1차 카테고리 이름과 id
                 {  "main_category_id": 8,
                    "main_category_name": "주얼리"}
            400: 데이터베이스 연결 에러
            500: server error"""
        connection = None
        # 마스터인데 셀러이름 검색 안한 경우(커넥션 열기전에 처리해줌)
        if g.token_info['account_type_id'] == 1 and args[0] is None:
            return jsonify({'message': 'SELLER_CATEGORY_SEARCHING_ERROR'}), 400

        try:
            connection = connect_db()
            if connection:

                filter_data = {
                    'account_type_id': g.token_info['account_type_id'],
                    'seller_name': args[0]
                }

                product_service = ProductService()
                main_categories_data = product_service.product_main_category(
                    filter_data, connection)
        except Exception as e:
            return jsonify({'message': f'{e}'}), 400
        else:
            return jsonify(main_categories_data), 200
        finally:
            try:
                connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route('/category/<int:main_category_id>', methods=['GET'])
    @login_validator
    @validate_params(
        Param('main_category_id', PATH, int),
        #유효한 카테고리 범위 벗어날 시 에러 반환
        Param('main_category_id',
              PATH,
              str,
              rules=[Pattern(r'^([0-9]|[0-3][0-9])$')]),
    )
    def get_sub_category(*args):
        """ 상품 분류별 sub 카테고리 목록 표출 엔드포인트
        Args:
            *args:
                main_category_id(int): 1차 카테고리 인덱스 번호
        """
        connection = None

        #쿼리 스트링에 값이 없으면 에러 반환
        if args[0] is None:
            return jsonify({'message': 'MAIN_CATEGORY_ID_ERROR'}), 400

        try:
            connection = connect_db()

            if connection:
                filter_data = {'main_category_id': args[0]}
                product_service = ProductService()
                sub_categories = product_service.product_sub_category(
                    filter_data, connection)

                return jsonify({'data': sub_categories}), 200
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 400

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route('/register', methods=['GET'])
    def get_option_list(*args):
        connection = None
        try:
            connection = connect_db()
            if connection:
                product_service = ProductService()
                options = product_service.get_options(connection)
                return options
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500
        finally:
            try:
                connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route('/register', methods=['POST'])
    @login_validator
    @validate_params(
        Param('is_selling', FORM, int, rules=[Enum(0, 1)]),
        Param('is_visible', FORM, int, rules=[Enum(0, 1)]),
        Param('sub_category_id', FORM, int, required=False),
        Param('product_name', FORM, str, rules=[Pattern(r"[^\"\']")]),
        Param('is_information_notice', FORM, int, rules=[Enum(0, 1)]),
        Param('manufacturer', FORM, str, required=False),
        Param('manufacture_date',
              FORM,
              str,
              required=False,
              rules=[
                  Pattern(
                      r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$")
              ]),
        Param('made_in', FORM, str, required=False),
        Param('short_description', FORM, str, required=False),
        Param('is_inventory_management', FORM, int, rules=[Enum(0, 1)]),
        Param('inventory', FORM, int, required=False),
        Param('price', FORM, int),
        Param('discount_rate', FORM, float, required=False),
        Param('is_discount_period', FORM, int, rules=[Enum(0, 1)]),
        Param('discount_start_time', FORM, str, required=False),
        Param('discount_end_time', FORM, str, required=False),
        Param('min_order', FORM, int),
        Param('max_order', FORM, int),
        Param('seller_id', FORM, int, required=False),
        # integer parameter 범위 지정을 위한 검증
        Param('is_selling', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('is_visible', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('sub_category_id',
              FORM,
              str,
              rules=[Pattern(r'^([0-9]|[0-9][0-9]|[1][0][0-9]|[1][1][0-4])$')
                     ]),
        Param('max_order', FORM, str,
              rules=[Pattern(r'^([1-9]|[1-2][0-9])$')]),
        Param('min_order', FORM, str,
              rules=[Pattern(r'^([1-9]|[1-2][0-9])$')]))
    def create_product(*args):
        connection = None

        # min_order, max_order 예외처리
        if args[16] > 20 or args[17] > 20:
            return jsonify({'message': 'ORDER_VALUE_ERROR'}), 400

        filter_data = {
            'editor_id': g.token_info['account_id'],
            'is_selling': args[0],
            'is_visible': args[1],
            'sub_category_id': args[2],
            'product_name': args[3],
            'is_information_notice': args[4],
            'manufacturer': args[5],
            'manufacture_date': args[6],
            'made_in': args[7],
            'short_description': args[8],
            'is_inventory_management': args[9],
            'inventory': args[10],
            'price': args[11],
            'discount_rate': args[12],
            'is_discount_period': args[13],
            'discount_start_datetime': args[14],
            'discount_end_datetime': args[15],
            'min_order': args[16],
            'max_order': args[17]
        }
        if args[18]:
            filter_data['seller_id'] = args[18]
        else:
            filter_data['seller_id'] = g.token_info['seller_id']

        print(filter_data['seller_id'])

        try:
            connection = connect_db()

            if connection:

                # option_list = '[{"name":"bb","age":29},{"name":"hh","age":20}]'
                options = request.form.get('option_list')
                option_list = json.loads(options)

                #image 저장을 위한 S3 connection instance 생성
                """
                images : File Request(List)
                [
                    { 'product_image_<int>' : <FileStorage: {filename} ({content_type})>}
                ]
                """
                images = request.files
                image_bucket_dir = datetime.datetime.now().strftime('%Y-%m-%d')

                desc_image = request.files.get('desc_image')
                desc_image_url = Image_uploader.upload_desc_images(
                    desc_image, image_bucket_dir)

                filter_data['desc_img_url'] = desc_image_url

                product_service = ProductService()
                create_info = product_service.create_product(
                    filter_data, option_list, connection)

                product_id = create_info['product_id']
                editor_id = filter_data['editor_id']
                insert_count = create_info['create_count']

                #상품 이미지 URL화, S3에 올리기
                product_images = Image_uploader.upload_product_images(
                    images, image_bucket_dir)

                #상품 이미지를 DB에 Insert하는 함수 실행
                product_service.upload_product_image(product_images,
                                                     product_id, editor_id,
                                                     connection)

                connection.commit()
                return jsonify(
                    {'message': f'{insert_count}products are created'}), 201
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except KeyError:
            connection.rollback()
            return jsonify({'message': 'KEY_ERROR'}), 400

        except Exception as e:
            connection.rollback()
            # raise e
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route('/download', methods=['GET'])
    @login_validator
    @validate_params(
        Param('started_date',
              GET,
              str,
              required=False,
              rules=[
                  Pattern(
                      r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$")
              ]),
        Param('ended_date',
              GET,
              str,
              required=False,
              rules=[
                  Pattern(
                      r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$")
              ]), Param('seller_name', GET, str, required=False),
        Param('product_name', GET, str, required=False),
        Param('product_number', GET, str, required=False),
        Param('product_code', GET, str, required=False),
        Param('seller_subcategory_id', GET, list, required=False),
        Param('is_selling', GET, bool, rules=[Enum(0, 1)], required=False),
        Param('is_visible', GET, bool, rules=[Enum(0, 1)], required=False),
        Param('is_discount', GET, bool, rules=[Enum(0, 1)], required=False),
        Param('limit', GET, int, required=False),
        Param('page', GET, int, required=False))
    def product_list_excel(*args):
        connection = None

        filter_data = {
            'started_date': args[0],
            'ended_date': args[1],
            'seller_name': args[2],
            'product_name': args[3],
            'product_number': args[4],
            'product_code': args[5],
            'seller_subcategory_id': args[6],
            'is_selling': args[7],
            'is_visible': args[8],
            'is_discount': args[9],
            'account_type_id': g.token_info['account_type_id'],
            'account_id': g.token_info['account_id']
        }

        try:
            connection = connect_db()
            if connection:
                product_service = ProductService()

                product_service.product_excel(filter_data, connection)

                return jsonify({'message': 'SUCCESS'}), 201
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 400

        finally:
            try:
                connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500
class ProductView:
    """
    프로덕트 뷰
    """
    product_app = Blueprint('product_app', __name__, url_prefix='/product')

    @product_app.route('', methods=['GET'], endpoint='get_product_list')
    @login_required
    @validate_params(
        Param('period_start',
              GET,
              str,
              required=False,
              rules=[
                  Pattern(
                      r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$")
              ]),
        Param('period_end',
              GET,
              str,
              required=False,
              rules=[
                  Pattern(
                      r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$")
              ]),
        Param('seller_name', GET, str, required=False),
        Param('product_name', GET, str, required=False),
        Param('product_number', GET, int, required=False),

        # 셀러 속성은 다중 값이 들어올 수 있어서 리스트로 받음
        Param('seller_type_id', GET, list, required=False),
        Param('is_available', GET, int, required=False),
        Param('is_on_display', GET, int, required=False),
        Param('is_on_discount', GET, int, required=False),
        Param('offset', GET, int),
        Param('limit', GET, int),
        Param('is_available',
              GET,
              str,
              required=False,
              rules=[Pattern(r"^[0-1]{1}$")]),
        Param('is_on_display',
              GET,
              str,
              required=False,
              rules=[Pattern(r"^[0-1]{1}$")]),
        Param('is_on_discount',
              GET,
              str,
              required=False,
              rules=[Pattern(r"^[0-1]{1}$")]))
    def get_product_list(*args):
        """ 상품 리스트 표출 엔드포인트

        상품 관리 페이지에서 표출되는 필터링된 상품 리스트를 표출합니다.
        쿼리 파라미터로 필터링에 사용할 파라미터 값을 받습니다.

        Returns:
            200: 상품 리스트
            403: NO_AUTHORIZATION
            500: NO_DATABASE_CONNECTION, DB_CURSOR_ERROR
                 NO_DATABASE_CONNECTION

        Authors:
            [email protected] (김승준)
            [email protected] (이종민)

        History:
            2020-04-09 ([email protected]): 초기 생성
            2020-04-13 ([email protected]): 수정
                - 주석 내용 보완
                - 쿼리파라미터 유효성 검사 추가
                - 마스터 권한이 아니면 접근 불가 처리(NO_AUTHORIZATION)
                - db connection try/except 추가
                - 셀러속성 쿼리 값을 리스트 형태로 받도록 변경
        """

        # 마스터 권한이 아니면 에러 반환
        if g.account_info['auth_type_id'] != 1:
            return jsonify({'message': 'NO_AUTHORIZATION'}), 403

        # 유효성 확인 위해 기간 데이터 먼저 정의
        period_start, period_end = args[0], args[1]

        # 두 값이 모두 들어왔을 때, 시작 기간이 종료 기간보다 늦으면 시작기간 = 종료기간
        if period_start and period_end:
            if period_end < period_start:
                period_start = period_end

        # 두 값이 각각 안들어왔을 경우 default 값 설정
        if not period_start:
            period_start = '2016-07-01'

        if not period_end:
            period_end = '2037-12-31'

        # seller_type_id 보정(아이디가 하나만 들어올 경우 튜플로 만들 때 오류가 생겨서 보정)
        seller_type_id = args[5]
        if seller_type_id:
            seller_type_id.append(0)

        # 유효성 검사를 통과한 쿼리 값을 filter_info 에 저장
        filter_info = {
            # '2020-04-14' 형식으로 들어오는 기간 데이터 변환
            'period_start': period_start + ' 00:00:00',
            'period_end': period_end + ' 23:59:59',
            'seller_name': args[2],
            'product_name': args[3],
            'product_number': args[4],
            'seller_type_id': seller_type_id,
            'is_available': args[6],
            'is_on_display': args[7],
            'is_on_discount': args[8],
            'offset': args[9],
            'limit': args[10]
        }

        # offset 과 limit 에 음수가 들어오면 default 값 지정
        if filter_info['offset'] < 0:
            filter_info['offset'] = 0

        if filter_info['limit'] < 0:
            filter_info['limit'] = 10

        try:
            db_connection = get_db_connection()
            if db_connection:
                product_service = ProductService()
                product_list_result = product_service.get_product_list(
                    filter_info, db_connection)
                return product_list_result

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route("/<int:product_no>",
                       methods=["GET"],
                       endpoint='get_product_detail')
    @login_required
    def get_product_detail(product_no):
        """ 상품 등록/수정시 나타나는 개별 상품의 기존 정보 표출 엔드포인트

        상품의 번호를 path parameter 로 받아 해당하는 상품의 기존 상세 정보를 표출.

        Args:
            product_no(integer): 상품 id

        Returns:
            200: 상품별 상세 정보
            500: 데이터베이스 에러

        Authors:
            [email protected] (이소헌)

        History:
            2020-04-03 ([email protected]): 초기 생성
            2020-04-07 ([email protected]): 파라미터 변수를 product_info_no -> product_no로 변경
            2020-04-16 ([email protected]): 사용하지 않는 parameter validator 삭제
        """

        try:
            db_connection = get_db_connection()
            if db_connection:
                product_service = ProductService()
                product_infos = product_service.get_product_detail(
                    product_no, db_connection)
                return product_infos

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route("/category", methods=["GET"])
    @login_required
    @validate_params(Param('account_no', GET, int, required=False))
    def get_first_categories(*args):
        """ 상품 분류별 1차 카테고리 표출 엔드포인트

        Returns:
            200: 셀러가 속한 상품 분류에 따른 1차 카테고리
            400: 데이터베이스 연결 에러
            500: server error

        Authors:
            [email protected] (이소헌)

        History:
            2020-04-02 ([email protected]): 초기 생성
            2020-04-13 ([email protected]): 셀러 정보 얻어오는 경로를 token 내부 데이터에서 query string 으로 변경
        """
        account_info = {'account_no': args[0]}

        try:
            db_connection = get_db_connection()
            if db_connection:
                product_service = ProductService()
                categories = product_service.get_first_categories(
                    account_info, db_connection)
                return categories

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 400

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route("/category/<int:first_category_no>",
                       methods=["GET"],
                       endpoint='get_second_categories')
    @login_required
    @validate_params(
        Param('first_category_no', PATH, int),

        # 유효한 카테고리 범위 벗어날 시 에러 반환
        Param('first_category_no',
              PATH,
              str,
              rules=[Pattern(r'^([0-9]|[0-3][0-9])$')]),
    )
    def get_second_categories(*args):
        """ 상품 2차 카테고리 목록 표출

        선택된 상품 1차 카테고릭에 따라 해당하는 2차카테고리 목록 표출

        Args:
            *args:
                first_category_no(integer): 1차 카테고리 인덱스 번호

        Returns:
            200: 1차 카테고리에 해당하는 2차 카테고리 목록
            400: 데이터베이스 연결 에러
            500: server error

        Authors:
            [email protected] (이소헌)

        History:
            2020-04-02 ([email protected]): 초기 생성
            2020-04-07 ([email protected]): URL 구조 변경
        """
        first_category_no = args[0]

        try:
            db_connection = get_db_connection()
            if db_connection:
                product_service = ProductService()
                categories = product_service.get_second_categories(
                    db_connection, first_category_no)
                return categories

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 400

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route("/color", methods=["GET"])
    def get_color_filters():
        """ 상품 등록시 컬러 필터 표출 엔드포인트

        Returns:
            200: 상품 등록시 선택할 수 있는 색상 필터
            500: 데이터 베이스 에러

        Authors:
            [email protected] (이소헌)

        History:
            2020-04-09 ([email protected]): 초기 생성
        """
        try:
            db_connection = get_db_connection()
            if db_connection:
                product_service = ProductService()
                get_color_result = product_service.get_color_filters(
                    db_connection)
                return get_color_result

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route('', methods=['POST'], endpoint='insert_new_product')
    @login_required
    @validate_params(
        Param('is_available', FORM, int),
        Param('is_on_display', FORM, int),
        Param('first_category_id', FORM, int),
        Param('second_category_id', FORM, int, required=False),
        Param('name', FORM, str, rules=[Pattern(r"[^\"\']")]),
        Param('short_description', FORM, str, required=False),
        Param('color_filter_id', FORM, int),
        Param('style_filter_id', FORM, int),
        Param('long_description', FORM, str),
        Param('youtube_url', FORM, str, required=False),
        Param('stock', FORM, int),
        Param('price', FORM, int),
        Param('discount_rate', FORM, float),
        Param('discount_start_time', FORM, str, required=False),
        Param('discount_end_time', FORM, str, required=False),
        Param('min_unit', FORM, int),
        Param('max_unit', FORM, int),
        Param('tags', FORM, list, required=False),
        Param('selected_account_no', FORM, int, required=False),

        # integer parameter 범위 지정을 위한 검증
        Param('is_available', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('is_on_display', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('first_category_id',
              FORM,
              str,
              rules=[Pattern(r'^([0-9]|[0-3][0-9])$')]),
        Param('second_category_id',
              FORM,
              str,
              rules=[Pattern(r'^([0-9]|[0-9][0-9]|[1][0][0-9]|[1][1][0-4])$')
                     ]),
        Param('max_unit', FORM, str, rules=[Pattern(r'^([1-9]|[1-2][0-9])$')]),
        Param('min_unit', FORM, str, rules=[Pattern(r'^([1-9]|[1-2][0-9])$')]),
    )
    def insert_new_product(*args):
        """ 상품 등록 엔드포인트

        새로운 상품을 등록하는 엔드포인트.
        등록하려는 셀러의 정보에 따라 내부 내용이 달라지므로, 데코레이터에서 셀러 정보를 먼저 읽어옴.
        등록 상세 정보는 form 에 존재함.
        유효성 검사를 위한 조건 통과 후 product_info 변수에 내용을 담아 product_service 로 전달.

        Args:
            *args: 등록할 제품의 상세 정보

        g.account_info: 데코레이터에서 넘겨받은 수정을 수행하는 계정 정보
            auth_type_id: 계정의 권한정보
            account_no: 데코레이터에서 확인된 계정번호

        Returns: Http 응답코드
            200: 신규 상품 등록 성공
            500: 데이터베이스 에러

        Authors:
            [email protected] (이소헌)
            
        History:
            2020-04-06 ([email protected]): 초기 생성
            2020-04-14 ([email protected]): 이미지 순서 문제 캐치
            2020-04-15 ([email protected]): form data 형태로 받을 수 있도록 tags 자료형 변경 (str -> list)
        """
        image_uploader = ImageUpload()
        uploaded_images = image_uploader.upload_product_image(request)
        if (400 in uploaded_images) or (500 in uploaded_images):
            return uploaded_images

        # 상품 등록시 대표 사진인 1번 사진부터 들어와야함
        if not uploaded_images['image_file_1']:
            return jsonify({'message':
                            'REPRESENTATIVE_IMAGE_DOES_NOT_EXIST'}), 400

        # 1번 사진부터 순서대로 들어와야함
        for i in range(2, 6):
            if uploaded_images[f'image_file_{i}']:
                if not uploaded_images[f'image_file_{i-1}']:
                    return jsonify({'message':
                                    'IMAGES_SHOULD_BE_IN_ORDER'}), 400
        product_info = {
            'auth_type_id':
            g.account_info['auth_type_id'],
            'account_no':
            g.account_info['account_no'],
            'uploader':
            g.account_info['account_no'],
            'modifier':
            g.account_info['account_no'],
            'is_available':
            args[0],
            'is_on_display':
            args[1],
            'first_category_id':
            args[2],
            'second_category_id':
            args[3],
            'name':
            args[4],
            'short_description':
            args[5],
            'color_filter_id':
            args[6],
            'style_filter_id':
            args[7],
            'long_description':
            args[8],
            'youtube_url':
            args[9],
            'stock':
            args[10],
            'price':
            args[11],
            'discount_rate':
            args[12] / 100,
            'discount_start_time':
            str(
                datetime.strptime(args[13][:args[13].index('G') - 1],
                                  "%a %b %d %Y %H:%M:%S")),
            'discount_end_time':
            str(
                datetime.strptime(args[14][:args[14].index('G') - 1],
                                  "%a %b %d %Y %H:%M:%S")),
            'min_unit':
            args[15],
            'max_unit':
            args[16],
            'tags':
            args[17],
            'selected_account_no':
            args[18],
            'images':
            uploaded_images,
        }
        print(product_info)
        try:
            db_connection = get_db_connection()
            if db_connection:
                product_service = ProductService()
                product_insert_result = product_service.insert_new_product(
                    product_info, db_connection)
                return product_insert_result

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @product_app.route("/<int:product_id>",
                       methods=['PUT'],
                       endpoint='update_product_info')
    @login_required
    @validate_params(
        Param('is_available', FORM, int),
        Param('is_on_display', FORM, int),
        Param('product_sort_id', FORM, int),
        Param('first_category_id', FORM, int),
        Param('second_category_id', FORM, int),
        Param('name', FORM, str),
        Param('short_description', FORM, str, required=False),
        Param('color_filter_id', FORM, int),
        Param('style_filter_id', FORM, int),
        Param('long_description', FORM, str),
        Param('youtube_url', FORM, str, required=False),
        Param('stock', FORM, int),
        Param('price', FORM, int),
        Param('discount_rate', FORM, float),
        Param('discount_start_time', FORM, str, required=False),
        Param('discount_end_time', FORM, str, required=False),
        Param('min_unit', FORM, int),
        Param('max_unit', FORM, int),
        Param('tags', FORM, str, required=False),
        Param('product_id', PATH, int),
        Param('seller_account_no', FORM, int),
    )
    def update_product_info(*args):
        """ 상품 정보 수정 엔드포인트

        상품의 정보를 수정하는 엔드포인트.
        등록하려는 셀러의 정보에 따라 내부 내용이 달라지므로, 데코레이터에서 셀러 정보를 먼저 읽어옴.
        수정 상세 정보는 form 에 존재함.
        유효성 검사를 위한 조건 통과 후 product_info 변수에 내용을 담아 product_service로 전달.

        Args:
            *args: 등록할 제품의 상세 정보

        g.account_info: 데코레이터에서 넘겨받은 수정을 수행하는 계정 정보
            auth_type_id: 계정의 권한정보
            account_no: 데코레이터에서 확인된 계정번호

        Returns: Http 응답코드
            200: 상품 정보 수정 성공
            500: 데이터베이스 에러

        Authors:
            [email protected] (이소헌)

        History:
            2020-04-08 ([email protected]): 초기 생성
        """

        image_uploader = ImageUpload()
        uploaded_images = image_uploader.upload_product_image(request)

        # 이미지 업로더를 호출한 결과값에 애러코드 400이 포함되어있으면 utils.py 에서 발생한 에러메세지를 그대로 리턴
        if (400 in uploaded_images) or (500 in uploaded_images):
            return uploaded_images

        product_info = {
            'auth_type_id': g.account_info['auth_type_id'],
            'token_account_no': g.account_info['account_no'],
            'modifier': g.account_info['account_no'],
            'is_available': args[0],
            'is_on_display': args[1],
            'product_sort_id': args[2],
            'first_category_id': args[3],
            'second_category_id': args[4],
            'name': args[5],
            'short_description': args[6],
            'color_filter_id': args[7],
            'style_filter_id': args[8],
            'long_description': args[9],
            'youtube_url': args[10],
            'stock': args[11],
            'price': args[12],
            'discount_rate': args[13] / 100,
            'discount_start_time': args[14],
            'discount_end_time': args[15],
            'min_unit': args[16],
            'max_unit': args[17],
            'tags': json.loads(args[18]),
            'product_id': args[19],
            'seller_account_id': args[20],
            'images': uploaded_images,
        }

        try:
            db_connection = get_db_connection()
            if db_connection:
                product_service = ProductService()
                product_update_result = product_service.update_product_info(
                    product_info, db_connection)
                return product_update_result

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500
class AccountView:
    account_app = Blueprint('account_app', __name__, url_prefix='/account')

    @account_app.route('signup/master', methods=['POST'])
    @validate_params(
        Param('email', JSON, str,
              rules=[Pattern(r'^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$')]),
        Param('password', JSON, str,
              rules=[MaxLength(80), MinLength(4)]),
        Param('account_type_id', JSON, int),
        Param('account_type_id', JSON, str,
              rules=[Pattern(r"^[1-2]{1}$")]),
        Param('name', JSON, str, rules=[Pattern(r"^[가-힣]{1,20}$")]),
        Param('master_code', JSON, str, required=True)
    )
    def sign_up_master(*args):
        connection = None
        account_info = {
            'email': args[0],
            'password': args[1],
            'account_type_id': args[2],
            'name': args[4],
            'master_code': args[5]
        }

        connection = connect_db()
        if connection:
            account_service = AccountService()
            try:
                account_service.signup_account(account_info, connection)
                if account_info['master_code'] != 'brandi_team1':
                    connection.rollback()
                    return jsonify({'MESSAGE': 'WRONG_MASTER_CODE'})
                connection.commit()
                return jsonify({'MESSAGE': 'ACCOUNT_CREATED', }), 200
            except Exception as e:
                connection.rollback()
                return jsonify({'MESSAGE': f'{e}'}), 400
            finally:
                if connection:
                    connection.close()

    @account_app.route('signup/seller', methods=['POST'])
    @validate_params(
        Param('email', JSON, str,
              rules=[Pattern(r'^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$')]),
        Param('password', JSON, str, rules=[MaxLength(80), MinLength(4)]),
        Param('account_type_id', JSON, int),
        Param('account_type_id', JSON, str, rules=[Pattern(r"^[1-2]{1}$")]),
        Param('service_number', JSON, str, rules=[Pattern(r"^[0-9]{11}$|^[0-9]{12}$")]),
        Param('seller_name_kr', JSON, str, rules=[Pattern(r"^[가-힣0-9]{1,20}$")]),
        Param('seller_name_en', JSON, str, rules=[Pattern(
            r"^[a-zA-Z0-9àáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ð ,.'-]{1,20}$")]),
        Param('subcategory_id', JSON, str, rules=[Pattern(r"^[1-8]{1}$")])
    )
    def sign_up_seller(*args):
        connection = None
        try:
            account_info = {
                'email': args[0],
                'password': args[1],
                'account_type_id': args[2],
                'name': '미설정',
                'service_number': args[4],
                'seller_name_kr': args[5],
                'seller_name_en': args[6],
                'subcategory_id': args[7],
                'seller_status_id': 1
            }
            print(account_info)
            connection = connect_db()
            account_service = AccountService()
            account_service.signup_seller(account_info, connection)
            connection.commit()

            return jsonify({'MESSAGE': 'SUCCESS'}), 200
        except Exception as e:
            connection.rollback()
            return jsonify({'MESSAGE': f'{e}'}), 400
        finally:
            if connection:
                connection.close()

    @account_app.route('/signin', methods=['POST'])
    def sign_in():
        connection = None
        try:
            connection = connect_db()
            login_data = request.json
            account_service = AccountService()
            token = account_service.signin(login_data, connection)
            return token
        except Exception as e:
            return jsonify({'MESSAGE': f'{e}'}), 400
        finally:
            if connection:
                connection.close()


    @account_app.route('/seller_list', methods=['GET'])
    @login_validator
    @validate_params(
        Param('seller_id', GET, int, required=False),
        Param('account_id', GET, int, required=False),
        Param('email', GET, str, required=False),
        Param('seller_en', GET, str, required=False),
        Param('seller_kr', GET, str, required=False),
        Param('user_id', GET, int, required=False),
        Param('manager_name', GET, str, required=False),
        Param('manager_email', GET, str, required=False),
        Param('seller_status', GET, str, required=False),
        Param('manager_phone', GET, str, required=False),
        Param('seller_category', GET, str, required=False),
        Param('created_lower', GET, str, required=False),
        Param('created_upper', GET, str, required=False),
        Param('excel', GET, int, required=False),
        Param('page', GET, int, required=False),
        Param('limit', GET, int, required=False),
        Param('order_by', GET, str, required=False, rules=[Enum('asc', 'desc')])
    )
    def list_sellers(*args):
        db_connection = None
        user = g.token_info
        filter_info = {
            'seller_id': args[0],
            'account_id': args[1],
            'email': args[2],
            'seller_en': args[3],
            'seller_kr': args[4],
            'user_id': args[5],
            'manager_name': args[6],
            'manager_email': args[7],
            'seller_status': args[8],
            'manager_phone': args[9],
            'seller_category': args[10],
            'created_upper': args[11],
            'created_lower': args[12],
            'excel': args[13],
            'page': args[14] if args[14] else 1,
            'limit': args[15] if args[15] else 10,
            'order_by': args[16]
        }
        try:
            connection = connect_db()
            account_service = AccountService()
            seller_list = account_service.filter_seller(filter_info, user, connection)
            return jsonify({'seller_list': seller_list}), 200

        except Exception as e:
            return jsonify({'MESSAGE': f'{e}'}), 400
        finally:
            connection.close()

    @account_app.route('edit', methods=['PATCH'])
    @login_validator
    @validate_params(
        Param('email', JSON, str,
              rules=[Pattern(r'^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$')], required=False),
        Param('password', JSON, str,
              rules=[MaxLength(80), MinLength(4)], required=False),
        Param('account_type_id', JSON, int, required=False),
        Param('account_type_id', JSON, str,
              rules=[Pattern(r"^[1-2]{1}$")], required=False),
        Param('name', JSON, str, rules=[Pattern(r"^[가-힣]{1,20}$")], required=False),
        Param('account_id', JSON, int, required=True),
        Param('is_active', JSON, int, required=False)
    )
    def edit_account(*args):
        connection = None
        change_info = {
            'email': args[0],
            'password': args[1],
            'account_type_id': args[2],
            'name': args[4],
            'id': args[5],
            'is_active': args[6]
        }

        connection = connect_db()
        user = g.token_info
        if connection:
            account_service = AccountService()
            try:
                change_account = account_service.change_account_info(change_info, user, connection)
                connection.commit()
                return change_account
            except Exception as e:
                connection.rollback()
                return jsonify({'MESSAGE': f'{e}'}), 400
            finally:
                connection.close()

    @account_app.route('edit_seller', methods=['PATCH'])
    @login_validator
    @validate_params(
        Param('subcategory_id', JSON, int, required=False),
        Param('seller_status_id', JSON, int, required=False),
        Param('seller_name_kr', JSON, str, rules=[Pattern(r"^[가-힣0-9]{1,20}$")], required=False),
        Param('seller_name_en', JSON, str, rules=[Pattern(
            r"^[a-zA-Z0-9àáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ð ,.'-]{1,20}$")], required= False),
        Param('seller_number', JSON, str, rules=[Pattern(r"^[0-9]{11}$|^[0-9]{12}$")], required=False),
        Param('profile_pic_url', JSON, str, required=False),
        Param('short_desc', JSON, str, required=False),
        Param('long_desc', JSON, str, required=False),
        Param('open_time', JSON, str, rules=[Pattern('^(20)[\d]{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$')], required= False),
        Param('close_time', JSON, str, rules=[Pattern('^(20)[\d]{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$')], required= False),
        Param('delivery_policy', JSON, str, required=False),
        Param('return_policy', JSON, str, required=False),
        Param('zip_code', JSON, int, required=False),
        Param('address_1', JSON, str, required=False),
        Param('address_2', JSON, str, required=False),
        Param('is_open_weekend', JSON, int, required=False),
        Param('seller_id', JSON, int, required=True)
    )
    def edit_seller(*args):
        connection = None
        change_info = {
            'subcategory_id': args[0],
            'seller_status_id': args[1],
            'seller_name_kr': args[2],
            'seller_name_en': args[3],
            'seller_number': args[4],
            'profile_pic_url': args[5],
            'short_desc': args[6],
            'long_desc': args[7],
            'open_time': args[8],
            'close_time': args[9],
            'delivery_policy': args[10],
            'return_policy': args[11],
            'zip_code': args[12],
            'address_1': args[13],
            'address_2': args[14],
            'is_open_weekend': args[15],
            'seller_id': args[16]
        }
        connection = connect_db()
        user = g.token_info
        if connection:
            account_service = AccountService()
            try:
                change_account = account_service.change_seller_info(change_info, user, connection)
                connection.commit()
                return change_account
            except Exception as e:
                connection.rollback()
                return jsonify({'MESSAGE': f'{e}'}), 400
            finally:
                connection.close()


    @account_app.route('change_seller_status', methods=['PATCH'])
    @login_validator
    @validate_params(
        Param('action_id', JSON, str),
        Param('status_id', JSON, str),
        Param('seller_id', JSON, str)
    )
    def seller_actions(*args):
        connection = None
        status = {
            'action_id': args[0],
            'status_id': args[1],
            'seller_id': args[2]
        }
        user = g.token_info
        connection = connect_db()
        if connection:
            try:
                account_service = AccountService()
                account_service.change_status(status, user, connection)
                connection.commit()
                return jsonify({'status_actions': 'SUCCESS'}), 200
            except Exception as e:
                connection.rollback()
                return jsonify({'MESSAGE': f'{e}'}), 400
            finally:
                connection.close()
class SellerView:
    """ 셀러 뷰

    Authors:
        [email protected] (이소헌)

    History:
        2020-03-25 ([email protected]): 초기 생성

    """
    seller_app = Blueprint('seller_app', __name__, url_prefix='/seller')

    @seller_app.route('', methods=['POST'])
    @validate_params(
        Param('login_id',
              JSON,
              str,
              rules=[Pattern(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9_-]{4,19}')]),
        Param('password', JSON, str, rules=[MaxLength(80)]),
        Param('password', JSON, str, rules=[MinLength(4)]),
        Param('contact_number',
              JSON,
              str,
              rules=[Pattern(r'^[0-9]{3}-{1}[0-9]{4}-{1}[0-9]{4}$')]),
        Param('seller_type_id', JSON, int),
        Param('name_kr',
              JSON,
              str,
              rules=[Pattern(r'^[가-힣a-zA-Z0-9\ ]{1,45}$')]),
        Param('name_en', JSON, str, rules=[Pattern(r'^[a-z\ ]{1,45}$')]),
        Param('center_number',
              JSON,
              str,
              rules=[Pattern(r'^[0-9]{2,3}-{1}[0-9]{3,4}-{1}[0-9]{4}$')]),
        Param(
            'site_url',
            JSON,
            str,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]), Param('site_url', JSON, str, rules=[MaxLength(200)]),
        Param('kakao_id',
              JSON,
              str,
              required=False,
              rules=[Pattern(r'^[가-힣a-zA-Z0-9]{1,45}$')]),
        Param('insta_id',
              JSON,
              str,
              required=False,
              rules=[Pattern(r'^[a-zA-Z0-9]{1,45}$')]),
        Param('seller_type_id', JSON, str, rules=[Pattern(r"^[1-7]{1}$")]))
    def sign_up(*args):
        """ 계정 회원가입 엔드포인트

        회원가입 엔드포인트 입니다.
        request.body 로 회원가입에 필요한 정보를 받고,
        유효성 검사를 마친 정보를 account_info 에 담아
        service 에 전달합니다.

        Args:
            *args: 유효성 검사를 통과한 파라미터

        request.body:
            login_id 로그인 아이디 str
            password 비밀번호 str
            contact_number 담당자 번호 str
            seller_type_id 셀러 속성 아이디 int
            name_kr 셀러명 str
            name_en 셀러 영문명 str
            center_number 고객센터 번호 str
            site_url 사이트 URL str
            kakao_id 카카오 아이디 str required=False
            insta_id 인스타 아이디 str required=False

        Returns: http 응답코드
            200: SUCCESS 셀러 회원가입 완료
            400: EXISTING_LOGIN_ID, EXISTING_NAME_KR,
                 EXISTING_NAME_EN, INVALID_KEY
            500: NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)

        History:
        2020-04-06 ([email protected]): 초기 생성
        2020-04-07 ([email protected]):
            'center_number'의 중간부분 유효성검사 허용 범위를 4글자->3~4글자로 변경

        """

        # validation 확인이 된 data 를 account_info 로 재정의
        account_info = {
            'login_id': args[0],
            'password': args[1],
            'contact_number': args[3],
            'seller_type_id': args[4],
            'name_kr': args[5],
            'name_en': args[6],
            'center_number': args[7],
            'site_url': args[8],
            'kakao_id': args[10],
            'insta_id': args[11]
        }

        # 데이터베이스 연결
        try:
            db_connection = get_db_connection()
            if db_connection:
                seller_service = SellerService()
                sign_up_result = seller_service.sign_up(
                    account_info, db_connection)
                return sign_up_result
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @seller_app.route('/login', methods=['POST'])
    @validate_params(Param('login_id', JSON, str),
                     Param('password', JSON, str))
    def login(*args):
        """ 셀러 로그인 엔드포인트
        셀러 로그인 엔드포인트 입니다.
        login_id 와 password 를 받습니다.

        request.body:
            login_id: 로그인 아이디
            password: 로그인 비밀번호

        Args:
            *args: 유효성 검사를 통과한 request.body 의 인자

        Returns:
            200: SUCCESS 로그인 성공
            401: INVALID_PASSWORD, STATUS_1_CANT_LOGIN
            500: NO_DATABASE_CONNECTION

        Authors:
            [email protected] (최예지)

        History:
            2020-04-04 ([email protected]): 초기 생성
            2020-04-04 ([email protected]): account_info 에 필요한 정보 담음, DB 열림닫힘 여부에 따라 실행되는 함수 작성
        """

        # validation 확인이 된 data 를 account_info 로 재정의
        account_info = {'login_id': args[0], 'password': args[1]}

        try:
            # DB에 연결
            db_connection = get_db_connection()
            if db_connection:

                # service 에 있는 SellerService 를 가져와서 seller_service 라는 인스턴스를 만듦
                seller_service = SellerService()

                # 로그인 함수를 실행한 결과값을 login_result 에 저장
                login_result = seller_service.login(account_info,
                                                    db_connection)
                return login_result

            # DB가 열리지 않았을 경우
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        # 정의하지 않은 모든 error 를 잡아줌
        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        # try 랑 except 에 상관없이 무조건 실행
        finally:
            try:
                db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @seller_app.route('', methods=['GET'], endpoint='get_all_sellers')
    @login_required
    @validate_params(Param('seller_account_no', GET, int, required=False),
                     Param('login_id', GET, str, required=False),
                     Param('name_en', GET, str, required=False),
                     Param('name_kr', GET, str, required=False),
                     Param('brandi_app_user_id', GET, int, required=False),
                     Param('manager_name', GET, str, required=False),
                     Param('manager_email', GET, str, required=False),
                     Param('seller_status', GET, str, required=False),
                     Param('manager_contact_number', GET, str, required=False),
                     Param('seller_type_name', GET, str, required=False),
                     Param('start_time', GET, str, required=False),
                     Param('close_time', GET, str, required=False),
                     Param('excel', GET, int, required=False),
                     Param('offset', GET, int, required=False),
                     Param('limit', GET, int, required=False))
    def get_seller_list(*args):
        """ 가입된 모든 셀러 정보 리스트를 표출
        유효성검사를 통과한 값을 request 에 넣어줌.

        Args:
            g.account_info: 데코레이터에서 넘겨받은 수정을 수행하는 계정 정보
            g.account_info.seller_account_no: 검색 셀러 번호
            g.account_info.account_no: 데코레이터에서 확인된 계정번호드
            args: path parameter 를 통해서 들어온 검색 키워

        Returns:
            seller_list_result: 가입된 모든 셀러 및 셀러 세부 정보 리스트로 표출(seller_service 에서 받은 리턴 값.)
            400: seller_service 로 값을 넘겨줄 때 애러가나면 400 리턴
            500: database 연결에 실패하면 500리턴

        Authors:
            [email protected] (윤희철)

        History:
            2020-04-03 ([email protected]): 초기 생성
            2020-04-07 ([email protected]): 파라미터 유효성검사 추가
            2020-04-10 ([email protected]): 애러 처리 추가
            2020-04-14 ([email protected]): offset 과 limit 도 유효성검사 실시
        """

        # 유효성 확인 위해 기간 데이터 먼저 정의
        start_time = args[10]
        close_time = args[11]

        # 두 값이 모두 들어왔을 때, 시작 기간이 종료 기간보다 늦으면 시작기간 = 종료기간
        if start_time and close_time:
            if start_time > close_time:
                start_time = close_time

        # validation 을 통과한 값을 담을 딕셔너리를 만들어줌.
        valid_param = {}

        # valid_param 딕셔너리에 validation 을 통과한 query parameter 을 넣어줌.
        valid_param['seller_account_no'] = args[0]
        valid_param['login_id'] = args[1]
        valid_param['name_en'] = args[2]
        valid_param['name_kr'] = args[3]
        valid_param['brandi_app_user_id'] = args[4]
        valid_param['manager_name'] = args[5]
        valid_param['manager_email'] = args[6]
        valid_param['seller_status'] = args[7]
        valid_param['manager_contact_number'] = args[8]
        valid_param['seller_type_name'] = args[9]
        valid_param['start_time'] = start_time
        valid_param['close_time'] = close_time
        valid_param['excel'] = args[12]
        valid_param['offset'] = args[13] if args[13] else 0
        valid_param['limit'] = args[14] if args[14] else 10

        # 유저 정보를 g에서 읽어와서 service 에 전달
        user = g.account_info

        # 데이터베이스 커넥션을 열어줌.
        try:
            db_connection = DatabaseConnection()
            if db_connection:
                seller_service = SellerService()
                seller_list_result = seller_service.get_seller_list(
                    valid_param, user, db_connection)
                return seller_list_result
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @seller_app.route('/<int:parameter_account_no>',
                      methods=['GET'],
                      endpoint='get_seller_info')
    @login_required
    @validate_params(
        Param('parameter_account_no', PATH, int),
        Param('parameter_account_no', PATH, str, rules=[MaxLength(6)]),
    )
    def get_seller_info(*args):
        """ 계정 셀러정보 표출 엔드포인트

        셀러정보를 표출하는 엔드포인트 입니다.
        url 로 셀러정보를 확인하고 싶은 계정 번호를 받습니다.

        셀러정보를 열람을 수행하려는 계정의 번호를 데코레이터로부터 받습니다.
        열람 대상 셀러정보의 계정의 번호를 url parameter 로 받습니다.

        받은 정보를 유효성 검사를 거친 후 account_info 로 저장해 service 로 넘겨줍니다.

        Args:
            *args: 유효성 검사를 통과한 파라미터

        url parameter:
            parameter_account_no: 열람하고자 하는 셀러정보의 계정 번호

        g.account_info: 데코레이터에서 넘겨받은(셀러정보를 열람을 수행하려는) 계정 정보
            auth_type_id: 계정의 권한정보
            account_no: 데코레이터에서 확인된 계정번호

        Returns: http 응답코드
            200: SUCCESS 셀러정보 겟 완료
            400: INVALID_ACCOUNT_NO, INVALID_AUTH_TYPE_ID
            403: NO_AUTHORIZATION
            500: NO_DATABASE_CONNECTION, DB_CURSOR_ERROR, INVALID_KEY

        Authors:
            [email protected] (이종민)

        History:
            2020-04-01 ([email protected]): 초기 생성
            2020-04-02 ([email protected]): 파라미터 validation 추가, 데코레이터 적용
            2020-04-03 ([email protected]): 주석 수정(메인문구, url parameter 수정)
            2020-04-06 ([email protected]):
                url path 변경('/<int:parameter_account_no>/info' -> '/<int:parameter_account_no>')

        """

        # validation 확인이 된 data 를 account_info 로 재정의
        account_info = {
            'parameter_account_no': args[0],
            'auth_type_id': g.account_info['auth_type_id'],
            'decorator_account_no': g.account_info['account_no'],
        }

        db_connection = get_db_connection()
        try:
            if db_connection:
                seller_service = SellerService()
                getting_seller_info_result = seller_service.get_seller_info(
                    account_info, db_connection)

                return getting_seller_info_result

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @seller_app.route('/mypage', methods=['GET'], endpoint='get_my_page')
    @login_required
    def get_my_page():
        """ 계정 셀러정보 표출 엔드포인트(my_page)

        mypage 셀러정보를 표출하는 엔드포인트 입니다.
        로그인 데코레이터로 셀러의 계정 번호를 확인합니다.

        확인된 계정 정보를 service 로 넘겨줍니다.

        g.account_info: 데코레이터에서 넘겨받은 계정 정보
            auth_type_id: 계정의 권한정보
            account_no: 데코레이터에서 확인된 계정번호

        Returns: http 응답코드
            200: SUCCESS 셀러정보 겟 완료
            400: INVALID_ACCOUNT_NO, INVALID_AUTH_TYPE_ID
            403: NO_AUTHORIZATION
            500: NO_DATABASE_CONNECTION, DB_CURSOR_ERROR, INVALID_KEY

        Authors:
            [email protected] (이종민)

        History:
            2020-04-08 ([email protected]): 초기 생성

        """

        # get_seller_info dao 를 같이 쓰기 위해 account_no를 아래와 같이 저장
        account_info = {
            'parameter_account_no': g.account_info['account_no'],
            'auth_type_id': g.account_info['auth_type_id']
        }

        try:
            db_connection = get_db_connection()
            if db_connection:
                seller_service = SellerService()
                getting_seller_info_result = seller_service.get_my_page(
                    account_info, db_connection)

                return getting_seller_info_result

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @seller_app.route('/<int:parameter_account_no>',
                      methods=['PUT'],
                      endpoint='change_seller_info')
    @login_required
    @validate_params(
        Param('parameter_account_no', PATH, int),
        Param('seller_status_no', FORM, int),
        Param('seller_type_no', FORM, int),
        Param('name_kr',
              FORM,
              str,
              rules=[Pattern(r'^[가-힣a-zA-Z0-9\ ]{1,45}$')]),
        Param('name_en', FORM, str, rules=[Pattern(r'^[a-z\ ]{1,45}$')]),
        Param('background_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param('brandi_app_user_app_id',
              FORM,
              str,
              rules=[Pattern(r'^[가-힣a-zA-Z0-9]{1,45}$')]),
        Param('ceo_name',
              FORM,
              str,
              rules=[Pattern(r'^[가-힣a-zA-Z0-9]{1,45}$')]),
        Param('company_name',
              FORM,
              str,
              rules=[Pattern(r'^[가-힣a-zA-Z0-9]{1,45}$')]),
        Param('business_number',
              FORM,
              str,
              rules=[Pattern(r'^[0-9]{3}-{1}[0-9]{2}-{1}[0-9]{5}$')]),
        Param('online_business_number', FORM, str, rules=[MaxLength(45)]),
        Param('short_description', FORM, str, rules=[MaxLength(100)]),
        Param('long_description',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param('long_description',
              FORM,
              str,
              required=False,
              rules=[MinLength(10)]),
        Param('site_url', FORM, str, rules=[MaxLength(200)]),
        Param(
            'site_url',
            FORM,
            str,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('manager_infos', FORM, str),
        Param('insta_id', FORM, str, rules=[Pattern(r"^[a-z0-9_\.]{1,45}$")]),
        Param('center_number', FORM, str, rules=[Pattern(r"^[0-9-]{1,14}$")]),
        Param('kakao_id',
              FORM,
              str,
              required=False,
              rules=[Pattern(r"^[가-힣a-zA-Z0-9_\.]{1,45}$")]),
        Param('yellow_id',
              FORM,
              str,
              required=False,
              rules=[Pattern(r"^[가-힣a-zA-Z0-9_\.]{1,45}$")]),
        Param('zip_code', FORM, str, rules=[Pattern(r"^[0-9]{5}$")]),
        Param('address', FORM, str, rules=[MaxLength(100)]),
        Param('detail_address', FORM, str, rules=[MaxLength(100)]),
        Param(
            'weekday_start_time',
            FORM,
            str,
            rules=[
                Pattern(
                    r"^(00|0[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param(
            'weekday_end_time',
            FORM,
            str,
            rules=[
                Pattern(
                    r"^(00|0[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param(
            'weekend_start_time',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(00|0[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param(
            'weekend_end_time',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(00|0[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param('bank_name', FORM, str, rules=[MaxLength(45)]),
        Param('bank_holder_name', FORM, str, rules=[MaxLength(45)]),
        Param('account_number', FORM, str, rules=[MaxLength(45)]),

        # int 를 str 로 인식해서 정규식 유효성 확인
        Param('seller_status_no', FORM, str, rules=[Pattern(r"^[1-6]{1}$")]),
        Param('seller_type_no', FORM, str, rules=[Pattern(r"^[1-7]{1}$")]),

        # 이미지 url 이 들어올 경우 유효성 확인
        Param(
            'profile_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param(
            'certificate_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param(
            'online_business_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param(
            'background_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('profile_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param('certificate_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param('online_business_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]))
    def change_seller_info(*args):
        """ 계정 셀러정보 수정 엔드포인트

        셀러정보를 수정하는 엔드포인트 입니다.
        url 로 셀러정보를 수정하고 싶은 계정 번호를 받습니다.
        셀러정보 수정을 수행하려는 계정의 정보를 데코레이터로부터 받습니다.
        수정하려는 내용은 form 으로 받습니다.

        받은 정보를 유효성 검사를 거친 후 account_info 로 저장해 service 로 넘겨줍니다.

        Args:
            *args: 유효성 검사를 통과한 파라미터

        url parameter:
            parameter_account_no: 저장할 셀러정보의 계정 번호

        g.account_info: 데코레이터에서 넘겨받은 수정을 수행하는 계정 정보
            auth_type_id: 계정의 권한정보
            account_no: 데코레이터에서 확인된 계정번호

        form:
            seller_status_no 셀러 상태번호 int
            seller_type_no 셀러 속성번호 int
            name_kr 셀러 한글명 str 한글,영문,숫자
            name_en 셀러 영문명 str required False 영문 소문자
            brandi_app_user_app_id 브랜디앱 유저 아이디 str
            ceo_name 대표자명 str
            company_name 사업자명 str
            business_number 사업자번호 str 12자리
            online_business_number 통신판매업번호 str
            short_description 셀러 한줄 소개 str
            long_description 셀러 상세 소개 str required False 10글자 이상
            site_url 사이트 URL str
            manager_info: 담당자 정보 list
            [
                {
                    name 담당자명 str
                    contact_number 담당자 핸드폰번호 str
                    email 담당자 이메일 str
                }
            ]
            insta_id 인스타그램 아이디 str
            center_number 고객센터 전화번호 str
            kakao_id 카카오톡 아이디 str required False
            yellow_id 옐로우 아이디 str required False
            zip_code 우편번호 str
            address 주소 int
            detail_address 상세주소 str
            weekday_start_time 고객센터 운영 시작시간(주중) str
            weekday_end_time 고객센터 운영 종료시간(주중) str
            weekend_start_time 고객센터 운영 시작시간(주말) str required False
            weekend_end_time 고객센터 운영 종료시간(주말) str required False
            bank_name 정산은행 str
            bank_holder_name 계좌주 str
            account_number 계좌번호 str
            previous_seller_status_no 이전 셀러정보의 상태번호 int (상태변경이력 체크용)

        Returns: http 응답코드
            200: SUCCESS 셀러정보 수정(새로운 이력 생성) 완료
            400: INVALID_APP_ID (존재하지 않는 브랜디 앱 아이디 입력)
            400: VALIDATION_ERROR_MANAGER_INFO, NO_SPECIFIC_MANAGER_INFO,
                 INVALID_AUTH_TYPE_ID, NO_PROFILE_IMAGE, NO_CERTIFICATE_IMAGE
                 NO_CHANGEABLE_STATUS, EXISTING_NAME_KR, EXISTING_NAME_EN
            403: NO_AUTHORIZATION, NO_AUTHORIZATION_FOR_STATUS_CHANGE, NO_ONLINE_BUSINESS_IMAGE
            500: INVALID_KEY, DB_CURSOR_ERROR, NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)
            [email protected] (윤희철)

        History:
            2020-04-03 ([email protected]): 초기 생성
            2020-04-04 ([email protected]): 이전 셀러 정보번호, 이전 셀러 정보 상태정보, 셀러 계정번호 추가
            2020-04-06 ([email protected]):
                url path 변경('/<int:parameter_account_no>/info' -> '/<int:parameter_account_no>')
            2020-04-08 ([email protected]):
                마스터가 아닐 때 셀러 상태(입점 등)를 변경하려고 하면 에러 처리하는 내용 추가
            2020-04-09 ([email protected]):
                이미지 업로더 적용.
            2020-04-09 ([email protected]):
                 이미지 파일을 새로 업로드하면, 이 파일을 저장한 s3 url 을 저장하고,
                 수정을 안해서 기존에 DB에 저장된 url 을 보내주면, 해당 url 을 저장함
                 필수값인 셀러 프로필, 등록증 2개가 들어오지 않으면 에러처리
            2020-04-12 ([email protected]):
                셀러용 이미지 업로더를 사용하는 것에서 공통 업로더를 사용하도록 변경
        """

        # manager_infos 유효성 확인을 확인하기 위해 따로 저장
        manager_info_list = json.loads(args[16])

        # manger_infos 리스트를 돌면서 각각의 유효성을 충족하는지 체크
        for info in manager_info_list:
            name_validation = r'^[가-힣a-zA-Z0-9\ ]{1,45}$'
            contact_number_validation = r'^[0-9-]{1,14}$'
            email_validation = r'^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$'
            ranking_validation = r'^[1-3]{1}$'

            # 각 키가 들어왔는지 먼저 확인
            if (info.get('name', None) and info.get('contact_number', None)
                    and info.get('email', None) and info.get('ranking', None)):

                # 키가 모두 들어왔으면, 유효성을 만족하는지 확인
                if (re.match(name_validation, info['name']) and re.match(
                        contact_number_validation, info['contact_number'])
                        and re.match(email_validation, info['email']) and
                        re.match(ranking_validation, str(info['ranking']))):
                    pass

                # 유효성을 만족시키지 못하면 에러 반환
                else:
                    return jsonify(
                        {"message": "VALIDATION_ERROR_MANAGER_INFO"}), 400

            # 키가 안들어오면 에러 반환
            else:
                return jsonify({"message": "NO_SPECIFIC_MANAGER_INFO"}), 400

        # 이미지 업로드 함수를 호출해서 이미지를 업로드하고 url 을 딕셔너리로 가져옴.
        image_upload = ImageUpload()
        seller_image = image_upload.upload_images(request)

        if (400 in seller_image) or (500 in seller_image):
            return seller_image

        # validation 확인이 된 data 를 account_info 로 재정의
        account_info = {
            'auth_type_id':
            g.account_info['auth_type_id'],
            'decorator_account_no':
            g.account_info['account_no'],
            'parameter_account_no':
            args[0],
            'profile_image_url':
            seller_image.get('seller_profile_image', None),
            'seller_status_no':
            args[1],
            'seller_type_no':
            args[2],
            'name_kr':
            args[3],
            'name_en':
            args[4],
            'brandi_app_user_app_id':
            args[6],
            'ceo_name':
            args[7],
            'company_name':
            args[8],
            'business_number':
            args[9],
            'certificate_image_url':
            seller_image.get('certificate_image', None),
            'online_business_number':
            args[10],
            'online_business_image_url':
            seller_image.get('online_business_image', None),
            'background_image_url':
            seller_image.get('background_image', None),
            'short_description':
            args[11],
            'long_description':
            args[12],
            'site_url':
            args[14],
            'manager_infos':
            manager_info_list,
            'insta_id':
            args[17],
            'center_number':
            args[18],
            'kakao_id':
            args[19],
            'yellow_id':
            args[20],
            'zip_code':
            args[21],
            'address':
            args[22],
            'detail_address':
            args[23],
            'weekday_start_time':
            args[24],
            'weekday_end_time':
            args[25],
            'weekend_start_time':
            args[26],
            'weekend_end_time':
            args[27],
            'bank_name':
            args[28],
            'bank_holder_name':
            args[29],
            'account_number':
            args[30],
        }

        # file 로 이미지가 안들어올 경우, FORM 으로 받은 이미지 url 로 대체
        if not account_info['profile_image_url']:
            account_info['profile_image_url'] = args[33]

        if not account_info['certificate_image_url']:
            account_info['certificate_image_url'] = args[34]

        if not account_info['online_business_image_url']:
            account_info['online_business_image_url'] = args[35]

        if not account_info['background_image_url']:
            account_info['background_image_url'] = args[36]

        # 이미지 url 필수값 3개가 안들어오면 에러 리턴
        if not account_info['profile_image_url']:
            return jsonify({'message': 'NO_PROFILE_IMAGE'}), 400

        if not account_info['certificate_image_url']:
            return jsonify({'message': 'NO_CERTIFICATE_IMAGE'}), 400

        if not account_info['online_business_image_url']:
            return jsonify({'message': 'NO_ONLINE_BUSINESS_IMAGE'}), 400

        # 데이터베이스 연결
        try:
            db_connection = get_db_connection()
            if db_connection:
                seller_service = SellerService()
                changing_seller_info_result = seller_service.change_seller_info(
                    account_info, db_connection)
                return changing_seller_info_result
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @seller_app.route('/<int:parameter_account_no>/password',
                      methods=['PUT'],
                      endpoint='change_password')
    @login_required
    @validate_params(
        Param('parameter_account_no', PATH, int),
        Param('original_password', JSON, str, required=False),
        Param('new_password', JSON, str),
    )
    def change_password(*args):
        """ 계정 비밀번호 변경 엔드포인트

        계정 비밀번호 변경 엔드포인트 입니다.
        계정이 가진 권한에 따라 지정된 인자를 받아 비밀번호를 변경합니다.
        url 에 비밀번호를 바꿔야할 계정 번호를 받습니다.

        Args:
            *args: 유효성 검사를 통과한 파라미터

        url parameter:
            parameter_account_no: 비밀번호가 바뀌어야할 계정 번호

        g.account_info: 데코레이터에서 넘겨받은 계정 정보(비밀번호 변경을 수행하려는 계정)
            auth_type_id: 계정의 권한정보
            account_no: 계정번호

        request.body: request 로 전달 받은 정보
            original_password: 기존 비밀번호(비밀번호 변경을 수행하는자가 셀러 권한일 경우에만 전달 받음)
            new_password: 변경하려는 새로운 비밀번호

        Returns: http 응답코드
            200: SUCCESS 비밀번호 변경 완료
            400: VALIDATION_ERROR, INVALID_AUTH_TYPE_ID, NO_ORIGINAL_PASSWORD
                 TOO_SHORT_PASSWORD, INVALID_PARAMETER_ACCOUNT_NO
            401: INVALID_PASSWORD
            403: NO_AUTHORIZATION
            500: DB_CURSOR_ERROR, NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)

        History:
            2020-03-31 ([email protected]): 초기 생성
            2020-04-02 ([email protected]): 파라미터 validation 추가, 데코레이터 적용
            2020-04-06 ([email protected]):
                url path 변경('/<int:parameter_account_no>' -> '/<int:parameter_account_no>/password')
            2020-04-12 ([email protected]): 리팩토링
                - 주석 수정 : 비밀번호 변경을 수행하려는 주체에 대해 명확히 명시
                - 변경할 비밀번호 길이 제한 추가(4글자 이상)
                - 전달 인자 명칭을 명확히 하기 위해 변경(account_info -> change_info)

        """

        # validation 확인이 된 data 를 change_info 로 재정의
        change_info = {
            'parameter_account_no': args[0],
            'original_password': args[1],
            'new_password': args[2],
            'auth_type_id': g.account_info['auth_type_id'],
            'decorator_account_no': g.account_info['account_no']
        }

        # 셀러 권한일 때 original_password 가 없으면 에러반환
        if change_info['auth_type_id'] == 2:
            if change_info['original_password'] is None:
                return jsonify({"message": "NO_ORIGINAL_PASSWORD"}), 400

        # 변경할 비밀번호 길이가 4글자 미만이면 에러 반환
        if len(change_info['new_password']) < 4:
            return jsonify({'message': 'TOO_SHORT_PASSWORD'}), 400

        try:
            db_connection = get_db_connection()
            if db_connection:
                seller_service = SellerService()
                changing_password_result = seller_service.change_password(
                    change_info, db_connection)
                return changing_password_result

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @seller_app.route('/<int:seller_account_id>/status',
                      methods=['PUT'],
                      endpoint='change_seller_status')
    @login_required
    @validate_params(Param('seller_account_id', PATH, int, required=False),
                     Param('seller_status_id', JSON, int, required=False))
    def change_seller_status(*args):
        """ 셀러 상태 변경
        마스터 권한을 가진 어카운트가 셀러의 상태를 변경 하는 기능.
        path parameter 로 셀러 계정 번호를 받고 body 로 변경하고자 하는 셀러 상태 번호를 받는다.

        Args:
            args: 유효성 검사를 통과한 파라미터 리스트
                seller_account_id: path parameter 를 통해서 들어온 셀러 계정 번호
                seller_status_id: request body 를 통해서 들어온

        Returns:
            status_change_result: seller_service 에서 넘겨받은 결과 값.
            400: seller_service 의 클래스 호출 실패 또는 parameter 를 제대로 넘겨지주 못했을 경우
            500: 데이터베이스 연결 실패

        Authors:
            [email protected] (윤희철)

        History:
            2020-04-05 ([email protected]): 초기 생성
            2020-04-09 ([email protected]): 선분이력을 반영하여 상태변경 작성
        """

        # 유저정보를 가져와 서비스로 넘김
        user = g.account_info

        # 유효성검사를 통과한 parameter 를 딕셔너리 담는다.
        target_seller_info = {
            'seller_account_id': args[0],
            'seller_status_id': args[1]
        }

        try:
            db_connection = DatabaseConnection()
            if db_connection:
                seller_service = SellerService()
                status_change_result = seller_service.change_seller_status(
                    target_seller_info, user, db_connection)
                return status_change_result
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return ({'message': f'{e}'}), 400
from flask_request_validator import Pattern

nickname_rule = Pattern(
    r'^[a-zA-Z0-9_-]{5,20}$')  # 영문,숫자 조합 5~20 글자. 대쉬, 하이픈 허용
password_rule = Pattern(
    r'^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,20}$'
)  # 영문,숫자, 특수문자 조합 8~20글자
phone_number_rule = Pattern(r'^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$')
seller_korean_name_rule = Pattern(r'^[가-힣\s]+$')
seller_english_name_rule = Pattern(r'^[a-zA-Z\s]+$')
Exemple #10
0
    @validate_params(Param('cities', GET, list), Param('countries', GET, dict))
    def get(self, cities, countries):
        return Response(json.dumps({
            'cities': [cities, cities.__class__.__name__],
            'countries': [countries, countries.__class__.__name__],
        }),
                        mimetype='application/json')


type_composite = CompositeRule(Enum('type1', 'type2'))


@app.route('/main/<string:key>/<string:uuid>', methods=['POST'])
@validate_params(
    Param('key', PATH, str, rules=[Enum('key1', 'key2')]),
    Param('uuid', PATH, str, rules=[Pattern(r'^[a-z-_.]{8,10}$')]),
    Param('sure', GET, bool, False),
    Param('sys', FORM, str, rules=[Pattern(r'^[a-z.]{3,6}$')]),
    Param('types', FORM, str, rules=type_composite),
    Param('price', FORM, float, False),
    Param('cities', FORM, list, False),
    Param('dql', FORM, dict, False),
    Param('default', FORM, dict, False, default=lambda: ['test']),
)
def route_form(key, uuid, sure, sys, types, price, cities, dql, default):
    return json.dumps([
        [key, key.__class__.__name__],
        [uuid, uuid.__class__.__name__],
        [sure, sure.__class__.__name__],
        [sys, sys.__class__.__name__],
        [types, types.__class__.__name__],
def create_product_endpoints(product_service, Session):

    product_app = Blueprint('product_app', __name__, url_prefix='/api/product')

    @product_app.route('/products', methods = ['GET'], endpoint='products')
    @login_required(Session)
    @validate_params(
        Param('filterLimit', GET, int, default=10, required=False),
        Param('page', GET, int, required=False),
        Param('exhibitionYn', GET, int, rules=[Enum(0, 1)], required=False),
        Param('exhibitionYn', GET, int, rules=[Enum(0, 1)], required=False),
        Param('sellYn', GET, int, rules=[Enum(0, 1)], required=False),
        Param('mdSeNo', GET, int, required=False),
        Param('selectFilter', GET, str, rules=[Enum('productName', 'productNo', 'productCode')], required=False),
        Param('filterKeyword', GET, required=False),
        Param('mdName', GET, required=False),
        Param('filterDateFrom', GET, str, rules=[Pattern(r"^\d\d\d\d-\d{1,2}-\d{1,2}$")], required=False),
        Param('filterDateTo', GET, str, rules=[Pattern(r"^\d\d\d\d-\d{1,2}-\d{1,2}$")], required=False)
    )
    def products(*args):
        """ 상품 정보 리스트 전달 API

        쿼리 파라미터로 필터링에 사용될 값을 받아 필터링된 상품의 데이터 리스트를 표출합니다.

        args:
            *args:
                filterLimit: pagination 을 위한 파라미터
                page: pagination 을 위한 파라미터
                exhibitionYn: 진열 여부
                discountYn: 할인 여부
                sellYn: 판매 여부
                mdSeNo: 셀러 속성 id
                selectFilter: 상품 검색 시 상품 명, 코드, 번호 중 어떤 것을 선택했는지 판단 위한 파라미터
                filterKeyword: 상품 검색을 위한 파라미터
                mdName: 셀러 이름 검색을 위한 파라미터
                filterDateFrom: 조회 기간 시작
                filterDateTo: 조회 기간 끝

        returns :
            200: 상품 리스트
            500: Exception

        Authors:
            고지원

        History:
            2020-10-01 (고지원): 초기 생성
        """
        try:
            session = Session()

            # 필터링을 위한 딕셔너리
            filter_dict = dict()

            # pagination
            filter_dict['filterLimit'] = args[0]
            filter_dict['page'] = args[1]

            # 진열 여부
            filter_dict['exhibitionYn'] = args[2]

            # 할인 여부
            filter_dict['discountYn'] = args[3]

            # 판매 여부
            filter_dict['sellYn'] = args[4]

            # 셀러 속성
            filter_dict['mdSeNo'] = args[5]

            # 상품 검색
            filter_dict['selectFilter'] = args[6]

            # 상품 검색어
            filter_dict['filterKeyword'] = args[7]

            # 셀러 검색어
            filter_dict['mdName'] = args[8]

            # 조회 기간 시작
            filter_dict['filterDateFrom'] = args[9]

            # 조회 기간 끝
            filter_dict['filterDateTo'] = args[10]

            # 상품 정보
            products = product_service.get_products(filter_dict, session)

            # 상품 쿼리 결과 count
            count_info = product_service.get_product_count(filter_dict, session)

            body = {
                'orders'             : [dict(product) for product in products],
                'page_number'        : count_info.p_count,
                'total_order_number' : round(count_info.p_count / filter_dict['filterLimit'])
            }

            return jsonify(body), 200

        except exc.ProgrammingError:
            return jsonify(({'message': 'ERROR_IN_SQL_SYNTAX'})), 500

        except Exception as e:
            traceback.print_exc()
            return jsonify({'message': f'{e}'}), 500

        finally:
            session.close()

    @product_app.route('/<int:product_id>', methods=['GET'], endpoint='product')
    @login_required(Session)
    def product(product_id):
        """ 상품 수정 시 기존 등록 정보 전달 API

        path parameter 로 id 받아 해당 상품의 데이터 표출합니다.

        args:
            product_id : 상품의 id

        returns :
            200: 상품 정보
            500: Exception

        Authors:
            고지원

        History:
            2020-10-01 (고지원): 초기 생성
        """
        session = Session()
        try:
            # 상품 데이터
            body = dict(product_service.get_product(product_id, session))

            return jsonify(body), 200

        except exc.ProgrammingError:
            return jsonify({'message': 'ERROR_IN_SQL_SYNTAX'}), 500

        except AttributeError:
            return jsonify({'message': 'THERE_IS_NO_PRODUCT_DATA'}), 400

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            session.close()

    @product_app.route('/history', methods=['GET'], endpoint='product_history')
    @login_required(Session)
    def product_history():
        """ 상품 수정 이력 전달 API

        점 이력으로 관리하는 상품의 수정 이력을 표출합니다.

        args:
            product_id : 상품의 id

        returns :
            200: 상품의 수정 이력 리스트
            500: Exception

        Authors:
            고지원

        History:
            2020-10-10 (고지원): 초기 생성
        """
        session = Session()
        try:
            product_id = request.args.get('product_id')
            body = [dict(history) for history in product_service.get_product_history(product_id, session)]

            return jsonify(body), 200

        except exc.ProgrammingError:
            return jsonify({'message': 'ERROR_IN_SQL_SYNTAX'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            session.close()

    @product_app.route('/excel', methods=['GET'], endpoint='make_excel')
    @login_required(Session)
    @validate_params(
        Param('product_id', GET, list, required=False)
    )
    def make_excel(*args):
        """ 상품 정보 엑셀 다운로드 API

        전체 상품 또는 선택 상품의 정보를 excel 파일로 다운로드 합니다.

        args:
            product_id : 상품의 id 리스트

        returns :
            200: Excel 파일 다운
            500: Exception

        Authors:
            고지원

        History:
            2020-10-02 (고지원): 초기 생성
        """
        session = Session()
        try:
            # 선택한 상품들의 id를 list 로 받는다.
            id_list = args[0]

            # service 의 make_excel 함수를 호출한다.
            product_service.make_excel(id_list, session)

            return jsonify({'message': 'SUCCESS'}), 200

        except exc.ProgrammingError:
            traceback.print_exc()
            return jsonify({'message': 'ERROR_IN_SQL_SYNTAX'}), 500

        except Exception as e:
            traceback.print_exc()
            return jsonify({'message': f'{e}'}), 500

        finally:
            session.close()

    @product_app.route('/seller', methods=['GET'], endpoint='sellers')
    @login_required(Session)
    @validate_params(
        Param('q', GET, str, required = True)
    )
    def sellers(*args):
        """ 셀러 리스트 전달 API
        query parameter 를 받아 필터링된 셀러 리스트 데이터를 표출합니다.

        args:
            *args:
                name: 셀러명 검색을 위한 쿼리 스트링

        returns :
            200: 셀러 리스트
            500: Exception

        Authors:
            고지원

        History:
            2020-10-04 (고지원): 초기 생성
        """
        session = Session()
        try:
            # 필터링을 위한 딕셔너리
            seller_dict = dict()
            seller_dict['name'] = args[0]

            # 셀러 데이터
            body = [dict(seller) for seller in product_service.get_sellers(seller_dict, session)]

            return jsonify(body), 200

        except exc.ProgrammingError:
            return jsonify({'message': 'ERROR_IN_SQL_SYNTAX'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            session.close()

    @product_app.route('/category', methods=['GET'], endpoint='product_categories')
    @login_required(Session)
    def product_categories():
        """ 1차, 2차 카테고리 정보 전달 API

        셀러의 속성 아이디를 받아 1차 카테고리, 1차 카테고리 아이디를 받아 2차 카테고리 정보를 전달합니다.

        args:
            seller_attr_id: 셀러의 속성 id
            f_category_id: 1차 카테고리 id

        returns :
            200: 1차 또는 2차 카테고리 정보
            500: Exception

        Authors:
            고지원

        History:
            2020-10-04 (고지원): 초기 생성
        """
        session = Session()
        try:
            # 셀러 속성 아이디 또는 1차 카테고리 아이디를 받는다.
            seller_attr_id = request.args.get('seller_attr_id')
            f_category_id = request.args.get('f_category_id')

            # 셀러 속성 아이디가 들어왔을 경우 1차 카테고리 정보를 반환
            if seller_attr_id:
                body = [dict(cat) for cat in product_service.get_first_categories(seller_attr_id, session)]

                return jsonify(body), 200

            # 1차 카테고리 아이디가 들어왔을 경우 2차 카테고리 정보를 반환
            body = [dict(cat)for cat in product_service.get_second_categories(f_category_id, session)]

            return jsonify(body), 200

        except exc.ProgrammingError:
            return jsonify({'message': 'ERROR_IN_SQL_SYNTAX'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            session.close()

    @product_app.route('', methods=['POST'], endpoint='insert_product')
    @login_required(Session)
    def insert_product():
        """ 상품 정보 등록 API

        returns :
            200: 상품 정보를 데이터베이스에 저장
            400:
                NAME_CANNOT_CONTAIN_QUOTATION_MARK,
                START_DATE_CANNOT_BE_EARLIER_THAN_END_DATE,
                CANNOT_SET_MORE_THAN_20,
                CANNOT_SET_LESS_THAN_10,
                DISCOUNT_RANGE_CAN_BE_SET_FROM_0_TO_99,
                DUPLICATE_DATA,
                INVALID_REQUEST
            500:
                 Exception,
                 ERROR_IN_SQL_SYNTAX

        Authors:
            고지원

        History:
            2020-10-03 (고지원): 초기 생성
            2020-10-04 (고지원): 상품 정보 입력 시 제한 사항 에러 추가
            2020-10-10 (고지원): 여러 개의 이미지를 업로드 할 수 있도록 수정
            2020-10-12 (고지원): 에러 발생 시 세션 rollback 과 함께 s3 에 업로드 된 이미지도 삭제되도록 수정
        """
        session = Session()
        image_urls = ''
        is_success = False
        try:
            # 상품명에 ' 또는 " 포함 되었는지 체크
            pattern = re.compile('[\"\']')
            if pattern.search(request.form['name']):
                return jsonify({'message': 'NAME_CANNOT_CONTAIN_QUOTATION_MARK'}), 400

            # 할인 시작일이 할인 종료일보다 빠를 경우
            if request.form['discount_start_date'] > request.form['discount_end_date']:
                return jsonify({'message': 'START_DATE_CANNOT_BE_EARLIER_THAN_END_DATE'}), 400

            # 최소 수량 또는 최대 수량이 20을 초과할 경우
            if int(request.form['min_unit']) > 20 or int(request.form['max_unit']) > 20:
                return jsonify({'message': 'CANNOT_SET_MORE_THAN_20'}), 400

            # 판매가가 10원 미만일 경우
            if int(request.form['price']) < 10:
                return jsonify({'message': 'CANNOT_SET_LESS_THAN_10'}), 400

            # 할인률이 0 ~ 99% 가 아닐 경우
            if int(request.form['discount_rate']) not in range(0, 99):
                return jsonify({'message': 'DISCOUNT_RANGE_CAN_BE_SET_FROM_0_TO_99'}), 400

            # 상품 코드
            product_code = str(uuid.uuid4())

            # S3 이미지 저장
            images = list()

            # 1~5 개의 이미지를 가져온다.
            for idx in range(1, 6):
                image = request.files.get(f'image_{idx}', None)

                if image:
                    images.append(image)

            image_urls = product_service.upload_image(product_code, images)

            # 반환된 image_urls 가 허용되지 않은 확장자일 경우 400 에러 메시지를 반환한다.
            if 400 in image_urls:
                return image_urls

            # 상품 입력을 위한 데이터를 받는다.
            product_info = {
                'seller_id': request.form['seller_id'],
                'is_on_sale': request.form['is_on_sale'],
                'is_displayed': request.form['is_displayed'],
                'name': request.form['name'],
                'simple_description': request.form['simple_description'],
                'detail_description': request.form['detail_description'],
                'price': request.form['price'],
                'is_definite': request.form['is_definite'],
                'discount_rate': request.form['discount_rate'],
                'discount_price': request.form['discount_price'],
                'min_unit': request.form['min_unit'],
                'max_unit': request.form['max_unit'],
                'is_stock_managed': request.form['is_stock_managed'],
                'stock_number': request.form['stock_number'],
                'first_category_id': request.form['first_category_id'],
                'second_category_id': request.form['second_category_id'],
                'modifier_id': request.form['modifier_id'],
                'images': image_urls,
                'discount_start_date': request.form['discount_start_date'],
                'discount_end_date': request.form['discount_end_date'],
                'product_code': product_code
            }

            product_service.insert_product(product_info, session)

            session.commit()

            is_success = True

            return jsonify({'message': 'SUCCESS'}), 200

        except KeyError:
            return jsonify({'message': 'KEY_ERROR'}), 400

        except exc.IntegrityError:
            return jsonify({'message': 'INTEGRITY_ERROR'}), 400

        except exc.InvalidRequestError:
            return jsonify({'message': 'INVALID_REQUEST'}), 400

        except exc.ProgrammingError:
            return jsonify({'message': 'ERROR_IN_SQL_SYNTAX'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            if is_success is False:
                session.rollback()
                delete_image_in_s3(image_urls, None)
            session.close()

    @product_app.route('/update', methods=['POST'], endpoint='update_product')
    @login_required(Session)
    def update_product():
        """ 상품 정보 수정 API

        returns :
            200: 상품 정보를 데이터베이스에 저장
            400:
                NAME_CANNOT_CONTAIN_QUOTATION_MARK,
                START_DATE_CANNOT_BE_EARLIER_THAN_END_DATE,
                CANNOT_SET_MORE_THAN_20,
                CANNOT_SET_LESS_THAN_10,
                DISCOUNT_RANGE_CAN_BE_SET_FROM_0_TO_99,
                DUPLICATE_DATA,
                INVALID_REQUEST
            500:
                 Exception,
                 ERROR_IN_SQL_SYNTAX

        Authors:
            고지원

        History:
            2020-10-10 (고지원): 초기 생성
        """
        session = Session()
        is_success = False
        try:
            # 상품 입력을 위한 데이터를 받는다.
            old_images = request.form['images'].split(',')
            product_info = {
                'product_id': request.form['product_id'],
                'product_code': request.form['product_code'],
                'seller_id': request.form['seller_id'],
                'is_on_sale': request.form['is_on_sale'],
                'is_displayed': request.form['is_displayed'],
                'name': request.form['name'],
                'simple_description': request.form['simple_description'],
                'detail_description': request.form['detail_description'],
                'price': request.form['price'],
                'is_definite': request.form['is_definite'],
                'discount_rate': request.form['discount_rate'],
                'discount_price': request.form['discount_price'],
                'min_unit': request.form['min_unit'],
                'max_unit': request.form['max_unit'],
                'is_stock_managed': request.form['is_stock_managed'],
                'stock_number': request.form['stock_number'],
                'first_category_id': request.form['first_category_id'],
                'second_category_id': request.form['second_category_id'],
                'modifier_id': g.seller_info['seller_no'],
                'images': old_images,
                'discount_start_date': request.form['discount_start_date'],
                'discount_end_date': request.form['discount_end_date']
            }

            # S3 이미지 저장
            images = list()

            # 1~5 개의 이미지를 가져온다.
            for idx in range(1, 6):
                image = request.files.get(f'image_{idx}', None)

                if image:
                    images.append(image)

            new_images = product_service.upload_image(product_info['product_code'], images)

            # 반환된 image_urls 가 허용되지 않은 확장자일 경우 400 에러 메시지를 반환한다.
            if 400 in new_images:
                return new_images

            product_info['new_images'] = new_images

            product_service.update_product(product_info, session)

            session.commit()

            is_success = True

            return jsonify({'message': 'SUCCESS'}), 200

        except KeyError:
            return jsonify({'message': 'KEY_ERROR'}), 400

        except exc.IntegrityError:
            return jsonify({'message': 'INTEGRITY_DATA'}), 400

        except exc.InvalidRequestError:
            return jsonify({'message': 'INVALID_REQUEST'}), 400

        except exc.ProgrammingError:
            return jsonify({'message': 'ERROR_IN_SQL_SYNTAX'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            if is_success is False:
                session.rollback()
                delete_image_in_s3(old_images, new_images)
            session.close()

    return product_app
Exemple #12
0
from models import Clothes_image
from database import DB_Manager
from datetime import datetime, timedelta

app = Flask(__name__)
DBManager = DB_Manager()
CORS(app)

@app.route('/')
def index():
    return "Hello"


@app.route('/api/check/id', methods=['GET'])
@validate_params(
    Param('userid', GET, str, rules=[Pattern(r'^[a-z0-9]+$')], required=True),  
)
def check_id(*request_element):
    userid = request_element[0]
    check = User.query.filter_by(userid=userid).first()

    if check is None:
        return {'id': True}
    else:
        return {'id': False}


@app.route('/api/register', methods=['POST'])
@validate_params(
    Param('userid', JSON, str, rules=[Pattern(r'^[a-z0-9]+$')], required=True),  
    Param('passwd', JSON, str, rules=[Pattern(r'[a-zA-Z0-9_-]')], required=True),
class OrderView:
    order_app = Blueprint('order_app', __name__, url_prefix='/orders')

    @order_app.route('/filter_options/<order_status_id>', methods=['GET'])
    @validate_params(
        Param('order_status_id', PATH, int, rules=[Enum(1, 2, 3, 4, 5)]))
    @login_validator
    def get_filter_options(order_status_id):
        # 주문관리 페이지에서 셀러속성 리스트와 주문상태 변경 버튼 보내주는 엔드포인트
        order_service = OrderService()

        connection = None
        try:
            connection = connect_db()
            filter_options = order_service.get_filter_options(
                connection, order_status_id)
            return jsonify(filter_options), 200

        except Exception as e:
            return jsonify({"message": f'{e}'}), 400

        finally:
            try:
                if connection:
                    connection.close()
            except Exception as e:
                return jsonify({"message": f'{e}'}), 500

    @order_app.route('', methods=['GET'])
    @validate_params(
        Param('order_status_id',
              GET,
              int,
              rules=[Enum(1, 2, 3, 4, 5)],
              required=True),
        Param('order_number',
              GET,
              str,
              rules=[Pattern('^[0-9]{16,}$')],
              required=False),
        Param('detailed_order_number',
              GET,
              str,
              rules=[Pattern('^[0-9]{17,}$')],
              required=False),
        Param('buyer_name', GET, str, rules=[Pattern('[\S]')], required=False),
        Param('phone_number',
              GET,
              str,
              rules=[Pattern('^[0-9]{11}$')],
              required=False),
        Param('seller_name', GET, str, rules=[Pattern('[\S]')],
              required=False),
        Param('product_name',
              GET,
              str,
              rules=[Pattern('[\S]')],
              required=False),
        Param(
            'start_date',
            GET,
            str,
            rules=[
                Pattern(
                    '^(20)[\d]{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$')
            ],
            required=False),
        Param(
            'end_date',
            GET,
            str,
            rules=[
                Pattern(
                    '^(20)[\d]{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$')
            ],
            required=False),
        # 셀러속성 다중선택 가능
        Param('seller_type_id',
              GET,
              list,
              rules=[MinLength(1), MaxLength(7)],
              required=False),
        Param('limit',
              GET,
              int,
              rules=[Enum(10, 20, 50, 100, 150)],
              required=False),
        Param('order_by',
              GET,
              str,
              rules=[Enum('desc', 'asc')],
              required=False),
        Param('page', GET, int, required=False))
    @login_validator
    def get_order_list(*args):
        # 주문상태별 주문 리스트 필터하여 보내주는 엔드포인트
        order_service = OrderService()

        # page parameter에 0이나 음수가 들어올 경우 키에러
        if args[12] and args[12] <= 0:
            return jsonify({"key error": "Page cannot be negative"}), 400

        order_filter = {
            'order_status_id': args[0],
            'order_number': args[1],
            'detailed_order_number': args[2],
            'buyer_name': args[3],
            'phone_number': args[4],
            'seller_name': args[5],
            'product_name': args[6],
            'start_date': args[7],
            'end_date': args[8],
            'seller_type_id': args[9],
            # 디폴트값: 최신주문일순, 50개씩 보기
            'limit': args[10] if args[10] else 50,
            'order_by': args[11] if args[11] else 'desc',
            'page': args[12] if args[12] else 1
        }

        # 셀러일 경우 필터에 seller_id 추가
        if g.token_info['account_type_id'] == 2:
            order_filter['seller_id'] = g.token_info['seller_id']

        connection = None
        try:
            connection = connect_db()
            order_list = order_service.get_order_list(connection, order_filter)
            return jsonify(order_list), 200

        except Exception as e:
            return jsonify({"message": f"{e}"}), 400

        finally:
            try:
                if connection:
                    connection.close()
            except Exception as e:
                return jsonify({"message": f"{e}"}), 500

    @order_app.route('', methods=['POST'])
    @validate_params(
        Param('order_item_id', JSON, list, required=True),
        # 2: 상품준비 3: 배송중
        Param('order_status_id', GET, int, rules=[Enum(2, 3)], required=True),
        # 1: 배송처리 2: 배송완료처리
        Param('order_action_id', JSON, int, rules=[Enum(1, 2)], required=True))
    @login_validator
    def update_order_status(*args):
        # 주문 id를 리스트로 받아서 일괄적으로 주문 상태를 업데이트하는 엔드포인트
        order_service = OrderService()

        update_status = {
            'editor_id': g.token_info['account_id'],
            'order_item_id': args[0],
            'order_status_id': args[1],
            'order_action_id': args[2]
        }

        connection = None
        try:
            connection = connect_db()
            number_of_orders_updated = order_service.update_order_status(
                connection, update_status)
            connection.commit()
            return jsonify({
                "message":
                f"{number_of_orders_updated} order(s) successfully updated"
            }), 201

        except Exception as e:
            connection.rollback()
            return jsonify({"message": f"{e}"}), 400

        finally:
            try:
                if connection:
                    connection.close()
            except Exception as e:
                return jsonify({"message": f"{e}"}), 500

    @order_app.route('/<order_item_id>', methods=['GET'])
    @validate_params(Param('order_item_id', PATH, int))
    @login_validator
    def get_order_detail(order_item_id):
        # 주문 상세정보 가져오는 엔드포인트

        order_service = OrderService()

        order_filter = {'order_item_id': order_item_id}

        # # 셀러일 경우 필터에 seller_id 추가
        # if g.token_info['account_type_id'] == 2:
        #     order_filter['seller_id'] = g.token_info['seller_id']

        connection = None
        try:
            connection = connect_db()
            order_detail = order_service.get_order_detail(
                connection, order_filter)
            return jsonify(order_detail), 200

        except Exception as e:
            return jsonify({"message": f"{e}"}), 400

        finally:
            try:
                if connection:
                    connection.close()
            except Exception as e:
                return jsonify({"message": f"{e}"}), 500

    @order_app.route('/<order_item_id>', methods=['POST'])
    @validate_params(Param('order_item_id', PATH, int, required=True),
                     Param('new_order_status_id',
                           JSON,
                           int,
                           rules=[Enum(2, 3, 4, 5)],
                           required=False),
                     Param('phone_number',
                           JSON,
                           str,
                           rules=[Pattern('^[0-9]{11}')],
                           required=False),
                     Param('address_1',
                           JSON,
                           str,
                           rules=[Pattern('[\S]')],
                           required=False),
                     Param('address_2',
                           JSON,
                           str,
                           rules=[Pattern('[\S]')],
                           required=False),
                     Param('zip_code',
                           JSON,
                           str,
                           rules=[Pattern('^[0-9]{5}')],
                           required=False),
                     Param('delivery_instruction', JSON, str, required=False))
    @login_validator
    def update_order_detail(*args):
        # 주문 상세정보(주문상태, 연락처, 배송지 정보)를 업데이트하고 업데이트된 정보를 리턴

        order_service = OrderService()

        order_filter = {'order_item_id': args[0]}

        update_order = {
            'editor_id': g.token_info['account_id'],
            'order_item_id': args[0],
            'new_order_status_id': args[1],
            'phone_number': args[2],
            'address_1': args[3],
            'address_2': args[4],
            'zip_code': args[5],
            'delivery_instruction': args[6],
        }

        # 셀러일 경우 필터에 seller_id 추가
        if g.token_info['account_type_id'] == 2:
            order_filter['seller_id'] = g.token_info['seller_id']

        connection = None
        try:
            connection = connect_db()
            order_service.update_order_detail(connection, update_order)
            updated_order_detail = order_service.get_order_detail(
                connection, order_filter)
            connection.commit()
            return jsonify({"updated_order_detail": updated_order_detail}), 201

        except Exception as e:
            connection.rollback()
            return jsonify({"message": f"{e}"}), 400

        finally:
            try:
                if connection:
                    connection.close()
            except Exception as e:
                return jsonify({"message": f"{e}"})
Exemple #14
0
def create_admin_product_endpoints(product_service):

    # 'admin/product' end point prefix 설정
    admin_product_app = Blueprint('product_app',
                                  __name__,
                                  url_prefix='/admin/product')

    @admin_product_app.route('', methods=['POST'])
    @catch_exception
    @validate_params(
        Param('mainCategoryId',
              FORM,
              str,
              rules=[Pattern(r'^([1-9]|1[0-1])$')]),
        Param('subCategoryId',
              FORM,
              str,
              rules=[Pattern(r'^([1-9]|[1-5][0-9]|6[0-2])$')]),
        Param('sellYn', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('exhibitionYn', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('productName', FORM, str),
        Param('simpleDescription', FORM, str, required=False),
        Param('detailInformation', FORM, str), Param('price', FORM, float),
        Param('discountRate',
              FORM,
              str,
              required=False,
              rules=[Pattern(r"^(0|[1-9][0-9]?|100)$")]),
        Param(
            'discountStartDate',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01]) (00|[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param(
            'discountEndDate',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01]) (00|[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param('minSalesQuantity',
              FORM,
              str,
              rules=[Pattern(r"^([1-9]|1[0-9]|20)$")]),
        Param('maxSalesQuantity',
              FORM,
              str,
              rules=[Pattern(r"^([1-9]|1[0-9]|20)$")]),
        Param('optionQuantity', FORM, str))
    def product_register(*args):
        """

        [상품관리 > 상품등록] - 엔드포인트 Function
        [POST] http://ip:5000/admin/product

        Args:
            request.form:
                mainCategoryId      : Main Category ID
                subCategoryId       : Sub Category ID
                sellYn              : 상품 판매여부 (Boolean)
                exhibitionYn        : 상품 진열여부 (Boolean)
                productName         : 상품이름
                simpleDescription   : 상품 한 줄 설명
                detailInformation   : 상품 상세 설명
                price               : 상품가격
                discountRate        : 상품 할인율
                discountStartDate   : 할인 시작일
                discountEndDate     : 할인 종료일
                minSalesQuantity    : 최소판매 수량
                maxSalesQuantity    : 최대판매 수량
                optionQuantity      : 옵션별 수량 List
                    {
                        colorId  : 상품 색상 id
                        sizeId   : 상품 사이즈 id
                        quantity : 상품 재고수량
                    }

            request.files
                product_image_(No.) : 상품이미지 파일(Number: 1-5)

        Returns:
            200 : SUCCESS, 상품등록 완료 message
            400 : VALIDATION_ERROR, KEY_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-08-25 ([email protected]) : 초기생성
            2020-08-26 ([email protected]) : controller, service, model role 재정의에 따른 함수수정
            2020-08-28 ([email protected]) : product_images, images 저장 기능추가
            2020-08-30 ([email protected]) : product option 별 재고수량 저장 기능추가
            2020-09-02 ([email protected]) : product_code column 추가에 따른 구조 수정
            2020-09-08 ([email protected]) : request Validation Check 추가
            2020-09-09 ([email protected]) : Validation Check 고도화

        """

        # finally error 발생 방지
        db_connection = None

        try:

            db_connection = get_connection()

            if db_connection:

                product_info = {
                    'mainCategoryId': args[0],
                    'subCategoryId': args[1],
                    'sellYn': args[2],
                    'exhibitionYn': args[3],
                    'productName': args[4],
                    'simpleDescription': args[5],
                    'detailInformation': args[6],
                    'price': args[7],
                    'discountRate': args[8],
                    'discountStartDate': args[9],
                    'discountEndDate': args[10],
                    'minSalesQuantity': args[11],
                    'maxSalesQuantity': args[12],
                    'optionQuantity': args[13]
                }

                if product_info['minSalesQuantity'] > product_info[
                        'maxSalesQuantity']:
                    product_info['minSalesQuantity'] = product_info[
                        'maxSalesQuantity']

                # 사이즈 별(Large, Medium, Small) 상품이미지 저장 위한 S3 Connection Instance 생성
                s3_connection = get_s3_connection()
                images = request.files

                # 상품정보를 DB에 저장하는 Function 실행
                product_id = product_service.create_product(
                    product_info, db_connection)

                # 상품이미지를 사이즈 별로 S3에 저장 및 URL을 DB에 Insert 하는 Function 실행
                product_service.upload_product_image(images, product_id,
                                                     s3_connection,
                                                     db_connection)

                # Exception이 발생하지 않았다면, commit 처리
                db_connection.commit()

                return jsonify({'message': 'SUCCESS'}), 200

        except pymysql.err.InternalError:
            db_connection.rollback()
            return jsonify({'message': 'DATABASE_DOES_NOT_EXIST'}), 500
        except pymysql.err.OperationalError:
            return jsonify({'message': 'DATABASE_AUTHORIZATION_DENIED'}), 500
        except pymysql.err.ProgrammingError:
            db_connection.rollback()
            return jsonify({'message': 'DATABASE_SYNTAX_ERROR'}), 500
        except pymysql.err.IntegrityError:
            db_connection.rollback()
            return jsonify({'message': 'FOREIGN_KEY_CONSTRAINT_ERROR'}), 500
        except pymysql.err.DataError:
            db_connection.rollback()
            return jsonify({'message': 'DATA_ERROR'}), 400
        except KeyError:
            db_connection.rollback()
            return jsonify({'message' 'KEY_ERROR'}), 400
        except json.decoder.JSONDecodeError as e:
            db_connection.rollback()
            return jsonify({'message':
                            'optionQuantity_VALUE_INVALID_JSON'}), 400
        except Exception as e:
            db_connection.rollback()
            return jsonify({'message': f'{e}'}), 500

        finally:
            if db_connection:
                db_connection.close()

    @admin_product_app.route('/option', methods=['GET'])
    def option_list():
        """

        [ 상품관리 > 상품등록] 옵션(색상, 사이즈) List Return 엔드포인트
        [GET] http://ip:5000/admin/product/option

        Returns:
            200 :
                "data" : [
                    "color":[
                        {
                            "color_no" : {color_no} ,
                            "name"     : "{color_nam}"
                        }
                    ]
                    "size":[
                        {
                            "size_no" : {size_no},
                            "name"    : "{size_name}"
                        }
                    ]
                ]
            400 : VALIDATION_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-08-29 ([email protected]) : 초기생성
            2020-09-02 ([email protected]) : 옵션(색상, 사이즈) 통합 형태로 제공
            2020-09-09 ([email protected]) : Validation Check 고도화

        """

        # finally error 발생 방지
        db_connection = None

        try:
            db_connection = get_connection()
            if db_connection:

                # get_option_list 함수 호출해 색상 List 받아오기
                options = product_service.get_option_list(db_connection)

                return jsonify({'data': options}), 200

        except pymysql.err.InternalError:
            return jsonify({'message': 'DATABASE_DOES_NOT_EXIST'}), 500
        except pymysql.err.OperationalError:
            return jsonify({'message': 'DATABASE_AUTHORIZATION_DENIED'}), 500
        except pymysql.err.ProgrammingError:
            return jsonify({'message': 'DATABASE_SYNTAX_ERROR'}), 500
        except pymysql.err.IntegrityError:
            return jsonify({'message': 'FOREIGN_KEY_CONSTRAINT_ERROR'}), 500
        except pymysql.err.DataError:
            return jsonify({'message': 'DATA_ERROR'}), 400
        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            if db_connection:
                db_connection.close()

    @admin_product_app.route('/category', methods=['GET'])
    def main_category_list():
        """

        [ 상품관리 > 상품등록] Main Category Return 엔드포인트
        [GET] http://ip:5000/admin/product/category

        Returns:
            200 :
                "data": [
                    {
                      "main_category_no" : {main_category_id},
                      "name"             : "{main_category_name}"
                    }
                ]
            400 : VALIDATION_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-08-30 ([email protected]) : 초기생성
            2020-09-09 ([email protected]) : Validation Check 고도화

        """

        # finally error 발생 방지
        db_connection = None

        try:
            db_connection = get_connection()

            if db_connection:

                # get_main_category_list 함수 호출해 Main Category List 받아오기
                main_category = product_service.get_main_category_list(
                    db_connection)

                return jsonify({'data': main_category}), 200

        except pymysql.err.InternalError:
            return jsonify({'message': 'DATABASE_DOES_NOT_EXIST'}), 500
        except pymysql.err.OperationalError:
            return jsonify({'message': 'DATABASE_AUTHORIZATION_DENIED'}), 500
        except pymysql.err.ProgrammingError:
            return jsonify({'message': 'DATABASE_SYNTAX_ERROR'}), 500
        except pymysql.err.IntegrityError:
            return jsonify({'message': 'FOREIGN_KEY_CONSTRAINT_ERROR'}), 500
        except pymysql.err.DataError:
            return jsonify({'message': 'DATA_ERROR'}), 400
        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            if db_connection:
                db_connection.close()

    @admin_product_app.route('/category/<main_category_id>', methods=['GET'])
    @catch_exception
    @validate_params(Param('main_category_id', PATH, int))
    def sub_category_list(*args):
        """

        [ 상품관리 > 상품등록] Sub Category Return 엔드포인트
        [GET] http://ip:5000/admin/product/category/<main_category_id>

        Args:
            Parameter:
                mainCategoryId : (int) 메인 카테고리 ID

        Returns:
            200 :
                "data": [
                    {
                      "name"            : "{sub_category_name}",
                      "sub_category_no" : {sub_category_no}
                    }
                ]
            400 : VALIDATION_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-08-30 ([email protected]) : 초기생성
            2020-09-09 ([email protected]) : Validation Check 고도화

        """

        # finally error 발생 방지
        db_connection = None

        try:
            db_connection = get_connection()

            if db_connection:

                main_category_id = args[0]

                # sub_category_list 함수 호출해 Sub Category List 받아오기
                sub_category = product_service.get_sub_category_list(
                    main_category_id, db_connection)

                return jsonify({'data': sub_category}), 200

        except pymysql.err.InternalError:
            return jsonify({'message': 'DATABASE_DOES_NOT_EXIST'}), 500
        except pymysql.err.OperationalError:
            return jsonify({'message': 'DATABASE_AUTHORIZATION_DENIED'}), 500
        except pymysql.err.ProgrammingError:
            return jsonify({'message': 'DATABASE_SYNTAX_ERROR'}), 500
        except pymysql.err.IntegrityError:
            return jsonify({'message': 'FOREIGN_KEY_CONSTRAINT_ERROR'}), 500
        except pymysql.err.DataError:
            return jsonify({'message': 'DATA_ERROR'}), 400
        except KeyError:
            return jsonify({'message' 'KEY_ERROR'}), 400
        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            if db_connection:
                db_connection.close()

    @admin_product_app.route('', methods=['GET'])
    @catch_exception
    @validate_params(
        Param('sellYn',
              GET,
              str,
              required=False,
              rules=[Pattern(r'^([0-1])$')]),
        Param('discountYn',
              GET,
              str,
              required=False,
              rules=[Pattern(r'^([0-1])$')]),
        Param('exhibitionYn',
              GET,
              str,
              required=False,
              rules=[Pattern(r'^([0-1])$')]),
        Param('startDate', GET, int, required=False, rules=[DatetimeRule()]),
        Param('endDate', GET, int, required=False, rules=[DatetimeRule()]),
        Param('productName', GET, str, required=False),
        Param('productNo', GET, int, required=False),
        Param('productCode', GET, str, required=False),
        Param('page', GET, int, rules=[PageRule()]),
        Param('limit', GET, int, rules=[LimitRule()]))
    def registered_product_list(*args):
        """

        [ 상품관리 > 상품등록] Sub Category Return 엔드포인트
        [GET] http://ip:5000/admin/product

        Args:
            Parameter: 미적용시 filter에서 제외
                sellYN       : 판매 여부(1|0)
                exhibitionYn : 진열 여부(1|0)
                discountYn   : 할인 여부(1|0)
                registDate   : 등록 일자(기준 시작일, 기준 종료일)
                {
                    startDate : "YYYYmmdd",
                    endDate   : "YYYYmmdd"
                }
                productName  : 상품 이름
                productNo    : 상품 번호
                productCode  : 상품 코드
                limit        : 페이지 당 상품 수
                page         : 페이지 리스트 시작 기준

        Returns:
            200 :
                "data": [
                    [
                        {
                            discountPrice        : 할인가
                            discountRate         : 할인율
                            discountYn           : 할인 여부
                            productCode          : 상품 코드
                            productExhibitYn     : 진열 여부
                            productName          : 상품 이름
                            productNo            : 상품 번호
                            productRegistDate    : 상품 등록 일시
                            productSellYn        : 판매 여부
                            productSmallImageUrl : SMALL SIZE IMAGE URL
                            sellPrice            : 상품 가격
                        }
                    ],
                        {
                            "total": 검색된 상품 개수
                        }
                ]
            400 : VALIDATION_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-08-31 ([email protected]) : 초기생성
            2020-09-09 ([email protected]) : Validation Check 고도화

        """

        # finally error 발생 방지
        db_connection = None

        try:
            db_connection = get_connection()

            if db_connection:

                filter_info = {
                    'sellYn': args[0],
                    'discountYn': args[1],
                    'exhibitionYn': args[2],
                    'startDate': args[3],
                    'endDate': args[4],
                    'productName': args[5],
                    'productNo': args[6],
                    'productCode': args[7],
                    'page': args[8],
                    'limit': args[9]
                }

                # 상품 List, Totacl Count 받는 service 함수 호출
                product_list = product_service.get_registered_product_list(
                    filter_info, db_connection)

                return jsonify({'data': product_list}), 200

        except pymysql.err.InternalError:
            return jsonify({'message': 'DATABASE_DOES_NOT_EXIST'}), 500
        except pymysql.err.OperationalError:
            return jsonify({'message': 'DATABASE_AUTHORIZATION_DENIED'}), 500
        except pymysql.err.ProgrammingError:
            return jsonify({'message': 'DATABASE_SYNTAX_ERROR'}), 500
        except pymysql.err.IntegrityError:
            return jsonify({'message': 'FOREIGN_KEY_CONSTRAINT_ERROR'}), 500
        except pymysql.err.DataError:
            return jsonify({'message': 'DATA_ERROR'}), 400
        except KeyError:
            return jsonify({'message' 'KEY_ERROR'}), 400
        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            if db_connection:
                db_connection.close()

    @admin_product_app.route('/detail-image', methods=['POST'])
    def product_detail_image_upload():
        """

        [상품관리 > 상품등록] - 엔드포인트 Function
        [POST] http://ip:5000/admin/product/detail-image

        Args:
            request.files
                product_detail_image_url : 상품 상세 image URL

        Returns:
            200 : 상품 상세 image URL
            400 : VALIDATION_ERROR, KEY_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-09-03 ([email protected]) : 초기 생성
            2020-09-04 ([email protected]) : CKEditor 양식에 맞춰 수정

        """

        try:
            # 상품의 상세 설명 이미지의 저장을 위한 S3 Connection Instance 생성
            s3_connection = get_s3_connection()
            image = request.files

            # 상품의 상세 설명 이미지의 S3 저장을 위한 Function 실행
            image_url_info = product_service.upload_detail_image(
                image, s3_connection)

            return jsonify(image_url_info), 200

        except Exception as e:
            return jsonify({'message': f"{e}"}), 400

    @admin_product_app.route('/<product_id>', methods=['GET'])
    @catch_exception
    @validate_params(Param('product_id', PATH, int))
    def product_detail(*args):
        """

        [ 상품관리 > 상품등록] 상품 수정을 위한 Detail 내역 Return 엔드포인트
        [GET] http://ip:5000/admin/product/<product_id>

        Args:
            Parameter:
                productId : (int) Product Id(상품 테이블 PK)

        Returns:
            200 :
                "data": [
                    {
                      "name"            : "{sub_category_name}",
                      "sub_category_no" : {sub_category_no}
                    }
                ]
            400 : VALIDATION_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-09-05 ([email protected]) : 초기생성
            2020-09-09 ([email protected]) : Validation Check 고도화

        """

        # finally error 발생 방지
        db_connection = None

        try:
            db_connection = get_connection()

            if db_connection:

                product_id = args[0]

                # sub_category_list 함수 호출해 Sub Category List 받아오기
                product_info = product_service.get_product_detail(
                    product_id, db_connection)

                return jsonify({'data': product_info}), 200

        except pymysql.err.InternalError:
            return jsonify({'message': 'DATABASE_DOES_NOT_EXIST'}), 500
        except pymysql.err.OperationalError:
            return jsonify({'message': 'DATABASE_AUTHORIZATION_DENIED'}), 500
        except pymysql.err.ProgrammingError:
            return jsonify({'message': 'DATABASE_SYNTAX_ERROR'}), 500
        except pymysql.err.IntegrityError:
            return jsonify({'message': 'FOREIGN_KEY_CONSTRAINT_ERROR'}), 500
        except pymysql.err.DataError:
            return jsonify({'message': 'DATA_ERROR'}), 400
        except KeyError:
            return jsonify({'message' 'KEY_ERROR'}), 400
        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            if db_connection:
                db_connection.close()

    @admin_product_app.route('/<product_id>', methods=['PUT'])
    @catch_exception
    @validate_params(
        Param('product_id', PATH, int),
        Param('mainCategoryId',
              FORM,
              str,
              rules=[Pattern(r'^([1-9]|1[0-1])$')]),
        Param('subCategoryId',
              FORM,
              str,
              rules=[Pattern(r'^([1-9]|[1-5][0-9]|6[0-2])$')]),
        Param('sellYn', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('exhibitionYn', FORM, str, rules=[Pattern(r'^([0-1])$')]),
        Param('productName', FORM, str),
        Param('simpleDescription', FORM, str, required=False),
        Param('detailInformation', FORM, str), Param('price', FORM, float),
        Param('discountRate',
              FORM,
              str,
              required=False,
              rules=[Pattern(r"^(0|[1-9][0-9]?|100)$")]),
        Param(
            'discountStartDate',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01]) (00|[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param(
            'discountEndDate',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^\d\d\d\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01]) (00|[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9])$"
                )
            ]),
        Param('minSalesQuantity',
              FORM,
              str,
              rules=[Pattern(r"^([1-9]|1[0-9]|20)$")]),
        Param('maxSalesQuantity',
              FORM,
              str,
              rules=[Pattern(r"^([1-9]|1[0-9]|20)$")]),
        Param('optionQuantity', FORM, str))
    def product_modify(*args):
        """

        [상품관리 > 상품등록] - 엔드포인트 Function
        [PUT] http://ip:5000/admin/product

        Args:
            request.form:
                mainCategoryId      : Main Category ID
                subCategoryId       : Sub Category ID
                sellYn              : 상품 판매여부 (Boolean)
                exhibitionYn        : 상품 진열여부 (Boolean)
                productName         : 상품이름
                simpleDescription   : 상품 한 줄 설명
                detailInformation   : 상품 상세 설명
                price               : 상품가격
                discountRate        : 상품 할인율
                discountStartDate   : 할인 시작일
                discountEndDate     : 할인 종료일
                minSalesQuantity    : 최소판매 수량
                maxSalesQuantity    : 최대판매 수량
                optionQuantity      : 옵션별 수량 List
                    {
                        colorId  : 상품 색상 id
                        sizeId   : 상품 사이즈 id
                        quantity : 상품 재고수량
                    }

            request.files
                product_image_(No.) : 상품이미지 파일(Number: 1-5)

        Returns:
            200 : SUCCESS, 상품등록 완료 message
            400 : VALIDATION_ERROR, KEY_ERROR
            500 : NO_DATABASE_CONNECTION_ERROR

        Author:
            [email protected] (이곤호)

        History:
            2020-09-06 ([email protected]) : 초기생성
            2020-09-09 ([email protected]) : Validation Check 고도화

        """

        # finally error 발생 방지
        db_connection = None

        try:

            db_connection = get_connection()

            if db_connection:

                product_id = args[0]
                product_info = {
                    'mainCategoryId': int(args[1]),
                    'subCategoryId': int(args[2]),
                    'sellYn': int(args[3]),
                    'exhibitionYn': int(args[4]),
                    'productName': args[5],
                    'simpleDescription': args[6],
                    'detailInformation': args[7],
                    'price': args[8],
                    'discountRate': int(args[9]),
                    'discountStartDate': args[10],
                    'discountEndDate': args[11],
                    'minSalesQuantity': int(args[12]),
                    'maxSalesQuantity': int(args[13]),
                    'optionQuantity': args[14]
                }

                if product_info['minSalesQuantity'] > product_info[
                        'maxSalesQuantity']:
                    product_info['minSalesQuantity'] = product_info[
                        'maxSalesQuantity']

                # DB 저장 내역과 비교를 위한 price value Decimal 변경
                product_info['price'] = round(Decimal(product_info['price']),
                                              2)

                # DB 저장 내역과 비교를 위한 discountStartDate, discountEndDate datetime 형태로 변경
                if product_info['discountStartDate'] is not None:
                    product_info['discountStartDate'] = datetime.strptime(
                        product_info['discountStartDate'], '%Y-%m-%d %H:%M')
                if product_info['discountEndDate'] is not None:
                    product_info['discountEndDate'] = datetime.strptime(
                        product_info['discountEndDate'], '%Y-%m-%d %H:%M')

                # 사이즈 별(Large, Medium, Small) 상품이미지 저장 위한 S3 Connection Instance 생성
                s3_connection = get_s3_connection()
                images = request.files

                # 상품정보를 DB에 저장하는 Function 실행
                product_service.update_product(product_id, product_info,
                                               db_connection)

                # 기존 상품 이미지(product_images, images)를 제거하고
                # 업데이트한 상품이미지를 사이즈 별로 S3에 저장 및 URL을 DB에 Insert 하는 Function 실행
                product_service.update_product_image(images, product_id,
                                                     s3_connection,
                                                     db_connection)

                # Exception이 발생하지 않았다면, commit 처리
                db_connection.commit()

                return jsonify({'message': 'SUCCESS'}), 200

        except pymysql.err.InternalError:
            db_connection.rollback()
            return jsonify({'message': 'DATABASE_DOES_NOT_EXIST'}), 500
        except pymysql.err.OperationalError:
            return jsonify({'message': 'DATABASE_AUTHORIZATION_DENIED'}), 500
        except pymysql.err.ProgrammingError:
            db_connection.rollback()
            return jsonify({'message': 'DATABASE_SYNTAX_ERROR'}), 500
        except pymysql.err.IntegrityError:
            db_connection.rollback()
            return jsonify({'message': 'FOREIGN_KEY_CONSTRAINT_ERROR'}), 500
        except pymysql.err.DataError:
            db_connection.rollback()
            return jsonify({'message': 'DATA_ERROR'}), 400
        except KeyError:
            db_connection.rollback()
            return jsonify({'message' 'KEY_ERROR'}), 400
        except json.decoder.JSONDecodeError as e:
            db_connection.rollback()
            return jsonify({'message':
                            'optionQuantity_VALUE_INVALID_JSON'}), 400
        except Exception as e:
            db_connection.rollback()
            return jsonify({'message': f'{e}'}), 500

        finally:
            if db_connection:
                db_connection.close()

    return admin_product_app
class EventView:
    """ 기획전 뷰

    Authors:
        [email protected] (이종민)
    History:
        2020-04-07 ([email protected]): 초기생성

    """

    event_app = Blueprint('event_app', __name__, url_prefix='/event')

    @event_app.route("", methods=["GET"], endpoint='get_all_events')
    @login_required
    @validate_params(Param('event_type_id', GET, list, required=False),
                     Param('event_name', GET, str, required=False),
                     Param('event_start_time', GET, str, required=False),
                     Param('event_end_time', GET, str, required=False),
                     Param('offset', GET, int, required=False),
                     Param('limit', GET, int, required=False))
    def get_all_events(*args):
        """ 등록된 모든 이벤트 목록 표출 엔드포인트

        Args:
            *args: 이벤트 정보
                event_type_id: 이벤트 타입
                event_name: 검색어에 포함되는 이벤트 이름
                event_start_time: 검색할 이벤트 등록 날짜 시작 지점
                event_end_time: 검색할 이벤트 등록 날짜 끝 지점

        Returns:
            200: 검색 조건에 맞는 이벤트 목록
            400: 유효하지 않은 검색 날짜 조건
            500: 데이터베이스 에러

        Authors:
            [email protected] (이소헌)

        History:
            2020-04-12 ([email protected]): 초기 생성
        """
        event_info = {
            'auth_type_id': g.account_info['auth_type_id'],
            'event_type_id': args[0],
            'event_name': args[1],
            'event_start_time': args[2],
            'event_end_time': args[3],
            'offset': args[4] if args[4] else 0,
            'limit': args[5] if args[5] else 10
        }
        if event_info['event_start_time'] and event_info['event_end_time']:
            if (datetime.strptime(event_info['event_start_time'], "%Y-%m-%d") \
                    > datetime.strptime(event_info['event_end_time'], "%Y-%m-%d")):
                return jsonify({'message': 'INVALID_EVENT_DATE'}), 400

        db_connection = None
        try:
            db_connection = get_db_connection()
            if db_connection:
                event_service = EventService()
                events = event_service.get_all_events(event_info,
                                                      db_connection)
                return events
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                if db_connection:
                    db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @event_app.route("/<int:event_no>",
                     methods=["GET"],
                     endpoint='get_event_infos')
    @login_required
    def get_event_infos(event_no):
        """ 기획전 정보 표출 엔드포인트

        기획전 정보 표출 엔드포인트 입니다.
        url parameter 로 받은 기획전 번호에 해당하는 정보를 표출합니다.

        Args:
            event_no: 기획전 번호

        Returns:
            200: 기획전 정보
            400: INVALID_EVENT_NO
            500: DB_CURSOR_ERROR, INVALID_KEY, NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)
            [email protected] (윤희철)

        History:
            2020-04-10 ([email protected]): 초기 생성
            2020-04-14 ([email protected]): 데이터베이스 커넥션 호출 시 try-catch 추가

        """

        # 마스터 권한이 아니면 반려
        if g.account_info['auth_type_id'] != 1:
            return jsonify({'message': 'NO_AUTHORIZATION'}), 403

        try:
            db_connection = get_db_connection()
            if db_connection:
                event_service = EventService()
                info = event_service.get_event_infos(event_no, db_connection)
                return info
            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()
            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @event_app.route('', methods=['POST'], endpoint='register_event_info')
    @login_required
    @validate_params(
        # 전체 기획전 필수값
        Param('event_type_id', FORM, int),
        Param('event_sort_id', FORM, int),
        Param('is_on_main', FORM, str, rules=[Pattern(r"^[0-1]{1}$")]),
        Param('is_on_event', FORM, str, rules=[Pattern(r"^[0-1]{1}$")]),
        Param('name', FORM, str, rules=[Pattern(r"^.{1,25}$")]),
        Param(
            'event_start_time',
            FORM,
            str,
            rules=[
                Pattern(
                    r"^([2][0]\d{2})-([0-2]\d)-([0-2]\d) ([0-2]\d):([0-5]\d)$")
            ]),
        Param(
            'event_end_time',
            FORM,
            str,
            rules=[
                Pattern(
                    r"^([2][0]\d{2})-([0-2]\d)-([0-2]\d) ([0-2]\d):([0-5]\d)$")
            ]),

        # 각 기획전 타입 필수값 여부는 function 내부에서 확인
        Param('short_description',
              FORM,
              str,
              required=False,
              rules=[MaxLength(45)]),
        Param('long_description', FORM, str, required=False),
        Param(
            'banner_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('banner_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param(
            'detail_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('detail_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param('button_name', FORM, str, required=False, rules=[MaxLength(10)]),
        Param('button_link_type_id', FORM, int, required=False),
        Param('button_link_description',
              FORM,
              str,
              required=False,
              rules=[MaxLength(45)]),
        Param('product', FORM, str, required=False),
        Param(
            'youtube_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('youtube_url', FORM, str, required=False,
              rules=[MaxLength(200)]),
        Param('event_type_id', FORM, str, rules=[Pattern(r"^[1-5]{1}$")]),
        Param('button_link_type_id',
              FORM,
              str,
              required=False,
              rules=[Pattern(r"^[1-6]{1}$")]))
    def register_event_info(*args):
        """ 기획전 등록 엔드포인트

        기획전을 신규 등록하는 엔드포인트 입니다.
        request.body 로 등록 정보를 받고, 유효성을 확인합니다.
        기획전 전 타입 공통 필수 파라미터는 먼저 확인하고,
        각 타입별 필수 파라미터는 function 내에서 확인합니다.

        확인이 끝나면 event_info 에 모든 파라미터를 저장합니다.
        등록을 수행하는 계정의 정보를 데코레이터에서 받아와 event_info 에 저장합니다.

        function 진입 후 마스터 권한이 없으면 에러를 리턴하고,
        마스터 권한이면 서비스로 값을 넘깁니다.

        Args:
            *args: 유효성 검사를 통과한 파라미터

        request.body:
            event_type_id 기획전 타입 외래키
            event_sort_id 기획전 종류 외래키
            is_on_main 메인 노출여부
            is_on_event 기획전 진열여부
            name 기획전명
            event_start_time 기획전 시작시간 (ex) 2020-04-10 23:59
            event_end_time 기획전 종료시간
            short_description 기획전 간략설명
            long_description 기획전 상세설명
            banner_image_url 배너 이미지 url
            detail_image_url 상세 이미지 url
            button_name 이벤트 버튼 이름
            button_link_type_id 이벤트 버튼 링크타입 외래키
            button_link_description 이벤트 버튼링크 내용
            product_order 상품 진열 순서
            product_id 상품 외래키
            youtube_url 유튜브 url

        Returns: http 응답코드
            200: SUCCESS 기획전 신규 등록 완료
            400: NO_SHORT_DESCRIPTION, BANNER_IMAGE_URL, NO_DETAIL_IMAGE_URL,
                 NO_BUTTON_NAME, NO_BUTTON_LINK_DESCRIPTION, INVALID_EVENT_SORT
            403: NO_AUTHORIZATION
            500: NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)
            [email protected] (윤희철)

        History:
            2020-04-07 ([email protected]): 초기생성 / 이벤트 기획전 부분 작성
            2020-04-08 ([email protected]): 기획전 기간 밸리데이션 추가
            2020-04-10 ([email protected]):
                - 상품(이미지), 상품(텍스트), 유튜브 기획전 작성
                - request form 형태로 오는 매니저 정보 list 를 parsing 해서 사용하는 로직 추가
                - 데이터베이스 커넥션을 호출 할 때 try/except 방식으로 변경
            2020-04-12 ([email protected]):
                - event_type_id 를 int 로 받아오도록 validator 변경
                - 기획전용 이미지 업로더를 사용하는 것에서 공통 업로더를 사용하도록 변경
                - 기획전 상품 정보를 json loads로 파싱하는 과정을 try/except 방식에서 if 문 방식으로 변경
        """
        if g.account_info['auth_type_id'] != 1:
            return jsonify({'message': 'NO_AUTHORIZATION'}), 403

        # 이미지 업로드 함수를 호출해서 이미지를 업로드하고 url 을 딕셔너리로 가져옴.
        image_upload = ImageUpload()
        event_image = image_upload.upload_images(request)

        # 함수의 실행결과에 400이 포함된 경우 애러메세지를 그대로 리턴함.
        if (400 in event_image) or (500 in event_image):
            return event_image

        # validation(형식) 확인된 데이터 저장
        event_info = {
            'event_type_id': args[0],
            'event_sort_id': args[1],
            'is_on_main': args[2],
            'is_on_event': args[3],
            'name': args[4],
            'event_start_time': args[5],
            'event_end_time': args[6],
            'short_description': args[7],
            'long_description': args[8],
            'banner_image_url': event_image.get('banner_image', None),
            'detail_image_url': event_image.get('detail_image', None),
            'button_name': args[13],
            'button_link_type_id': args[14],
            'button_link_description': args[15],
            'youtube_url': args[17],
            'auth_type_id': g.account_info['auth_type_id'],
            'account_no': g.account_info['account_no']
        }

        # file 로 이미지가 안들어올 경우, FORM 으로 받은 이미지 url 로 대체
        if not event_info['banner_image_url']:
            event_info['banner_image_url'] = args[10]

        if not event_info['detail_image_url']:
            event_info['detail_image_url'] = args[12]

        # 딕셔너리를 품은 리스트인 product 정보를 따로 저장 (dao 에서 에러를 막기 위해)
        event_product_info = args[16]

        # form 데이터로 값을 받으면 str 처리가 되기 때문에 json.loads 로 읽을 수 있게 파싱
        if event_product_info:
            event_product_info = json.loads(event_product_info)

        # 기획전 기간 밸리데이션
        now = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M')

        # 시작시간이 현재 시간보다 전이거나 시작시간이 종료시간보다 늦으면 에러 반환
        if event_info['event_start_time'] < now or event_info[
                'event_start_time'] > event_info['event_end_time']:
            return jsonify({'message': 'INVALID_EVENT_TIME'}), 400

        # 기획전 타입이 이벤트일 경우 필수값과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 1:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            if not event_info['detail_image_url']:
                return jsonify({'message': 'NO_DETAIL_IMAGE'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(1, 3):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 쿠폰일 경우 필수값 확인
        if event_info['event_type_id'] == 2:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(3, 9):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 상품(이미지)일 경우 필수값과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 3:
            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            if not event_info['detail_image_url']:
                return jsonify({'message': 'NO_DETAIL_IMAGE'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(9, 11):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 상품(텍스트)일 경우 필수값과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 4:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(11, 13):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 유튜브일 경우 필수값 확인과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 5:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            if not event_info['youtube_url']:
                return jsonify({'message': 'NO_YOUTUBE_URL'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(13, 15):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 입력 인자 관계에 따른 필수값 확인
        if event_info['button_link_type_id']:

            # button_link_type_id 가 있을 때 button_name 은 무조건 있어야 함
            if not event_info['button_name']:
                return jsonify({'message': 'NO_BUTTON_NAME'}), 400

            if event_info['button_link_type_id'] in range(4, 7):

                # button_link_type_id가 4~6이면 description 이 있어야 함
                if not event_info['button_link_description']:
                    return jsonify({'message':
                                    'NO_BUTTON_LINK_DESCRIPTION'}), 400

        try:
            # 데이터베이스 연결
            db_connection = get_db_connection()
            if db_connection:
                event_service = EventService()
                registering_event_result = event_service.register_event(
                    event_info, db_connection, event_product_info)
                return registering_event_result

            else:
                return jsonify({'view_message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @event_app.route("/type", methods=["GET"])
    @login_required
    def get_event_types():
        """ 기획전 타입 표출 엔드포인트

        기획전 타입 표출 엔드포인트 입니다.
        기획전 등록페이지에서 기획전 타입 목록을 표출할때 사용됩니다.

        Returns:
            200: 기획전 타입 목록
            500: DB_CURSOR_ERROR, INVALID_KEY, NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)

        History:
            2020-04-09 ([email protected]): 초기 생성

        """
        try:
            db_connection = get_db_connection()
            if db_connection:
                event_service = EventService()
                types = event_service.get_event_types(db_connection)
                return types

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @event_app.route("/type/<int:event_type_id>",
                     methods=["GET"],
                     endpoint='get_event_sorts')
    @login_required
    @validate_params(
        Param('event_type_id', PATH, str, rules=[Pattern(r"^[1-5]{1}$")]))
    def get_event_sorts(*args):
        """ 기획전 타입별 종류 표출 엔드포인트

        기획전 타입별 종류 표출 엔드포인트 입니다.
        기획전 등록페이지에서 기획전 타입 별 목록을 표출할때 사됩니다.

        Args:
            *args: 유효성 검사를 통과한 파라미터

        url Parameter:
            event_type_id: 기획전 타입 아이디

        Returns:
            200: 기획전 타입별 종류 목록
            500: DB_CURSOR_ERROR, INVALID_KEY, NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)

        History:
            2020-04-09 ([email protected]): 초기 생성

        """

        # event_type_id 저장
        event_type_info = {"event_type_id": args[0]}

        try:
            db_connection = get_db_connection()
            if db_connection:
                event_service = EventService()
                sorts = event_service.get_event_sorts(event_type_info,
                                                      db_connection)
                return sorts

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500

    @event_app.route("/<int:event_no>",
                     methods=["PUT"],
                     endpoint='change_event_infos')
    @login_required
    @validate_params(
        # 전체 기획전 필수값
        Param('event_no', PATH, int),
        Param('event_type_id', FORM, int),
        Param('event_sort_id', FORM, int),
        Param('is_on_main', FORM, str, rules=[Pattern(r"^[0-1]{1}$")]),
        Param('is_on_event', FORM, str, rules=[Pattern(r"^[0-1]{1}$")]),
        Param('name', FORM, str, rules=[Pattern(r"^.{1,25}$")]),
        Param(
            'event_start_time',
            FORM,
            str,
            rules=[
                Pattern(
                    r"^([2][0]\d{2})-([0-2]\d)-([0-2]\d) ([0-2]\d):([0-5]\d)$")
            ]),
        Param(
            'event_end_time',
            FORM,
            str,
            rules=[
                Pattern(
                    r"^([2][0]\d{2})-([0-2]\d)-([0-2]\d) ([0-2]\d):([0-5]\d)$")
            ]),

        # 각 기획전 타입 필수값 여부는 function 내부에서 확인
        Param('short_description',
              FORM,
              str,
              required=False,
              rules=[MaxLength(45)]),
        Param('long_description', FORM, str, required=False),
        Param(
            'banner_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('banner_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param(
            'detail_image_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('detail_image_url',
              FORM,
              str,
              required=False,
              rules=[MaxLength(200)]),
        Param('button_name', FORM, str, required=False, rules=[MaxLength(10)]),
        Param('button_link_type_id', FORM, int, required=False),
        Param('button_link_description',
              FORM,
              str,
              required=False,
              rules=[MaxLength(45)]),
        Param('product', FORM, str, required=False),
        Param(
            'youtube_url',
            FORM,
            str,
            required=False,
            rules=[
                Pattern(
                    r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
                )
            ]),
        Param('youtube_url', FORM, str, required=False,
              rules=[MaxLength(200)]),
        Param('event_type_id', FORM, str, rules=[Pattern(r"^[1-5]{1}$")]),
        Param('button_link_type_id',
              FORM,
              str,
              required=False,
              rules=[Pattern(r"^[1-6]{1}$")]))
    def change_event_infos(*args):
        """ 기획전 정보 수정 엔드포인트

        기획전 정보 수정 엔드포인트 입니다.
        form 과 url parameter 로 등록 정보를 받고, 유효성을 확인합니다.
        기획전 전 타입 공통 필수 파라미터는 먼저 확인하고,
        각 타입별 필수 파라미터는 function 내에서 확인합니다.

        확인이 끝나면 event_info 에 모든 파라미터를 저장합니다.
        등록을 수행하는 계정의 정보를 데코레이터에서 받아와 event_info 에 저장합니다.

        function 진입 후 마스터 권한이 없으면 에러를 리턴하고,
        마스터 권한이면 서비스로 값을 넘깁니다.

        Args:
            *args: 유효성 검사를 통과한 파라미터

        request.form:
            event_no: 기획전 번호
            event_type_id 기획전 타입 외래키
            event_sort_id 기획전 종류 외래키
            is_on_main 메인 노출여부
            is_on_event 기획전 진열여부
            name 기획전명
            event_start_time 기획전 시작시간 (ex) 2020-04-10 23:59
            event_end_time 기획전 종료시간
            short_description 기획전 간략설명
            long_description 기획전 상세설명
            banner_image_url 배너 이미지 url
            detail_image_url 상세 이미지 url
            button_name 이벤트 버튼 이름
            button_link_type_id 이벤트 버튼 링크타입 외래키
            button_link_description 이벤트 버튼링크 내용
            product 연결 상품 정보
            youtube_url 유튜브 url

        g.account_info: 데코레이터에서 넘겨받은 수정을 수행하는 계정 정보
            auth_type_id: 계정의 권한정보
            account_no: 데코레이터에서 확인된 계정번호

        Returns: http 응답코드
            200: SUCCESS 수정(새로운 이력 생성) 완료
            400: NOT_ALLOWED_TO_CHANGE_EVENT_TYPE_OR_SORT, INVALID_EVENT_NO
                 NO_SHORT_DESCRIPTION, NO_BANNER_IMAGE, NO_DETAIL_IMAGE,
                 INVALID_EVENT_SORT, NO_YOUTUBE_URL, NO_BUTTON_NAME, NO_BUTTON_LINK_DESCRIPTION
            500: DB_CURSOR_ERROR, INVALID_KEY, NO_DATABASE_CONNECTION

        Authors:
            [email protected] (이종민)
            [email protected] (윤희철)

        History:
            2020-04-10 ([email protected]): 초기 생성
            2020-04-11 ([email protected]):
                - utils.py(이미지 업로더) 에서 나오는 결과값에 애러코드 400이있으면 애러메세지를 그대로 리턴하는 코드 추가
                - 기획전 상품이 validation 을 통과하면 json loads 를 통해서 array 자료형으로 파싱하는 코드 추가.
                - 기획전 상품이 들어오면 따로 변수처리해서 service 로 넘기는 코드 추가
                - 데이터베이스 커넥션을 호출 할 때 try/except 방식 추가
            2020-04-12 ([email protected]):
                - 기획전용 이미지 업로더를 사용하는 것에서 공통 업로더를 사용하도록 변경
                - 기획전 상품 정보를 json loads 로 파싱하는 과정을 try/except 방식에서 if 문 방식으로 변경
                - 기획전 타입이 상품(텍스트), 유튜브 일 경우 기획전 종류 유효성 확인 추가
            2020-04-15 ([email protected]:
                - button_link_type_id 를 str 로 받던 것에서 int 로 받기로 변경
        """

        # 마스터 권한이 아니면 반려
        if g.account_info['auth_type_id'] != 1:
            return jsonify({'message': 'NO_AUTHORIZATION'}), 403

        # 이미지 업로드 함수를 호출해서 이미지를 업로드하고 url 을 딕셔너리로 가져옴.
        image_upload = ImageUpload()
        event_image = image_upload.upload_images(request)

        # 함수의 실행결과에 400이 포함된 경우 애러메세지를 그대로 리턴함.
        if (400 in event_image) or (500 in event_image):
            return event_image

        # validation(형식) 확인된 데이터 저장
        event_info = {
            'event_no': args[0],
            'event_type_id': args[1],
            'event_sort_id': args[2],
            'is_on_main': args[3],
            'is_on_event': args[4],
            'name': args[5],
            'event_start_time': args[6],
            'event_end_time': args[7],
            'short_description': args[8],
            'long_description': args[9],
            'banner_image_url': event_image.get('banner_image', None),
            'detail_image_url': event_image.get('detail_image', None),
            'button_name': args[14],
            'button_link_type_id': args[15],
            'button_link_description': args[16],
            'youtube_url': args[18],
            'auth_type_id': g.account_info['auth_type_id'],
            'account_no': g.account_info['account_no']
        }

        # file 로 이미지가 안들어올 경우, FORM 으로 받은 이미지 url 로 대체
        if not event_info['banner_image_url']:
            event_info['banner_image_url'] = args[10]

        if not event_info['detail_image_url']:
            event_info['detail_image_url'] = args[12]

        # 딕셔너리를 품은 리스트인 product 정보를 따로 저장 (dao 에서 에러를 막기 위해)
        event_product_info = args[17]

        # form 데이터로 값을 받으면 str 처리가 되기 때문에 json.loads 로 읽을 수 있게 파싱
        if event_product_info:
            event_product_info = json.loads(event_product_info)

        # 기획전 기간 밸리데이션
        now = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M')

        # 시작시간이 현재 시간보다 전이거나 시작시간이 종료시간보다 늦으면 에러 반환
        if event_info['event_start_time'] < now or event_info[
                'event_start_time'] > event_info['event_end_time']:
            return jsonify({'message': 'INVALID_EVENT_TIME'}), 400

        # 기획전 타입이 이벤트일 경우 필수값과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 1:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            if not event_info['detail_image_url']:
                return jsonify({'message': 'NO_DETAIL_IMAGE'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(1, 3):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 쿠폰일 경우 필수값 확인
        if event_info['event_type_id'] == 2:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(3, 9):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 상품(이미지)일 경우 필수값과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 3:
            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            if not event_info['detail_image_url']:
                return jsonify({'message': 'NO_DETAIL_IMAGE'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(9, 11):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 상품(텍스트)일 경우 필수값과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 4:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(11, 13):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 기획전 타입이 유튜브일 경우 필수값 확인과 기획전 종류 범위 확인
        if event_info['event_type_id'] == 5:
            if not event_info['short_description']:
                return jsonify({'message': 'NO_SHORT_DESCRIPTION'}), 400

            if not event_info['banner_image_url']:
                return jsonify({'message': 'NO_BANNER_IMAGE'}), 400

            if not event_info['youtube_url']:
                return jsonify({'message': 'NO_YOUTUBE_URL'}), 400

            # 기획전 종류 범위 확인
            if event_info['event_sort_id'] not in range(13, 15):
                return jsonify({'message': 'INVALID_EVENT_SORT'}), 400

        # 입력 인자 관계에 따른 필수값 확인
        if event_info['button_link_type_id']:
            if not event_info['button_name']:
                return jsonify({'message': 'NO_BUTTON_NAME'}), 400

            if event_info['button_link_type_id'] in range(4, 7):
                if not event_info['button_link_description']:
                    return jsonify({'message':
                                    'NO_BUTTON_LINK_DESCRIPTION'}), 400

        try:
            db_connection = get_db_connection()
            if db_connection:
                event_service = EventService()
                info = event_service.change_event_infos(
                    event_info, event_product_info, db_connection)
                return info

            else:
                return jsonify({'message': 'NO_DATABASE_CONNECTION'}), 500

        except Exception as e:
            return jsonify({'message': f'{e}'}), 500

        finally:
            try:
                db_connection.close()

            except Exception as e:
                return jsonify({'message': f'{e}'}), 500
Exemple #16
0
def seller_endpoints(app, services, get_session):
    seller_service = services.seller_service

    @app.route("/signup", methods=['POST'])
    @validate_params(
        Param('brand_crm_number', JSON, str, rules=[Pattern(r'^[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}$')]),
        Param('password', JSON, str, rules=[Pattern(r'^(?=.*[0-9])(?=.*[A-Za-z])(?=.*[^a-zA-Z0-9]).{8,20}$')]),
        Param('phone_number', JSON, str, rules=[Pattern(r'^010-[0-9]{3,4}-[0-9]{4}$')]),
        Param('account', JSON, str, rules=[MinLength(5)]),
        Param('brand_name_korean', JSON, str),
        Param('brand_name_english', JSON, str),
        Param('seller_property_id', JSON, str)
    )
    def sign_up(*args):
        """ 회원가입 API

        회원가입을 위한 정보를 Body 에 받음

        Args:
            *args:
                brand_crm_number   : 브랜드 고객센터 번호
                password           : 비밀번호
                phone_number       : 담당자 핸드폰 번호
                account            : 계정
                brand_name_korean  : 브랜드명 ( 한글 )
                brand_name_english : 브랜드명 ( 영어 )
                seller_property_id : 셀러 속성 id ( 로드샵, 마켓 등 )

        Returns:
            200 : success , 회원가입 성공 시
            400 : 같은 계정이 존재할 때, key error
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            new_seller = {
                'brand_crm_number':     args[0],
                'password':             args[1],
                'phone_number':         args[2],
                'account':              args[3],
                'brand_name_korean':    args[4],
                'brand_name_english':   args[5],
                'seller_property_id':   args[6]
            }

            new_seller = seller_service.create_new_seller(new_seller, session)

            # 같은 계정이 이미 존재할 때
            if new_seller == 'already exist':
                return jsonify({'message': 'account already exist'}), 400

            session.commit()
            return jsonify({'message': 'success'}), 200

        except KeyError:
            return jsonify({'message': 'key error'}), 400

        except NoAffectedRowException as e:
            session.rollback()
            return jsonify({'message': 'no affected row error {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/login", methods=['POST'])
    @validate_params(
        Param('account', JSON, str),
        Param('password', JSON, str)
    )
    def log_in(*args):
        """

        Args:
            *args:
                account  : 계정
                password : 비밀번호

        Returns:
            200 : 로그인 성공 하면 access token 발행
            400 : 계정이 존재하지 않을 때, 비밀번호가 틀렸을 때, soft delete 된 계정일 때,
                셀러의 상태가 입점 대기 상태일 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()
            seller = {
                'account': args[0],
                'password': args[1]
            }
            token = seller_service.enter_login(seller, session)

            # 계정이 존재하지 않을 때
            if token == 'not exist':
                return jsonify({'message': 'account not exist'}), 400

            # 비밀번호가 틀렸을 때
            if token == 'wrong password':
                return jsonify({'message': 'wrong password'}), 400

            # 소프트 딜리트 된 계정일 때
            if token == 'deleted account':
                return jsonify({'message': 'account deleted'}), 400

            # 셀러 상태가 입점 대기 일 때 ( seller_status_id = 1 )
            if token == 'not authorized':
                return jsonify({'message': 'not authorized'}), 400

            return jsonify({'access_token': token})

        except KeyError:
            return jsonify({'message': 'key error'}), 400

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/mypage", methods=['GET'])
    @login_required
    def get_my_page():
        """ 셀러정보관리(셀러)

        셀러가 셀러 정보 관리를 들어갔을 때 등록된 셀러의 데이터 가져오기

        Returns:
            200 : seller_data ( type : dict )
            500 : Exception

        """
        session = None
        try:
            session = get_session()
            seller_data = seller_service.get_my_page(g.seller_id, session)

            return jsonify(seller_data)

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/mypage", methods=['PUT'])
    @login_required
    @validate_params(
        Param('image', JSON, str),
        Param('simple_introduce', JSON, str),
        Param('brand_crm_number', JSON, str, rules=[Pattern(r'^[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}$')]),
        Param('zip_code', JSON, int),
        Param('address', JSON, str),
        Param('detail_address', JSON, str),
        Param('brand_crm_open', JSON, str),
        Param('brand_crm_end', JSON, str),
        Param('delivery_information', JSON, str),
        Param('refund_exchange_information', JSON, str),
        Param('seller_status_id', JSON, int),
        Param('background_image', JSON, str, required=False),
        Param('detail_introduce', JSON, str, required=False),
        Param('is_brand_crm_holiday', JSON, int, rules=[Enum(0, 1)], required=False),
        Param('brand_name_korean', JSON, str),
        Param('brand_name_english', JSON, str),
        Param('manager_information', JSON, list)
    )
    def put_my_page(*args):
        """ 셀러 정보 관리 (셀러 )

        셀러의 정보를 Body 로 받아서 데이터에 업데이트하기

        Args:
            *args:
                image                       : 셀러 프로필 이미지
                simple_introduce            : 셀러 한줄 소개
                brand_crm_number            : 브랜드 고객센터 번호
                zip_code                    : 우편번호
                address                     : 주소
                detail_address              : 상세 주소
                brand_crm_open              : 고객센터 오픈시간
                brand_crm_end               : 고객센터 마감시간
                delivery_information        : 배송 정보
                refund_exchange_information : 교환/환불 정보
                seller_status_id            : 셀러 상태 id ( 입점, 입점대기 등 )
                background_image            : 셀러페이지 배경이미지
                detail_introduce            : 셀러 상세 소개
                is_brand_crm_holiday        : 고객센터 주말 및 공휴일 운영 여부
                brand_name_korean           : 브랜드명 ( 한글 )
                brand_name_english          : 브랜드명 ( 영어 )
                manager_information         : 담당자 정보 ( 이름, 핸드폰 번호, 이메일 )

        Returns:
            200 : success, 셀러 데이터 업데이트 성공 시
            400 : 담당자 정보 안에 이름, 핸드폰 번호, 이메일 중 하나라도 없을 때,
                담당자 이메일 형식 맞지 않을 때, 담당자 핸드폰 번호 형식 맞지 않을 때
            500 : Exception

        """
        # 입력한 셀러 정보 받아서 데이터베이스에 넣기
        session = None
        try:
            session = get_session()
            seller = {
                'image':                        args[0],
                'simple_introduce':             args[1],
                'brand_crm_number':             args[2],
                'zip_code':                     args[3],
                'address':                      args[4],
                'detail_address':               args[5],
                'brand_crm_open':               args[6],
                'brand_crm_end':                args[7],
                'delivery_information':         args[8],
                'refund_exchange_information':  args[9],
                'seller_status_id':             args[10],
                'background_image':             args[11],
                'detail_introduce':             args[12],
                'is_brand_crm_holiday':         args[13],
                'brand_name_korean':            args[14],
                'brand_name_english':           args[15],
                'manager_information':          args[16],
                'id':                           g.seller_id
            }

            for manager in seller['manager_information']:
                # 담당자 정보 안에 이름, 번호, 이메일이 없으면 에러 발생
                if manager['name'] is None or manager['phone_number'] is None or manager['email'] is None:
                    return jsonify({'message': 'no manager information data'}), 400

                # 이메일 형식이 맞지 않을 때 에러 발생
                if re.match(r'^([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)\.([0-9a-zA-Z_-]+)$', manager['email']) is None:
                    return jsonify({'message': 'invalid email'}), 400

                # 핸드폰 번호 형식이 맞지 않을 때 에러 발생
                if re.match(r'^010-[0-9]{3,4}-[0-9]{4}$', manager['phone_number']) is None:
                    return jsonify({'message': 'invalid phone number'}), 400

            seller_service.post_my_page(seller, session)

            session.commit()
            return jsonify({'message': 'success'}), 200

        except NoAffectedRowException as e:
            session.rollback()
            return jsonify({'message': 'no affected row error {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/master/management-seller", methods=['GET'])
    @login_required
    @validate_params(
        Param('limit', GET, int, required=False),
        Param('offset', GET, int, required=False),
        Param('number', GET, int, required=False),
        Param('account', GET, str, required=False),
        Param('brand_name_korean', GET, str, required=False),
        Param('brand_name_english', GET, str, required=False),
        Param('manager_name', GET, str, required=False),
        Param('manager_number', GET, str, rules=[Pattern(r'^010-[0-9]{3,4}-[0-9]{4}$')], required=False),
        Param('manager_email', GET, str, rules=[Pattern(r'^([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)\.([0-9a-zA-Z_-]+)$')],
              required=False),
        Param('status_id', GET, int, required=False),
        Param('property_id', GET, int, required=False),
        Param('start_date', GET, str, required=False),
        Param('end_date', GET, str, required=False)
    )
    def get_management_seller(*args):
        """ 셀러 계정 관리 ( 마스터 ) API

        쿼리 파라미터로 필터링 될 값을 받아서 필터링 한 후 리스트를 보내줌

        Args:
            *args:
                limit              : pagination 범위
                offset             : pagination 시작 번호
                number             : 셀러의 id
                account            : 셀러의 계정
                brand_name_korean  : 브랜드명 ( 한글 )
                brand_name_english : 브랜드명 ( 영어 )
                manager_name       : 담당자명
                manager_number     : 담당자 핸드폰 번호
                manager_email      : 담당자 이메일
                status_id          : 셀러의 상태 id ( 입점, 입점대기 등 )
                property_id        : 셀러의 속성 id ( 로드샵, 마켓 등 )
                start_date         : 해당 날짜 이후로 등록한 셀러 검색
                end_date           : 해당 날짜 이전에 등록한 셀러 검색

        Returns:
            200 : seller_list ( type : dict )
            400 : 마스터 계정이 아닌 경우
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            # 쿼리스트링을 딕셔너리로 만들어 줌
            query_string_list = {
                'limit':                10 if args[0] is None else args[0],
                'offset':               0 if args[1] is None else args[1],
                'number':               args[2],
                'account':              args[3],
                'brand_name_korean':    args[4],
                'brand_name_english':   args[5],
                'manager_name':         args[6],
                'manager_number':       args[7],
                'email':                args[8],
                'seller_status_id':     args[9],
                'seller_property_id':   args[10],
                'start_date':           args[11],
                'end_date':             args[12]
            }

            seller_list = seller_service.get_seller_list(query_string_list, g.seller_id, session)

            # 마스터 계정이 아닐 때 에러 발생
            if seller_list == 'not authorized':
                return jsonify({'message': 'no master'}), 400

            return jsonify(seller_list)

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/master/management-seller", methods=['PUT'])
    @login_required
    @validate_params(
        Param('seller_id', JSON, int),
        Param('button', JSON, int)
    )
    def put_management_seller(seller_id, button):
        """ 마스터 셀러계정관리 API

        마스터가 버튼을 눌러 셀러의 상태를 변경함
        셀러의 상태가 변경될 때마다 슬랙 채널로 "(seller_id)번 셀러의 상태가 (status)로 변경되었습니다" 라는 메세지 전송

        Args:
            seller_id: 셀러의 id
            button: 셀러의 상태 변경 버튼 ( 입점으로 변경, 휴점으로 변경 등 )

        Returns:
            200 : success, 셀러의 상태가 정상적으로 변경되었을 때
            400 : 눌린 버튼과 셀러의 상태가 맞지 않을 때, 슬랙봇 메세지가 전송 실패되었을 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            status = seller_service.post_seller_status(seller_id, button, session)

            # 버튼과 버튼이 눌리는 셀러의 속성이 맞지 않을 때
            if status == 'invalid request':
                return jsonify({'message': 'invalid request'}), 400

            # 슬랙봇 메세지가 전송실패되었을 때
            if status == 'message fail':
                return jsonify({'message': 'message failed'}), 400

            session.commit()
            return jsonify({'message': 'success'}), 200

        except NoAffectedRowException as e:
            session.rollback()
            return jsonify({'message': 'no affected row error {}'.format(e.message)}), e.status_code

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/home", methods=['GET'])
    @login_required
    def get_home_seller():
        """ 홈 API

        로그인했을 때 나오는 홈 화면의 데이터 보내주기

        Returns:
            200 : data ( type : dict )
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            data = seller_service.get_home_data(session)

            return jsonify(data), 200

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/master/management-seller/<int:seller_id>", methods=['GET'])
    @login_required
    @validate_params(
        Param('seller_id', PATH, int)
    )
    def get_master_seller_page(seller_id):
        """ 셀러계정관리 ( 마스터 )

        마스터가 셀러의 데이터 가져오기

        Args:
            seller_id: 셀러 id

        Returns:
            200 : seller_data ( type : dict )
            400 : 마스터 계정이 아닐 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()
            seller_data = seller_service.get_seller_page(seller_id, session)

            # 마스터 계정이 아닐 때 에러 발생
            if seller_data == 'not authorized':
                return jsonify({'message': 'not authorized'}), 400

            return jsonify(seller_data)

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/master/management-seller/<int:seller_id>", methods=['PUT'])
    @login_required
    @validate_params(
        Param('seller_id', PATH, int),
        Param('image', JSON, str),
        Param('simple_introduce', JSON, str),
        Param('brand_crm_number', JSON, str, rules=[Pattern(r'^[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}$')]),
        Param('zip_code', JSON, int),
        Param('address', JSON, str),
        Param('detail_address', JSON, str),
        Param('brand_crm_open', JSON, str),
        Param('brand_crm_end', JSON, str),
        Param('delivery_information', JSON, str),
        Param('refund_exchange_information', JSON, str),
        Param('seller_status_id', JSON, int),
        Param('background_image', JSON, str, required=False),
        Param('detail_introduce', JSON, str, required=False),
        Param('is_brand_crm_holiday', JSON, int, rules=[Enum(0, 1)]),
        Param('brand_name_korean', JSON, str),
        Param('brand_name_english', JSON, str),
        Param('manager_information', JSON, list),
        Param('seller_property_id', JSON, int)
    )
    def put_master_seller_page(*args):
        """ 셀러 계정 관리 ( 마스터 )

        Path parameter 로 셀러의 아이디를 받고 Body 로 셀러의 수정 데이터 받아서 수정하기

        Args:
            *args:
                seller_id : 셀러 id
                image : 셀러의 프로필 이미지
                simple_introduce : 셀러 한줄 소개
                brand_crm_number : 고객센터 전화번호
                zip_code : 우편번호
                address : 주소
                detail_address : 상세주소
                brand_crm_open : 고객센터 오픈시간
                brand_crm_end : 고객센터 마감시간
                delivery_information : 배송 정보
                refund_exchange_information : 교환/환불 정보
                seller_status_id : 셀러 상태 id ( 입점, 입점 대기 등 )
                background_image : 셀러 배경 이미지
                detail_introduce : 셀러 상세 정보
                is_brand_crm_holiday : 고객센터 휴일/공휴일 영업 여부
                brand_name_korean : 브랜드명 ( 한글 )
                brand_name_english : 브랜드명 ( 영어 )
                manager_information : 담당자 정보 ( 이름, 핸드폰 번호, 이메일 )
                seller_property_id : 셀러 속성 id ( 마켓, 로드샵 등 )

        Returns:
            200 : success, 데이터 수정하기 성공했을 때
            400 : 담당자 정보에 이름, 핸드폰 번호, 이메일 중 하나라도 없을 때 ,
                  이메일 형식이 맞지 않을 때, 핸드폰번호 형식이 맞지 않을 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            # 셀러 데이터 받아서 딕셔너리로 만들기
            seller = {
                'id':                           args[0],
                'image':                        args[1],
                'simple_introduce':             args[2],
                'brand_crm_number':             args[3],
                'zip_code':                     args[4],
                'address':                      args[5],
                'detail_address':               args[6],
                'brand_crm_open':               args[7],
                'brand_crm_end':                args[8],
                'delivery_information':         args[9],
                'refund_exchange_information':  args[10],
                'seller_status_id':             args[11],
                'background_image':             args[12],
                'detail_introduce':             args[13],
                'is_brand_crm_holiday':         args[14],
                'brand_name_korean':            args[15],
                'brand_name_english':           args[16],
                'manager_information':          args[17],
                'seller_property_id':           args[18]
            }

            # 담당자 정보 안에 이름, 번호, 이메일이 없으면 에러 발생
            for manager in seller['manager_information']:
                if manager['name'] is None or manager['phone_number'] is None or manager['email'] is None:
                    return jsonify({'message': 'no manager information data'}), 400

                # 이메일 형식이 맞지 않을 때 에러 발생
                if re.match(r'^([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)\.([0-9a-zA-Z_-]+)$', manager['email']) is None:
                    return jsonify({'message': 'invalid email'}), 400

                # 핸드폰 번호 형식이 맞지 않을 때 에러 발생
                if re.match(r'^010-[0-9]{3,4}-[0-9]{4}$', manager['phone_number']) is None:
                    return jsonify({'message': 'invalid phone number'}), 400

            seller_service.put_master_seller_page(seller, session)

            session.commit()
            return jsonify({'message': 'success'}), 200

        except NoAffectedRowException as e:
            session.rollback()
            return jsonify({'message': 'no affected row error {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()
app = Flask(__name__)
CORS(app)
mongo = DBManager()
auth = Authentication()
token = None


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/api/login', methods=['POST'])
@validate_params(
    Param('user_id', JSON, str, rules=[Pattern(r'^[a-z0-9]+$')],
          required=True),  # 소문자와 숫자만 가능
    Param('user_pwd', JSON, str, required=True))
def login(*request_elements):
    user_id = request_elements[0]
    user_pwd = request_elements[1]
    user_info = mongo.get_user_info(user_id)
    if user_info is not None:
        if user_pwd == user_info['user_pwd']:
            auth.token_recreation(user_id)
            json_request = {
                'login': '******',
                'user_id': user_id,
                'level': user_info['level'],
                'token': auth.token_get(user_id)
            }
def order_endpoints(app, services, get_session):
    order_service = services.order_service

    @app.route("/order/product/<int:product_id>", methods=['GET'])
    @login_required
    @validate_params(Param('product_id', PATH, int))
    def get_order_product(product_id):
        """ 상품 구매하기 API

        상품 구매하기 클릭할 때 상품 id를 path parameter 로 받아 모달창에 상품 정보 가져오기

        Args:
            product_id: 상품 id

        Returns:
            200 : product_data ( type : dict )
            500 : Exception

        """
        session = None
        try:
            session = get_session()
            product_data = order_service.get_product_data(product_id, session)

            return jsonify(product_data), 200

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data error {}'.format(e.message)
                            }), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/order/product/<int:product_id>", methods=['POST'])
    @login_required
    @validate_params(Param('product_id', PATH, int),
                     Param('user_name', JSON, str),
                     Param('phone_number',
                           JSON,
                           str,
                           rules=[Pattern(r'^010-[0-9]{3,4}-[0-9]{4}$')]),
                     Param('zip_code', JSON, int), Param('address', JSON, str),
                     Param('detail_address', JSON, str),
                     Param('count', JSON, int), Param('color_id', JSON, int),
                     Param('size_id', JSON, int),
                     Param('total_price', JSON, int))
    def post_order_product(*args):
        """ 상품 주문하기 API

        Body 로 주문한 상품 데이터를 받아 데이터베이스에 저장하기

        Args:
            *args:
                product_id     : 상품 id
                user_name      : 주문자명
                phone_number   : 주문자의 핸드폰 번호
                zip_code       : 주문자 우편번호
                address        : 주문자 주소
                detail_address : 주문자 상세주소
                count          : 주문 수량
                color_id       : 컬러 id
                size_id        : 사이즈 id
                total_price    : 총 결제금액

        Returns:
            200 : success, 주문 데이터를 데이터베이스 저장에 성공했을 때
            400 : 재고수량에 맞지 않는 수량을 주문했을 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            # validation params 을 통해 들어온 데이터를 딕셔너리로 만들기
            order_data = {
                'product_id': args[0],
                'user_name': args[1],
                'phone_number': args[2],
                'zip_code': args[3],
                'address': args[4],
                'detail_address': args[5],
                'count': args[6],
                'color_id': args[7],
                'size_id': args[8],
                'total_price': args[9]
            }

            order_data = order_service.order_product(order_data, g.seller_id,
                                                     session)

            # 재고 수량에 맞지 않는 수량을 주문했을 때 에러 발생
            if order_data == 'invalid count':
                return jsonify({'message': "invalid count"}), 400

            session.commit()
            return jsonify({'message': 'success'}), 200

        except NoAffectedRowException as e:
            session.rollback()
            return jsonify(
                {'message':
                 'no affected row error {}'.format(e.message)}), e.status_code

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data error {}'.format(e.message)
                            }), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/order/status/<int:order_status_id>", methods=['GET'])
    @login_required
    @validate_params(Param('order_status_id', PATH, int),
                     Param('limit', GET, int, required=False),
                     Param('offset', GET, int, required=False),
                     Param('start_date', GET, str, required=False),
                     Param('end_date', GET, str, required=False),
                     Param('order_number', GET, int, required=False),
                     Param('detail_number', GET, int, required=False),
                     Param('user_name', GET, str, required=False),
                     Param('phone_number', GET, str, required=False),
                     Param('product_name', GET, str, required=False),
                     Param('order_by', GET, int, required=False),
                     Param('brand_name_korean', GET, str, required=False))
    def order_prepare(*args):
        """ 주문리스트 API

        path 파라미터로 주문 상태 id ( 배송중, 결제완료 등 ) 를 받아서 쿼리 파라미터로 받은 필터링 조건에 맞게 주문 리스트를 가져오기

        Args:
            *args:
                order_status_id : 주문 상태 id ( 결제완료, 배송중 등 )
                limit           : pagination 범위
                offset          : pagination 시작 번호
                start_date      : 해당날짜 이후의 주문 상품 리스트
                end_date        : 해당날짜 이전의 주문 상품 리스트
                order_number    : 주문 번호
                detail_number   : 주문 상세 번호
                user_name       : 주문자명
                phone_number    : 주문자 핸드폰 번호
                product_name    : 주문한 상품명
                order_by        : 정렬 순서
                brand_name_korean : 브랜드명(한글)

        Returns:
            200 : order_list ( type : dict )
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            # 쿼리스트링으로 리스트 만들기
            query_string_list = {
                'order_status_id': args[0],
                'limit': 50 if args[1] is None else args[1],
                'offset': 0 if args[2] is None else args[2],
                'start_date': args[3],
                'end_date': args[4],
                'order_number': args[5],
                'detail_number': args[6],
                'user_name': args[7],
                'phone_number': args[8],
                'product_name': args[9],
                'order_by': 2 if args[10] is None else args[10],
                'brand_name_korean': args[11]
            }

            order_list = order_service.get_order_product_list(
                query_string_list, session)

            return jsonify(order_list)

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/order/shipment", methods=['POST'])
    @login_required
    @validate_params(Param('order_id_list', JSON, list),
                     Param('shipment_button', JSON, int))
    def change_shipment_status(*args):
        """ 배송 처리 버튼 API

        배송 처리, 배송완료 처리 버튼을 눌렀을 때 Body 로 주문상품 데이터와 버튼 데이터를 받아 배송상태 변경하기

        Args:
            *args:
                order_id_list   : 주문 상품들이 id 리스트
                shipment_button : 배송처리버튼

        Returns:
            200 : success, 주문 상품의 배송처리 상태가 정상적으로 성공했을 때
            400 : 버튼이 잘못 눌렸을 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()
            order_list = {'order_id_list': args[0], 'shipment_button': args[1]}

            # 1,2번 외 다른 버튼이 잘못 눌렸을 때 에러 발생
            if order_list['shipment_button'] != shipment_button['SHIPMENT'] \
                    and order_list['shipment_button'] != shipment_button['SHIPMENT_COMPLETE']:
                return jsonify({'message': 'invalid button error'}), 400

            order_status = order_service.change_order_status(
                order_list, session)

            if order_status is not None:
                return jsonify({'message': order_status}), 400

            session.commit()
            return jsonify({'message': 'success'}), 200

        except NoAffectedRowException as e:
            session.rollback()
            return jsonify(
                {'message':
                 'no affected row error {}'.format(e.message)}), e.status_code

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data error {}'.format(e.message)
                            }), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/order/<int:order_id>", methods=['GET'])
    @login_required
    @validate_params(Param('order_id', PATH, int))
    def get_order_detail(order_id):
        """ 주문 상세페이지 API

        path parameter 로 주문 id 를 받아와서 주문 상세 데이터 가져오기

        Args:
            order_id: 주문 id

        Returns:
            200 : order_details ( type : dict )
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            order_details = order_service.get_details(order_id, session)

            return jsonify(order_details), 200

        except NoDataException as e:
            session.rollback()
            return jsonify({'message': 'no data error {}'.format(e.message)
                            }), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()

    @app.route("/order/change_number", methods=['PUT'])
    @login_required
    @validate_params(
        Param('phone_number',
              JSON,
              str,
              rules=[Pattern(r'^010-[0-9]{3,4}-[0-9]{4}$')]),
        Param('order_id', JSON, int))
    def change_phone_number(*args):
        """ 핸드폰 번호 변경 API

        주문 상세페이지에서 핸드폰 번호 변경할 때 변경 데이터 가져와서 핸드폰 번호 업데이트하기

        Args:
            *args:
                phone_number : 변경하려는 핸드폰 번호
                order_id     : 주문 id

        Returns:
            200 : success, 성공적으로 핸드폰 데이터를 변경하였을 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()

            data = {'phone_number': args[0], 'order_id': args[1]}

            result = order_service.change_number(data, g.seller_id, session)

            if result == 'not authorized':
                return jsonify({'message': result}), 400

            session.commit()
            return jsonify({'message': 'success'}), 200

        except NoAffectedRowException as e:
            session.rollback()
            return jsonify(
                {'message':
                 'no affected row error {}'.format(e.message)}), e.status_code

        except Exception as e:
            session.rollback()
            return jsonify({'message': '{}'.format(e)}), 500

        finally:
            if session:
                session.close()