class ProductManageSearchView(MethodView):
    """ Presentation Layer

        Attributes:
            service  : MainCategoryListService 클래스
            database : app.config['DB']에 담겨있는 정보(데이터베이스 관련 정보)

        Author: 심원두

        History:
            2020-12-31(심원두): 초기 작성
            2021-01-03(심원두): 상품 리스트 검색 기능 구현
    """
    def __init__(self, service, database):
        self.service = service
        self.database = database

    @signin_decorator()
    @validate_params(
        Param('lookup_start_date',
              GET,
              str,
              required=False,
              rules=[DateRule(), NotEmpty()]),
        Param('lookup_end_date',
              GET,
              str,
              required=False,
              rules=[DateRule(), NotEmpty()]),
        Param('seller_name',
              GET,
              str,
              required=False,
              rules=[DefaultRule(), NotEmpty(),
                     MaxLength(20)]),
        Param('product_name',
              GET,
              str,
              required=False,
              rules=[DefaultRule(), NotEmpty(),
                     MaxLength(100)]),
        Param('product_id',
              GET,
              str,
              required=False,
              rules=[NumberRule(), NotEmpty()]),
        Param('product_code',
              GET,
              str,
              required=False,
              rules=[DefaultRule(), NotEmpty(),
                     MaxLength(20)]),
        Param('is_sale', GET, int, required=False, rules=[Enum(1, 2)]),
        Param('is_display', GET, int, required=False, rules=[Enum(1, 2)]),
        Param('is_discount', GET, int, required=False, rules=[Enum(1, 2)]),
        Param('page_number', GET, int, required=True, rules=[PageRule()]),
        Param('limit', GET, int, required=True, rules=[Enum(10, 20, 50)]))
    def get(self, *args):
        """GET 메소드: 특정 조건에 해당하는 상품 리스트를 조회한다.
            
            Args:
                'lookup_start_date'       : 조회 시작 기간
                'lookup_end_date'         : 조회 종료 기간
                'seller_name'             : 셀러명
                'product_name'            : 상품명
                'product_id'              : 상품 아이디
                'product_code'            : 상품 코드
                'seller_attribute_type_id : 셀러 속성
                'is_sale'                 : 할인 여부
                'is_display'              : 진열 여부
                'is_discount'             : 할인 여부
                'page_number'             : 페이지 번호
                'limit'                   : 한 화면에 보여줄 상품의 갯수
                
            Author: 심원두
            
            Returns:
                return {"message": "success", "result": result}
            
            Raises:
                400, {'message': 'key error',
                      'errorMessage': 'key_error' + format(e)} : 잘못 입력된 키값
                      
                400, {'message': 'both date field required',
                      'errorMessage': 'both_date_field_required'}: 필수 값 유효성 체크 에러
                      
                400, {'message': 'start date is greater than end date',
                      'errorMessage': 'start_date_is_greater_than_end_date'}: 날짜 비교 유효성 체크 에러
                
                400, {'message': 'invalid seller attribute type',
                      'errorMessage': 'invalid_seller_attribute_type'}: 셀러 타입 유효성 체크 에러
            
            History:
                2020-12-31(심원두): 초기생성
                2021-01-03(심원두): 상품 리스트 검색 기능 구현, Login Decorator 구현 예정
        """

        try:
            search_condition = {
                'seller_id':
                g.account_id if g.permission_type_id == 2 else None,
                'lookup_start_date':
                request.args.get('lookup_start_date', None),
                'lookup_end_date':
                request.args.get('lookup_end_date', None),
                'seller_name':
                request.args.get('seller_name', None),
                'product_name':
                request.args.get('product_name', None),
                'product_id':
                request.args.get('product_id', None),
                'product_code':
                request.args.get('product_code', None),
                'seller_attribute_type_ids':
                json.loads(request.args.get('seller_attribute_type_id'))
                if request.args.get('seller_attribute_type_id') else None,
                'is_sale':
                request.args.get('is_sale', None),
                'is_display':
                request.args.get('is_display', None),
                'is_discount':
                request.args.get('is_discount', None),
                'page_number':
                request.args.get('page_number'),
                'limit':
                request.args.get('limit')
            }

            search_condition_back_to_front = {
                'lookup_start_date':
                search_condition['lookup_start_date'],
                'lookup_end_date':
                search_condition['lookup_end_date'],
                'seller_name':
                search_condition['seller_name'],
                'product_name':
                search_condition['product_name'],
                'product_id':
                search_condition['product_id'],
                'product_code':
                search_condition['product_code'],
                'seller_attribute_type':
                search_condition['seller_attribute_type_ids'],
                'is_sale':
                0 if search_condition['is_sale'] is None else int(
                    search_condition['is_sale']),
                'is_display':
                0 if search_condition['is_display'] is None else int(
                    search_condition['is_display']),
                'is_discount':
                0 if search_condition['is_discount'] is None else int(
                    search_condition['is_discount']),
                'page_number':
                int(search_condition['page_number']),
                'limit':
                int(search_condition['limit'])
            }

            connection = get_connection(self.database)
            result = self.service.search_product_service(
                connection, search_condition)
            result['search_condition'] = search_condition_back_to_front

            return jsonify({'message': 'success', 'result': result})

        except KeyError as e:
            traceback.print_exc()
            raise e

        except Exception as e:
            traceback.print_exc()
            raise e

        finally:
            try:
                if connection:
                    connection.close()
            except Exception:
                raise DatabaseCloseFail('database close fail')
예제 #2
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()
class TestJsonParam(unittest.TestCase):
    LIST_SCHEMA = JsonParam(
        {
            'person':
            JsonParam(
                {
                    'info':
                    JsonParam({
                        'contacts':
                        JsonParam({
                            'phones':
                            JsonParam([Enum('+375', '+49')], as_list=True),
                            'networks':
                            JsonParam(
                                {'name': [Enum('facebook', 'telegram')]},
                                as_list=True,
                            ),
                            'emails':
                            JsonParam([IsEmail()], as_list=True),
                            'addresses':
                            JsonParam({'street': []}, required=False),
                        }),
                    }),
                }, ),
        }, )

    DICT_SCHEMA = JsonParam(
        {
            'street':
            CompositeRule(Enum('Jakuba Kolasa')),
            'meta':
            JsonParam(
                {
                    'description':
                    JsonParam({
                        'color': [Enum('green', 'yellow', 'blue')],
                    }, ),
                    'buildings':
                    JsonParam({
                        'warehouses':
                        JsonParam({
                            'small':
                            JsonParam({
                                'count': CompositeRule(Min(0), Max(99)),
                            }),
                            'large': [Min(1), Max(10)]
                        }),
                    }),
                    'not_required':
                    JsonParam({'text': []}, required=False),
                }, ),
        }, )

    @parameterized.expand([
        # invalid
        (
            DICT_SCHEMA,
            {
                'street': 'Rampische',
                'meta': {
                    'buildings': {
                        'warehouses': {
                            'small': {
                                'count': 100,
                            },
                            'large': 0,
                        },
                    },
                },
            },
            [
                [
                    ['root', 'meta', 'buildings', 'warehouses', 'small'],
                    {
                        'count': [ValueMaxError]
                    },
                ],
                [
                    ['root', 'meta', 'buildings', 'warehouses'],
                    {
                        'large': [ValueMinError]
                    },
                ],
                [
                    ['root', 'meta'],
                    {
                        'description': RequiredJsonKeyError
                    },
                ],
                [
                    ['root'],
                    {
                        'street': [ValueEnumError],
                    },
                ],
            ],
        ),
        # valid
        (
            DICT_SCHEMA,
            {
                'country': 'Belarus',
                'city': 'Minsk',
                'street': 'Jakuba Kolasa',
                'meta': {
                    'buildings': {
                        'warehouses': {
                            'small': {
                                'count': 99,
                            },
                            'large': 1,
                        },
                    },
                    'description': {
                        'color': 'green',
                    },
                },
            },
            {},
        )
    ])
    def test_dict(self, param: JsonParam, data, exp):
        value, errors = param.validate(data)
        for ix, json_error in enumerate(errors):  # type: list, JsonError
            self.assertTrue(isinstance(json_error, JsonError))
            exp_depth, epx_errors_map = exp[ix]  # type: list, dict
            self.assertListEqual(json_error.depth, exp_depth)
            for key, error in json_error.errors.items():
                if isinstance(error, RulesError):
                    self.assertEqual(len(error.errors), len(epx_errors_map))
                    for ix_rule, rule_err in enumerate(error.errors):
                        self.assertTrue(
                            isinstance(rule_err, epx_errors_map[key][0]))
                else:
                    self.assertTrue(isinstance(error, epx_errors_map[key]))

    @parameterized.expand([
        # invalid
        (
            LIST_SCHEMA,
            {
                'person': {
                    'info': {
                        'contacts': {
                            'phones': [
                                '+375', '+49', {
                                    'code': '+420'
                                }, {
                                    'code': '+10000'
                                }
                            ],
                            'emails': [{
                                'work': 'bad_type1'
                            }, {
                                'work': 'bad_type2'
                            }, 'bad_mail'],
                            'networks': [
                                {
                                    'name': 'facebook'
                                },
                                {
                                    'name': 'insta'
                                },
                                {
                                    'name': 'linkedin'
                                },
                            ],
                        },
                    },
                },
            },
            [
                [
                    ['root', 'person', 'info', 'contacts', 'phones'],
                    {
                        2: JsonListItemTypeError,
                        3: JsonListItemTypeError,
                    },
                ],
                [
                    ['root', 'person', 'info', 'contacts', 'networks'],
                    {
                        1: {
                            'name': [ValueEnumError],
                        },
                        2: {
                            'name': [ValueEnumError],
                        },
                    },
                ],
                [
                    ['root', 'person', 'info', 'contacts', 'emails'],
                    {
                        0: JsonListItemTypeError,
                        1: JsonListItemTypeError,
                        2: [ValueEmailError],
                    },
                ],
            ],
        ),
        # valid
        (
            LIST_SCHEMA,
            {
                'person': {
                    'info': {
                        'contacts': {
                            'phones': ['+375', '+49'],
                            'networks': [
                                {
                                    'name': 'facebook'
                                },
                                {
                                    'name': 'telegram'
                                },
                                {
                                    'name': 'telegram'
                                },
                                {
                                    'name': 'facebook'
                                },
                            ],
                            'emails': ['*****@*****.**'],
                        },
                    },
                },
            },
            [],
        ),
    ])
    def test_list(self, param: JsonParam, data, exp):
        value, errors = param.validate(data)
        self.assertEqual(len(exp), len(errors))
        for err_ix, json_er in enumerate(errors):  # type: int, JsonError
            exp_err = exp[err_ix]
            exp_rule_err = exp_err[1]
            self.assertListEqual(json_er.depth, exp_err[0])
            self.assertEqual(len(exp_err[1]), len(json_er.errors))
            for rules_ix, rules_err in exp_rule_err.items():
                json_rules = json_er.errors[
                    rules_ix]  # type: dict or list or RulesError
                if isinstance(exp_rule_err[rules_ix], list):
                    self.assertTrue(isinstance(json_rules, RulesError))
                    for k, rule_err in enumerate(json_rules.errors):
                        self.assertTrue(
                            isinstance(rule_err, exp_rule_err[rules_ix][k]))
                else:
                    if isinstance(rules_err, dict):
                        self.assertTrue(len(json_rules), len(rules_err))
                    else:
                        # RulesError
                        self.assertTrue(
                            isinstance(json_er.errors[rules_ix], rules_err))
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
예제 #5
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 CreateProductView(MethodView):
    """ Presentation Layer

        Attributes:
            service  : CreateProductService 클래스
            database : app.config['DB']에 담겨있는 정보(데이터베이스 관련 정보)

        Author: 심원두

        History:
            2020-12-29(심원두): 초기 생성. products insert, product_code updated, product_histories 생성 기능 작성
            2020-12-30(심원두): 각 Param rules 추가, stock insert 기능 작성.
            2020-01-03(심원두): 상품 등록 Param rules 추가
    """
    def __init__(self, service, database):
        self.service = service
        self.database = database

    @signin_decorator()
    @validate_params(
        Param('seller_name', GET, str, required=False, rules=[MaxLength(20)]),
        Param('main_category_id',
              GET,
              str,
              required=False,
              rules=[NumberRule()]))
    def get(self, *args):
        """POST 메소드: 상품 정보 등록 초기 화면

            Args:
                'seller_name'      : 사용자가 입력한 셀러명
                'main_category_id' : 사용자가 선택한 메인 카테고리 아이디

            Author: 심원두

            Returns:
                return {"message": "success", "result": [{}]}

            Raises:
                400, {'message': 'key error',
                      'errorMessage': 'key_error' + format(e)}: 잘못 입력된 키값

                500, {'message': 'fail to get sub category list',
                      'errorMessage': 'fail_to_get_sub_category_list'}: 색상 정보 취득 실패

                500, {'message': 'fail to get product origin types',
                      'errorMessage': 'fail_to_get_product_origin_types'} : 원산지 정보 취득 실패

                500, {'message': 'fail to get color list',
                      'errorMessage': 'fail_to_get_color_list'}: 색상 정보 취득 실패

                500, {'message': 'fail to get color list',
                      'errorMessage': 'fail_to_get_color_list'}: 색상 정보 취득 실패

            History:
                2020-12-30(심원두): 초기생성
                2021-01-06(심원두): 로그인 데코레이터 처리 추가. 관리자일 경우에만 셀러 검색 허용하도록 수정
        """

        try:
            data = {
                'seller_name': request.args.get('seller_name', None),
                'main_category_id': request.args.get('main_category_id', None)
            }

            connection = get_connection(self.database)

            if data['seller_name'] and g.permission_type_id == 1:
                sellers = self.service.search_seller_list_service(
                    connection, data)

                return jsonify({'message': 'success', 'result': sellers})

            if data['main_category_id']:
                sub_categories = self.service.get_sub_category_list_service(
                    connection, data)

                return jsonify({
                    'message': 'success',
                    'result': sub_categories
                })

            result = dict()

            result['product_origin_types'] = \
                self.service.get_product_origin_types_service(
                    connection
                )

            result['color_list'] = \
                self.service.get_color_list_service(
                    connection
                )

            result['size_list'] = \
                self.service.get_size_list_service(
                    connection
                )

            return jsonify({'message': 'success', 'result': result})

        except KeyError as e:
            traceback.print_exc()
            raise e

        except Exception as e:
            traceback.print_exc()
            raise e

        finally:
            try:
                if connection:
                    connection.close()
            except Exception:
                raise DatabaseCloseFail('database close fail')

    @signin_decorator()
    @validate_params(
        Param('seller_id', FORM, str, required=True, rules=[NumberRule()]),
        Param('is_sale', FORM, int, required=True, rules=[Enum(0, 1)]),
        Param('is_display', FORM, int, required=True, rules=[Enum(0, 1)]),
        Param('main_category_id',
              FORM,
              str,
              required=True,
              rules=[NumberRule()]),
        Param('sub_category_id',
              FORM,
              str,
              required=True,
              rules=[NumberRule()]),
        Param('is_product_notice',
              FORM,
              int,
              required=True,
              rules=[Enum(0, 1)]),
        Param('manufacturer', FORM, str, required=False,
              rules=[MaxLength(30)]),
        Param('manufacturing_date', FORM, str, required=False),
        Param('product_origin_type_id', FORM, str, required=False),
        Param('product_name',
              FORM,
              str,
              required=True,
              rules=[NotEmpty(), MaxLength(100)]),
        Param('description', FORM, str, required=False,
              rules=[MaxLength(200)]),
        Param('detail_information',
              FORM,
              str,
              required=True,
              rules=[NotEmpty()]), Param('options', FORM, list, required=True),
        Param('minimum_quantity',
              FORM,
              str,
              required=False,
              rules=[NumberRule()]),
        Param('maximum_quantity',
              FORM,
              str,
              required=False,
              rules=[NumberRule()]),
        Param('origin_price', FORM, str, required=True, rules=[NumberRule()]),
        Param('discount_rate', FORM, str, required=True, rules=[NumberRule()]),
        Param('discounted_price',
              FORM,
              str,
              required=True,
              rules=[NumberRule()]),
        Param('discount_start_date', FORM, str, required=False),
        Param('discount_end_date', FORM, str, required=False))
    def post(self, *args):
        """ POST 메소드: 상품 정보 등록

            Args:
            - 사용자 입력 값(상품 이미지 최대 5개) : image_files
            - 사용자 입력 값(옵션 정보 리스트)    : options
            - 사용자 입력 값
            Form-Data: (
                'seller_id'
                'account_id',
                'is_sale',
                'is_display',
                'main_category_id',
                'sub_category_id',
                'is_product_notice',
                'manufacturer',
                'manufacturing_date',
                'product_origin_type_id',
                'product_name',
                'description',
                'detail_information',
                'options',
                'minimum_quantity',
                'maximum_quantity',
                'origin_price',
                'discount_rate',
                'discounted_price',
                'discount_start_date',
                'discount_end_date',
            )

            Author: 심원두

            Returns:
                200, {'message': 'success'}                                                   : 상품 정보 등록 성공

            Raises:
                400, {'message': 'key_error',
                      'errorMessage': 'key_error_' + format(e)}                               : 잘못 입력된 키값

                400, {'message': 'required field is blank',
                      'errorMessage': 'required_manufacture_information'}                     : 제조 정보 필드 없음

                400, {'message': 'required field is blank',
                      'errorMessage': 'required_discount_start_or_end_date'}                  : 필수 입력 항목 없음

                400, {'message': 'compare quantity field check error',
                      'errorMessage': 'minimum_quantity_cannot_greater_than_maximum_quantity'}: 최소 구매 수량이 최대 보다 큼

                400, {'message': 'compare price field check error',
                      'errorMessage': 'discounted_price_cannot_greater_than_origin_price'}    : 할인가가 판매가 보다 큼

                400, {'message': 'compare price field check error',
                      'errorMessage': 'wrong_discounted_price'}                               : 판매가와 할인가 일치하지 않음

                400, {'message': 'compare price field check error',
                      'errorMessage': 'required_discount_start_or_end_date'}                  : 할인 시작, 종료 일자 필드 없음

                400, {'message': 'start date is greater than end date',
                      'errorMessage': 'start_date_cannot_greater_than_end_date'}              : 할인 시작일이 종료일 보다 큼

                400, {'message': 'compare price field check error',
                      'errorMessage': 'discounted_price_have_to_same_with_origin_price'}      : 할인가, 판매가 불일치(할인율 0)

                413, {'message': 'invalid file',
                      'errorMessage': 'invalid_file'}                                         : 파일 이름이 공백, 혹은 파일을 정상적으로 받지 못함

                413, {'message': 'file size too large',
                      'errorMessage': 'file_size_too_large'}                                  : 파일 사이즈 정책 위반 (4메가 이상인 경우)

                413, {'message': 'file scale too small, 640 * 720 at least',
                      'errorMessage': 'file_scale_at_least_640*720'}                          : 파일 스케일 정책 위반 (680*720 미만인 경우)

                413, {'message': 'only allowed jpg type',
                      'errorMessage': 'only_allowed_jpg_type'}                                : 파일 확장자 정책 위반 (JPG, JPEG 아닌 경우)

                500, {'message': 'image_file_upload_to_amazon_fail',
                      'errorMessage': 'image_file_upload_fail'}                               : 이미지 업로드 실패

                500, {'message': 'product create denied',
                      'errorMessage': 'unable_to_create_product'}                             : 상품 정보 등록 실패

                500, {'message': 'product code update denied',
                      'errorMessage': 'unable_to_update_product_code'}                        : 상품 코드 갱신 실패

                500, {'message': 'product code update denied',
                      'errorMessage': 'unable_to_update_product_code'}                        : 상품 코드 갱신 실패

                500, {'message': 'product image create denied',
                      'errorMessage': 'unable_to_create_product_image'}                       : 상품 이미지 등록 실패

                500, {'message': 'stock create denied',
                      'errorMessage': 'unable_to_create_stocks'}                              : 상품 옵션 정보 등록 실패

                500, {'message': 'product history create denied',
                      'errorMessage': 'unable_to_create_product_history'}                     : 상품 이력 등록 실패

                500, {'message': 'bookmark volumes create denied',
                      'errorMessage': 'unable_to_create_bookmark_volumes'}                    : 북마크 초기 등록 실패

                500, {'message': 'database_connection_fail',
                      'errorMessage': 'database_close_fail'}                                  : 커넥션 종료 실패

                500, {'message': 'database_error',
                      'errorMessage': 'database_error_' + format(e)}                          : 데이터베이스 에러

                500, {'message': 'internal_server_error',
                      'errorMessage': format(e)})                                             : 서버 에러

            History:
                2020-12-29(심원두): 초기 생성
                2021-01-03(심원두): 파라미터 유효성 검사 추가 Enum(), NotEmpty()
                2021-01-05(심원두): -이미지 저장 처리 순서를 3번째에서 가장 마지막으로 내림. 테이블 인서트 처리에 문제가 있을 경우,
                                    S3에 올라간 이미지는 롤백을 할 수 없는 이슈 반영.
                                   -북마크 테이블 초기 등록 처리 추가.
        """

        try:
            data = {
                'seller_id':
                request.form.get('seller_id'),
                'account_id':
                g.account_id,
                'is_sale':
                request.form.get('is_sale'),
                'is_display':
                request.form.get('is_display'),
                'main_category_id':
                request.form.get('main_category_id'),
                'sub_category_id':
                request.form.get('sub_category_id'),
                'is_product_notice':
                request.form.get('is_product_notice'),
                'manufacturer':
                request.form.get('manufacturer'),
                'manufacturing_date':
                request.form.get('manufacturing_date'),
                'product_origin_type_id':
                request.form.get('product_origin_type_id'),
                'product_name':
                request.form.get('product_name'),
                'description':
                request.form.get('description'),
                'detail_information':
                request.form.get('detail_information'),
                'minimum_quantity':
                request.form.get('minimum_quantity'),
                'maximum_quantity':
                request.form.get('maximum_quantity'),
                'origin_price':
                request.form.get('origin_price'),
                'discount_rate':
                request.form.get('discount_rate'),
                'discounted_price':
                request.form.get('discounted_price'),
                'discount_start_date':
                request.form.get('discount_start_date'),
                'discount_end_date':
                request.form.get('discount_end_date')
            }

            product_images = request.files.getlist("image_files")
            stocks = json.loads(request.form.get('options'))
            connection = get_connection(self.database)

            product_id = self.service.create_product_service(connection, data)

            product_code = self.service.update_product_code_service(
                connection, product_id)

            self.service.create_stock_service(connection, product_id, stocks)

            self.service.create_product_history_service(
                connection, product_id, data)

            self.service.create_product_sales_volumes_service(
                connection, product_id)

            self.service.create_bookmark_volumes_service(
                connection, product_id)

            self.service.create_product_images_service(connection,
                                                       data['seller_id'],
                                                       product_id,
                                                       product_code,
                                                       product_images)

            connection.commit()

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

        except KeyError as e:
            traceback.print_exc()
            connection.rollback()
            raise e

        except Exception as e:
            traceback.print_exc()
            connection.rollback()
            raise e

        finally:
            try:
                if connection:
                    connection.close()
            except Exception:
                traceback.print_exc()
                raise DatabaseCloseFail('database close fail')
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()
예제 #8
0
def create_product_endpoints(product_service, Session):

    product_app = Blueprint("product_app", __name__, url_prefix="/api/products")

    @product_app.route("/category", methods=["GET"])
    def product_category():
        session = Session()
        try:
            # 메뉴 데이터
            categories = product_service.get_menu(None, session)

            # 각 카테고리를 저장하기 위한 리스트
            second_category, first_category, main_category = [], [], []

            # JOIN 을 하며 생기는 중복을 제거하기 위해서 중복 체크 후 리스트에 저장
            for category in categories:

                # 1. (메인 카테고리의 id, 이름) 이 main_category 에 없을 경우 append
                if (category.m_id, category.main_category_name) not in main_category:
                    main_category.append((category.m_id, category.main_category_name))

                # 2. (1차 카테고리의 id, 이름, 이에 해당하는 메인 카테고리의 id) 가 first_category 에 없을 경우 append
                if (
                    category.f_id,
                    category.first_category_name,
                    category.main_category_id,
                ) not in first_category:
                    first_category.append(
                        (category.f_id, category.first_category_name, category.main_category_id)
                    )

                # 3. (2차 카테고리의 id, 이름, 이에 해당하는 1차 카테고리의 id) 가 second_category 에 없을 경우 append
                second_category.append(
                    (category.s_id, category.second_category_name, category.first_category_id)
                )

            # 카테고리의 계층 구조를 전달하기 위한 JSON 형식
            body = [
                {
                    # 메인 카테고리의 id 와 이름
                    "id": m_menu[0],
                    m_menu[1]: [
                        {
                            # 1차 카테고리의 id 와 이름
                            "id": f_menu[0],
                            f_menu[1]: [
                                {
                                    # 2차 카테고리의 id 와 이름
                                    "id": s_menu[0],
                                    "name": s_menu[1],
                                }
                                for s_menu in second_category
                                if s_menu[2] == f_menu[0]
                            ],
                        }
                        for f_menu in first_category
                        if f_menu[2] == m_menu[0]
                    ],
                }
                for m_menu in main_category
            ]

            return jsonify(body), 200

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

        finally:
            session.close()

    @product_app.route("", methods=["GET"])
    @validate_params(
        Param("limit", GET, int, default=100, required=False),
        Param("offset", GET, int, required=False),
        Param("main_category_id", GET, int, rules=[Enum(4, 5, 6)], required=False),
        Param("first_category_id", GET, int, required=False),
        Param("second_category_id", GET, int, required=False),
        Param("is_promotion", GET, int, rules=[Enum(0, 1)], required=False),
        Param("select", GET, int, rules=[Enum(0, 1)], required=False),
        Param("q", GET, str, required=False),
        Param("all_items", GET, int, rules=[Enum(1)], required=False),
    )
    def products(*args):
        session = Session()
        try:
            # args[2]: 메인 카테고리의 pk, args[8]: 전체 상품을 보여줄 지 판단하는 파라미터, args[3]: 1차 카테고리의 pk, args[4]: 2차 카테고리의 pk
            if args[2] == 5 or args[2] == 6 and not args[8] and not args[3] and not args[4]:

                # 특정 메인 카테고리 아이디 (5: 브랜드, 6: 뷰티) 파라미터만 들어올 경우 베스트 상품, 추천 상품 데이터 등을 전달
                body = {
                    "best_items": [],
                    "brand_items": [],
                    "recommended_items": [],
                    "category_items": [],
                }

                # 1. 파라미터로 들어온 카테고리의 id (args[2]) 에 따라 특정 셀러를 지정하고 상품 5개만 가져오기 위해 선언,
                # 2. 특정 1차 카테고리 아이디로 필터링된 상품 리스트를 가져오기 위해 선언
                if args[2] == 5:
                    f_cat_list = (12, 13, 14, 15, 16, 17, 18)
                    seller_id = 30

                else:
                    f_cat_list = (23, 24, 25, 26, 27, 28)
                    seller_id = 359

                for f_cat_id in f_cat_list:
                    # 1. 첫 번째 카테고리 상품 5개 씩 보여주기 위한 필터
                    f_category_filter = {"first_category_id": f_cat_id, "limit": 5}

                    # 2. 카테고리의 id, name 과 함께 상품 리스트를 반환한다.
                    category_products = {
                        "category_id": f_cat_id,
                        "category_name": product_service.get_menu(f_cat_id, session)[
                            0
                        ].first_category_name,
                        "product": product_service.get_products(f_category_filter, session),
                    }

                    body["category_items"].append(category_products)

                # Best 상품 필터 - 해당하는 메인 카테고리의 상품 중 판매량 순 10개만 가져오기 위해 선언
                best_prod_filter = {
                    "main_category_id": args[2],
                    "limit": 10,
                }
                best_products = product_service.get_products(best_prod_filter, session)

                # 추천 상품 필터 - 할인율 기준
                recommended_prod_filter = {
                    "main_category_id": args[2],
                    "limit": 30,
                    "discount_rate": 1,
                }
                recommended_products = product_service.get_products(
                    recommended_prod_filter, session
                )

                # 특정 셀러 상품 리스트 필터
                seller_filter = {"main_category_id": args[2], "seller_id": seller_id}
                brand_products = product_service.get_products(seller_filter, session)

                body["best_items"] = best_products
                body["brand_items"] = brand_products
                body["recommended_items"] = recommended_products

                return body

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

            # pagination
            filter_dict["limit"] = args[0]
            filter_dict["offset"] = args[1]

            # 카테고리
            filter_dict["main_category_id"] = args[2]
            filter_dict["first_category_id"] = args[3]
            filter_dict["second_category_id"] = args[4]

            # 세일
            filter_dict["is_promotion"] = args[5]

            # 판매량순, 최신순
            filter_dict["select"] = args[6]

            # 검색 필터
            filter_dict["q"] = args[7]

            # 메인 카테고리의 모든 상품 필터
            filter_dict["all_items"] = args[8]

            body = dict()

            # 상품 데이터
            body["products"] = product_service.get_products(filter_dict, session)

            # 검색어가 들어올 경우 전달하는 셀러 정보
            if filter_dict["q"]:

                # 필터링된 셀러 리스트를 가져오기 위한 필터
                seller_info = dict()
                seller_info["name"] = filter_dict["q"]
                seller_info["limit"] = 100

                # 검색된 셀러 리스트 정의
                sellers = dict()

                # 검색어에 해당하는 셀러의 리스트
                seller_list = [
                    dict(seller) for seller in product_service.get_sellers(seller_info, session)
                ]

                # 셀러 검색 결과 개수
                sellers["count"] = len(seller_list)

                # 셀러 데이터
                sellers["seller_list"] = seller_list

                body["sellers"] = sellers

            return jsonify(body), 200

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

        finally:
            session.close()

    @product_app.route("/product/<int:product_id>", methods=["GET"])
    def product(product_id):
        session = Session()
        try:
            # 상품 데이터
            body = dict(product_service.get_product(product_id, session))

            return jsonify(body), 200

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

        finally:
            session.close()

    @product_app.route("/seller", methods=["GET"])
    @validate_params(
        Param("limit", GET, int, default=100, required=False),
        Param("offset", GET, int, required=False),
        Param("main_category_id", GET, int, rules=[Enum(4, 5, 6)], required=False),
        Param("select", GET, int, rules=[Enum(0, 1)], default=1, required=False),
    )
    def sellers(*args):
        session = Session()
        try:
            # 필터링을 위한 딕셔너리
            seller_dict = dict()
            seller_dict["limit"] = args[0]
            seller_dict["offset"] = args[1]
            seller_dict["main_category_id"] = args[2]
            seller_dict["select"] = args[3]

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

            return jsonify(body), 200

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

        finally:
            session.close()

    return product_app
def product_endpoints(app, services, get_session):
    product_service = services.product_service

    @app.route("/product/register", methods=['GET'])
    @login_required
    def get_register_product():
        """ 상품 등록 API

        상품 등록 페이지에 들어갔을 때 불러오는 데이터

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

        """
        session = None
        try:
            session = get_session()
            data_list = product_service.get_category_color_size(session)

            return jsonify(data_list), 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("/product/register", methods=['POST'])
    @login_required
    @validate_params(Param('sub_categories_id', JSON, int),
                     Param('name', JSON, str), Param('main_image', JSON, str),
                     Param('is_sell', JSON, int, rules=[Enum(0, 1)]),
                     Param('is_display', JSON, int, rules=[Enum(0, 1)]),
                     Param('is_discount', JSON, int, rules=[Enum(0, 1)]),
                     Param('price', JSON, int), Param('detail', JSON, str),
                     Param('maximum_sell_count', JSON, int),
                     Param('minimum_sell_count', JSON, int),
                     Param('options', JSON, list),
                     Param('discount_rate', JSON, int, required=False),
                     Param('discount_start_date', JSON, str, required=False),
                     Param('discount_end_date', JSON, str, required=False),
                     Param('simple_information', JSON, str, required=False),
                     Param('manufacturer', JSON, str, required=False),
                     Param('manufacture_date', JSON, str, required=False),
                     Param('origin', JSON, str, required=False),
                     Param('image_list', JSON, list, required=False))
    def post_register_product(*args):
        """ 상품 등록 API

        Body 로 상품 데이터를 받아 상품 등록하기

        Args:
            *args:
                sub_categories_id   : 2차 카테고리 id
                name                : 상품명
                main_image          : 상품 메인 이미지
                is_sell             : 판매 여부
                is_display          : 진열 여부
                is_discount         : 할인 여부
                price               : 상품 가격
                detail              : 상품 상세정보
                maximum_sell_count  : 최대 판매 수량
                minimum_sell_count  : 최소 판매 수량
                options             : 리스트. [{ 컬러 id, 사이즈 id, 재고관리여부, 재고수량 }]
                discount_rate       : 할인율
                discount_start_date : 할인 시작 날짜
                discount_end_date   : 할인 마지막 날짜
                simple_information  : 한줄 상품 설명
                manufacturer        : 제조사
                manufacture_date    : 제조일자
                origin              : 원산지
                image_list          : 서브이미지 리스트

        Returns:
            200 : success, 상품 등록에 성공했을 때
            400 : key error , 옵션리스트 안에 컬러아이디, 사이즈아이디, 재고관리여부 중 하나라도 없을 때
            500 : Exception

        """
        session = None
        try:
            session = get_session()
            # 바디에 담긴 부분 유효성 검사 후에 딕셔너리로 만들기
            product_data = {
                'sub_categories_id': args[0],
                'name': args[1],
                'main_image': args[2],
                'is_sell': args[3],
                'is_display': args[4],
                'is_discount': args[5],
                'price': args[6],
                'detail': args[7],
                'maximum_sell_count': args[8],
                'minimum_sell_count': args[9],
                'options': args[10],
                'discount_rate': args[11],
                'discount_start_date': args[12],
                'discount_end_date': args[13],
                'simple_information': args[14],
                'manufacturer': args[15],
                'manufacture_date': args[16],
                'origin': args[17],
                'image_list': args[18],
                'seller_id': g.seller_id
            }

            # 옵션 안의 리스트의 키값들이 None 일 때 에러 발생
            for options in product_data['options']:
                if options['color_id'] is None or options[
                        'size_id'] is None or options[
                            'is_inventory_manage'] is None:
                    return jsonify({'message': 'option data not exist'}), 400

            product_service.post_register_product(product_data, session)

            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("/category/<int:category_id>", methods=['GET'])
    @login_required
    def get_sub_category_list(category_id):
        """ 2차 카테고리 불러오기

        상품 등록 페이지에서 1차 카테고리를 선택했을 때 해당하는 2차 카테고리 리스트 불러오기

        Args:
            category_id: 1차 카테고리 id

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

        """
        session = None
        try:
            session = get_session()
            category_list = product_service.get_sub_categories(
                category_id, session)

            return jsonify(category_list), 200

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

        finally:
            if session:
                session.close()

    @app.route("/product/update/<int:product_id>", methods=['GET'])
    @login_required
    @validate_params(Param('product_id', PATH, int))
    def get_update_product(product_id):
        """ 상품 상세페이지 ( 수정 )

        상품 수정 페이지 들어갔을 때 등록되어 있는 상품 데이터 가져오기

        Args:
            product_id: 상품 id

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

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

            product_data = product_service.get_product(product_id, session)

            return jsonify(product_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("/product/update/<int:product_id>", methods=['PUT'])
    @login_required
    @validate_params(Param('product_id', PATH, int),
                     Param('sub_categories_id', JSON, int),
                     Param('name', JSON, str), Param('main_image', JSON, str),
                     Param('is_sell', JSON, int, rules=[Enum(0, 1)]),
                     Param('is_display', JSON, int, rules=[Enum(0, 1)]),
                     Param('is_discount', JSON, int, rules=[Enum(0, 1)]),
                     Param('price', JSON, int), Param('detail', JSON, str),
                     Param('maximum_sell_count', JSON, int),
                     Param('minimum_sell_count', JSON, int),
                     Param('options', JSON, list),
                     Param('discount_rate', JSON, int, required=False),
                     Param('discount_start_date', JSON, str, required=False),
                     Param('discount_end_date', JSON, str, required=False),
                     Param('simple_information', JSON, str, required=False),
                     Param('manufacturer', JSON, str, required=False),
                     Param('manufacture_date', JSON, str, required=False),
                     Param('origin', JSON, str, required=False),
                     Param('image_list', JSON, list, required=False))
    def put_update_product(*args):
        """ 상품 데이터 수정 API

        Path parameter 로 상품 id를 받고 Body 로 상품 데이터를 받아 데이터 업데이트하기

        Args:
            *args:
                product_id          : 상품 id
                sub_categories_id   : 2차 카테고리 id
                name                : 상품명
                main_image          : 상품 메인 이미지
                is_sell             : 판매 여부
                is_display          : 진열 여부
                is_discount         : 할인 여부
                price               : 상품 가격
                detail              : 상품 상세 정보
                maximum_sell_count  : 최대 판매 수량
                minimum_sell_count  : 최소 판매 수량
                options             : 옵션리스트 ( 컬러아이디, 사이즈아이디, 재고관리여부, 재고수량 )
                discount_rate       : 할인율
                discount_start_date : 할인 시작 날짜
                discount_end_date   : 할인 마지막 날짜
                simple_information  : 상품 한줄 정보
                manufacturer        : 제조사
                manufacture_date    : 제조 날짜
                origin              : 원산지
                image_list          : 서브 이미지 리스트

        Returns:
            200 : success, 상품 데이터 업데이트에 성공했을 때
            400 : 옵션리스트에 컬러아이디, 사이즈아이디, 재고관리여부가 하나라도 없을 때
            500 : Exception

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

            # 수정할 상품 데이터 딕셔너리로 만들기
            product_data = {
                'product_id': args[0],
                'sub_categories_id': args[1],
                'name': args[2],
                'main_image': args[3],
                'is_sell': args[4],
                'is_display': args[5],
                'is_discount': args[6],
                'price': args[7],
                'detail': args[8],
                'maximum_sell_count': args[9],
                'minimum_sell_count': args[10],
                'options': args[11],
                'discount_rate': args[12],
                'discount_start_date': args[13],
                'discount_end_date': args[14],
                'simple_information': args[15],
                'manufacturer': args[16],
                'manufacture_date': args[17],
                'origin': args[18],
                'image_list': args[19],
                'seller_id': g.seller_id
            }

            # 옵션 안의 키값들이 None 이면 에러 발생
            for options in product_data['options']:
                if options['color_id'] is None or options[
                        'size_id'] is None or options[
                            'is_inventory_manage'] is None:
                    return jsonify({'message': 'option data not exist'}), 400

            product_service.post_update_product(product_data, 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("/product/management", methods=['GET'])
    @login_required
    @validate_params(Param('limit', GET, int, required=False),
                     Param('offset', GET, int, required=False),
                     Param('is_sell',
                           GET,
                           int,
                           rules=[Enum(0, 1)],
                           required=False),
                     Param('is_discount',
                           GET,
                           int,
                           rules=[Enum(0, 1)],
                           required=False),
                     Param('is_display',
                           GET,
                           int,
                           rules=[Enum(0, 1)],
                           required=False),
                     Param('name', GET, str, required=False),
                     Param('code_number', GET, int, required=False),
                     Param('product_number', GET, str, required=False),
                     Param('start_date', GET, str, required=False),
                     Param('end_date', GET, str, required=False),
                     Param('seller_property_id', GET, int, required=False),
                     Param('brand_name_korean', GET, str, required=False))
    def management_product(*args):
        """ 상품 관리 리스트 API

        쿼리 파라미터로 필터링 조건들을 받아서 필터링 조건에 따라서 등록되어 있는 상품 리스트를 가져오기

        Args:
            *args:
                limit              : pagination 범위
                offset             : pagination 시작 번호
                is_sell            : 판매 여부
                is_discount        : 할인 여부
                is_display         : 진열 여부
                name               : 상품명
                code_number        : 상품 코드 번호
                product_number     : 상품 id
                start_date         : 해당날짜 이후에 등록된 상품
                end_date           : 해당날짜 이전에 등록된 상품
                seller_property_id : 셀러 속성 id ( 로드샵, 마켓 등 )
                brand_name_korean  : 브랜드명 ( 한글 )

        Returns:
            200 : product_list ( type : dict )
            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],
                'is_sell': args[2],
                'is_discount': args[3],
                'is_display': args[4],
                'name': args[5],
                'code_number': args[6],
                'product_number': args[7],
                'start_date': args[8],
                'end_date': args[9],
                'seller_property_id': args[10],
                'brand_name_korean': args[11],
                'seller_id': g.seller_id
            }

            product_list = product_service.get_product_list(
                query_string_list, session)

            return jsonify(product_list)

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

        finally:
            if session:
                session.close()
예제 #10
0
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
예제 #11
0
        return Response(json.dumps([
            [key, key.__class__.__name__],
            [sure, sure.__class__.__name__],
        ]),
                        mimetype='application/json')

    @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):
예제 #12
0
def create_qna_endpoints(qna_service, Session):

    qna_app = Blueprint('qna_app', __name__, url_prefix='/api/qnas')

    @qna_app.route('/qna', methods=['POST', 'DELETE'])
    @login_required
    def qna():
        """ question 작성 API

        사용자가 입력한 문의를 데이터베이스에 입력합니다.

        returns :
            200: question 데이터베이스 입력
            400: KEY_ERROR,
                 DELETE_FAILED
            500: Exception

        Authors:
            고지원

        History:
            2020-09-26 (고지원): 초기 생성
            2020-09-28 (고지원): 수정
                - delete 메소드 추가
                - 로그인 데코레이터 추가
            2020-10-05 (고지원): 삭제하려는 유저와 문의한 유저가 같은 유저인지 확인하는 코드 추가
        """
        session = Session()
        try:
            if request.method == 'POST':

                # 문의 입력을 위한 데이터를 받는다.
                qna_info = {
                    'type_id': request.json['type_id'],
                    'user_id': g.user_id['user_id'],
                    'product_id': request.json['product_id'],
                    'content': request.json['content'],
                    'is_private': request.json['is_private']
                }

                qna_service.insert_question(qna_info, session)

                session.commit()

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

            # 삭제하려는 유저와 문의한 유저가 일치하는지 id 를 통해 확인
            question_info = {
                'question_id': request.args.get('question_id'),
                'user_id': g.user_id['user_id']
            }

            row_count = qna_service.delete_question(question_info, session)

            # 유저 아이디가 매칭될 경우 row_count = 1, token의 유저 id 와 삭제하려는 유저의 id 가 다를 경우 0
            if row_count == 0:
                return jsonify({'message': 'DELETE_FAILED'}), 400

            session.commit()

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

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

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

        finally:
            session.close()

    @qna_app.route('', methods=['GET'])
    @validate_params(Param('limit', GET, int, default=100, required=False),
                     Param('offset', GET, int, required=False),
                     Param('product_id', GET, int, required=True))
    def qnas(*args):
        """ QnA 리스트 전달 API

        product_id 에 따른 QnA 리스트를 표출합니다.

        args:
            product_id: 상품의 pk

        returns :
            200: QnA 리스트
            500: Exception

        Authors:
            고지원

        History:
            2020-09-27 (고지원): 초기 생성
            2020-09-30 (고지원): 파라미터 유효성 검사 추가
            2020-10-05 (고지원): pagination 추가
            2020-10-12 (고지원): 삭제를 위해 로그인 한 유저의 아이디와 함께 반환하도록 수정
        """
        session = Session()
        try:
            qna_info = dict()

            # pagination
            qna_info['limit'] = args[0]
            qna_info['offset'] = args[1]

            # 상품 상세페이지 상품 아이디
            qna_info['product_id'] = args[2]

            # 로그인한 유저의 문의인지 판단하기 위해 토큰을 통해 id를 가져온다.
            access_token = request.headers.get('Authorization')
            if access_token:
                payload = jwt.decode(access_token, SECRET_KEY, ALGORITHM)
            else:
                payload = None

            body = {
                'login_user_id':
                payload,
                'qna':
                [dict(qna) for qna in qna_service.get_qnas(qna_info, session)]
            }

            return jsonify(body), 200

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

        finally:
            session.close()

    @qna_app.route('/user', methods=['GET'])
    @login_required
    @validate_params(
        Param('limit', GET, int, default=100, required=False),
        Param('offset', GET, int, required=False),
        Param('product_id', GET, int, required=False),
        Param('is_answered', GET, int, rules=[Enum(0, 1)], required=False),
    )
    def user_qnas(*args):
        """ 로그인한 user 의 QnA 리스트 전달 API

        user_id, product_id 에 따른 QnA 리스트를 표출합니다.

        args :
            *args:
            product_id: 상품의 pk
            is_answered: 답변, 미답변 여부 판단위한 파라미터

            g.user_id: 데코레이터에서 넘어온 user 의 pk

        returns :
            200: QnA 리스트
            500: Exception

        Authors:
            고지원

        History:
            2020-09-29 (고지원): 초기 생성
            2020-09-30 (고지원): 파라미터 유효성 검사 추가
            2020-10-06 (고지원): 로그인 데코레이터에서 id 가져와 해당 유저의 문의만 보여주도록 수정
        """

        session = Session()
        try:
            qna_info = dict()

            # pagination
            qna_info['limit'] = args[0]
            qna_info['offset'] = args[1]

            # 상품 아이디 (특정 상품의 상세페이지에서 내가 쓴 문의 필터링을 위해)
            qna_info['product_id'] = args[2]

            # 유저의 아이디
            qna_info['user_id'] = g.user_id['user_id']

            # 답변 여부
            qna_info['is_answered'] = args[3]

            body = [
                dict(qna) for qna in qna_service.get_qnas(qna_info, session)
            ]

            return jsonify(body), 200

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

        finally:
            session.close()

    return qna_app
예제 #13
0
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}"})
예제 #14
0
def create_product_endpoints(product_service, Session):

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

    @product_app.route('/category', methods=['GET'])
    def product_category():
        """ 카테고리 정보 전달 API

        상위 카테고리에 따른 하위 카테고리 리스트를 전달합니다.

        returns :
            200: 카테고리 리스트
            500: Exception

        Authors:
            고지원

        History:
            2020-09-21 (고지원): 초기 생성
            2020-09-25 (고지원): 한 번의 쿼리로 3개의 카테고리 데이터를 전달하도록 수정
        """
        session = Session()
        try:
            # 메뉴 데이터
            categories = product_service.get_menu(None, session)

            # 각 카테고리를 저장하기 위한 리스트
            second_category, first_category, main_category = [], [], []

            # JOIN 을 하며 생기는 중복을 제거하기 위해서 중복 체크 후 리스트에 저장
            for category in categories:

                # 1. (메인 카테고리의 id, 이름) 이 main_category 에 없을 경우 append
                if (category.m_id,
                        category.main_category_name) not in main_category:
                    main_category.append(
                        (category.m_id, category.main_category_name))

                # 2. (1차 카테고리의 id, 이름, 이에 해당하는 메인 카테고리의 id) 가 first_category 에 없을 경우 append
                if (category.f_id, category.first_category_name,
                        category.main_category_id) not in first_category:
                    first_category.append(
                        (category.f_id, category.first_category_name,
                         category.main_category_id))

                # 3. (2차 카테고리의 id, 이름, 이에 해당하는 1차 카테고리의 id) 가 second_category 에 없을 경우 append
                second_category.append(
                    (category.s_id, category.second_category_name,
                     category.first_category_id))

            # 카테고리의 계층 구조를 전달하기 위한 JSON 형식
            body = [
                {
                    # 메인 카테고리의 id 와 이름
                    'id':
                    m_menu[0],
                    m_menu[1]: [
                        {
                            # 1차 카테고리의 id 와 이름
                            'id':
                            f_menu[0],
                            f_menu[1]: [
                                {
                                    # 2차 카테고리의 id 와 이름
                                    'id': s_menu[0],
                                    'name': s_menu[1]
                                } for s_menu in second_category
                                if s_menu[2] == f_menu[0]
                            ]
                        } for f_menu in first_category
                        if f_menu[2] == m_menu[0]
                    ]
                } for m_menu in main_category
            ]

            return jsonify(body), 200

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

        finally:
            session.close()

    @product_app.route('', methods=['GET'])
    @validate_params(Param('limit', GET, int, default=100, required=False),
                     Param('offset', GET, int, required=False),
                     Param('main_category_id',
                           GET,
                           int,
                           rules=[Enum(4, 5, 6)],
                           required=False),
                     Param('first_category_id', GET, int, required=False),
                     Param('second_category_id', GET, int, required=False),
                     Param('is_promotion',
                           GET,
                           int,
                           rules=[Enum(0, 1)],
                           required=False),
                     Param('select',
                           GET,
                           int,
                           rules=[Enum(0, 1)],
                           required=False), Param('q',
                                                  GET,
                                                  str,
                                                  required=False),
                     Param('all_items',
                           GET,
                           int,
                           rules=[Enum(1)],
                           required=False))
    def products(*args):
        """ 상품 정보 전달 API
        여러 상품 정보가 필요한 페이지에서 쿼리 파라미터로 필터링에 사용될 값을 받아 필터링된 상품의 데이터들을 표출합니다.

        args:
            *args:
                limit: pagination 을 위한 파라미터
                offset: pagination 을 위한 파라미터
                main_category_id: 메인 카테고리의 pk
                first_category_id: 첫 번째 카테고리의 pk
                second_category_id: 두 번째 카테고리의 pk
                is_promotion: 세일 여부를 판단하기 위한 파라미터
                select: 최신순, 판매량순을 판단하기 위한 파라미터
                q: 검색을 위한 파라미터
                all_items: 전체 상품 리스트를 전달할지 판단하기 위한 파라미터

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

        Authors:
            고지원

        History:
            2020-09-21 (고지원): 초기 생성
            2020-09-23 (고지원): 파라미터 유효성 검사
            2020-09-24 (고지원): 검색을 위한 파라미터 추가, 검색 시 셀러 리스트 추가
            2020-09-28 (고지원): 브랜드, 뷰티 메인에서 필요한 데이터를 위한 필터링 추가
            2020-10-02 (고지원): 브랜드, 뷰티 메인의 첫 번째 카테고리 상품 리스트 전달
        """
        session = Session()
        try:
            # args[2]: 메인 카테고리의 pk, args[8]: 전체 상품을 보여줄 지 판단하는 파라미터, args[3]: 1차 카테고리의 pk, args[4]: 2차 카테고리의 pk
            if args[2] == 5 \
                or args[2] == 6 \
                and not args[8] \
                and not args[3] \
                and not args[4]:

                # 특정 메인 카테고리 아이디 (5: 브랜드, 6: 뷰티) 파라미터만 들어올 경우 베스트 상품, 추천 상품 데이터 등을 전달
                body = {
                    'best_items': [],
                    'brand_items': [],
                    'recommended_items': [],
                    'category_items': []
                }

                # 1. 파라미터로 들어온 카테고리의 id (args[2]) 에 따라 특정 셀러를 지정하고 상품 5개만 가져오기 위해 선언,
                # 2. 특정 1차 카테고리 아이디로 필터링된 상품 리스트를 가져오기 위해 선언
                if args[2] == 5:
                    f_cat_list = (12, 13, 14, 15, 16, 17, 18)
                    seller_id = 30

                else:
                    f_cat_list = (23, 24, 25, 26, 27, 28)
                    seller_id = 359

                for f_cat_id in f_cat_list:
                    # 1. 첫 번째 카테고리 상품 5개 씩 보여주기 위한 필터
                    f_category_filter = {
                        'first_category_id': f_cat_id,
                        'limit': 5
                    }

                    # 2. 카테고리의 id, name 과 함께 상품 리스트를 반환한다.
                    category_products = {
                        'category_id':
                        f_cat_id,
                        'category_name':
                        product_service.get_menu(
                            f_cat_id, session)[0].first_category_name,
                        'product':
                        product_service.get_products(f_category_filter,
                                                     session),
                    }

                    body['category_items'].append(category_products)

                # Best 상품 필터 - 해당하는 메인 카테고리의 상품 중 판매량 순 10개만 가져오기 위해 선언
                best_prod_filter = {
                    'main_category_id': args[2],
                    'limit': 10,
                }
                best_products = product_service.get_products(
                    best_prod_filter, session)

                # 추천 상품 필터 - 할인율 기준
                recommended_prod_filter = {
                    'main_category_id': args[2],
                    'limit': 30,
                    'discount_rate': 1
                }
                recommended_products = product_service.get_products(
                    recommended_prod_filter, session)

                # 특정 셀러 상품 리스트 필터
                seller_filter = {
                    'main_category_id': args[2],
                    'seller_id': seller_id
                }
                brand_products = product_service.get_products(
                    seller_filter, session)

                body['best_items'] = best_products
                body['brand_items'] = brand_products
                body['recommended_items'] = recommended_products

                return body

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

            # pagination
            filter_dict['limit'] = args[0]
            filter_dict['offset'] = args[1]

            # 카테고리
            filter_dict['main_category_id'] = args[2]
            filter_dict['first_category_id'] = args[3]
            filter_dict['second_category_id'] = args[4]

            # 세일
            filter_dict['is_promotion'] = args[5]

            # 판매량순, 최신순
            filter_dict['select'] = args[6]

            # 검색 필터
            filter_dict['q'] = args[7]

            # 메인 카테고리의 모든 상품 필터
            filter_dict['all_items'] = args[8]

            body = dict()

            # 상품 데이터
            body['products'] = product_service.get_products(
                filter_dict, session)

            # 검색어가 들어올 경우 전달하는 셀러 정보
            if filter_dict['q']:

                # 필터링된 셀러 리스트를 가져오기 위한 필터
                seller_info = dict()
                seller_info['name'] = filter_dict['q']
                seller_info['limit'] = 100

                # 검색된 셀러 리스트 정의
                sellers = dict()

                # 검색어에 해당하는 셀러의 리스트
                seller_list = [
                    dict(seller) for seller in product_service.get_sellers(
                        seller_info, session)
                ]

                # 셀러 검색 결과 개수
                sellers['count'] = len(seller_list)

                # 셀러 데이터
                sellers['seller_list'] = seller_list

                body['sellers'] = sellers

            return jsonify(body), 200

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

        finally:
            session.close()

    @product_app.route('/product/<int:product_id>', methods=['GET'])
    def product(product_id):
        """ 상품 상세 정보 전달 API
        path parameter 를 받아 한 상품의 상세 데이터를 표출합니다.

        args:
            product_id: 상품의 pk

        returns :
            200: 상위 카테고리에 따른 하위 카테고리 리스트
            500: Exception

        Authors:
            고지원

        History:
            2020-09-23 (고지원): 초기 생성
            2020-09-24 (고지원): seller 데이터를 한 번의 쿼리로 가지고 오도록 수정
        """
        session = Session()
        try:
            # 상품 데이터
            body = dict(product_service.get_product(product_id, session))

            return jsonify(body), 200

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

        finally:
            session.close()

    @product_app.route('/seller', methods=['GET'])
    @validate_params(
        Param('limit', GET, int, default=100, required=False),
        Param('offset', GET, int, required=False),
        Param('main_category_id',
              GET,
              int,
              rules=[Enum(4, 5, 6)],
              required=False),
        Param('select',
              GET,
              int,
              rules=[Enum(0, 1)],
              default=1,
              required=False),
    )
    def sellers(*args):
        """ 셀러 리스트 전달 API
        query parameter 를 받아 필터링된 셀러 리스트 데이터를 표출합니다.

        args:
            *args:
                limit: pagination 을 위한 limit
                offset: pagination 을 위한 offset
                main_category_id: 셀러 속성을 필터링 하기 위한 파라미터
                select: 1 일 경우 판매량 순, 0 일 경우 최신 순으로 필터링

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

        Authors:
            고지원

        History:
            2020-09-30 (고지원): 초기 생성
        """
        session = Session()
        try:
            # 필터링을 위한 딕셔너리
            seller_dict = dict()
            seller_dict['limit'] = args[0]
            seller_dict['offset'] = args[1]
            seller_dict['main_category_id'] = args[2]
            seller_dict['select'] = args[3]

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

            return jsonify(body), 200

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

        finally:
            session.close()

    return product_app
예제 #15
0
    return "Unravel SaaS"


# E.g., http://127.0.0.1:5000/start_trial?first_name=Alejandro&last_name=Fernandez&company=Unravel&title=Engineer&[email protected]&cloud_provider=EMR&send_email=true
@app.route("/start_trial", methods=["GET", "POST"])
@validate_params(Param("first_name", GET, str, required=True),
                 Param("last_name", GET, str, required=True),
                 Param("company", GET, str, required=True),
                 Param("title", GET, str, required=True),
                 Param("email", GET, str, required=True, rules=[RuleEmail()]),
                 Param("cloud_provider",
                       GET,
                       str,
                       required=True,
                       rules=[
                           Enum("EMR", "HDI", "GCP", "DATABRICKS_ON_AWS",
                                "DATABRICKS_ON_AZURE")
                       ]),
                 Param("send_email",
                       GET,
                       bool,
                       required=False,
                       default=lambda: False))
def start_trial(first_name, last_name, company, title, email, cloud_provider,
                send_email):
    """
    Start a free-trial of Unravel on the Cloud.
    :param first_name: First name (str)
    :param last_name: Last name (str)
    :param company: Company name (str)
    :param title: Job title (str)
    :param email: Email address (str)
예제 #16
0
def create_qna_endpoints(qna_service, Session):

    qna_app = Blueprint("qna_app", __name__, url_prefix="/api/qnas")

    @qna_app.route("/qna", methods=["POST", "DELETE"])
    @login_required
    def qna():
        session = Session()
        try:
            if request.method == "POST":

                # 문의 입력을 위한 데이터를 받는다.
                qna_info = {
                    "type_id": request.json["type_id"],
                    "user_id": g.user_id["user_id"],
                    "product_id": request.json["product_id"],
                    "content": request.json["content"],
                    "is_private": request.json["is_private"],
                }

                qna_service.insert_question(qna_info, session)

                session.commit()

                return jsonify({"message": "INSERT_SUCCESS"}), 200

            # 삭제하려는 유저와 문의한 유저가 일치하는지 id 를 통해 확인
            question_info = {
                "question_id": request.args.get("question_id"),
                "user_id": g.user_id["user_id"],
            }

            row_count = qna_service.delete_question(question_info, session)

            # 유저 아이디가 매칭될 경우 row_count = 1, token의 유저 id 와 삭제하려는 유저의 id 가 다를 경우 0
            if row_count == 0:
                return jsonify({"message": "DELETE_FAILED"}), 400

            session.commit()

            return jsonify({"message": "DELETE_SUCCESS"}), 200

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

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

        finally:
            session.close()

    @qna_app.route("", methods=["GET"])
    @validate_params(
        Param("limit", GET, int, default=100, required=False),
        Param("offset", GET, int, required=False),
        Param("product_id", GET, int, required=True),
    )
    def qnas(*args):
        session = Session()
        try:
            qna_info = dict()

            # pagination
            qna_info["limit"] = args[0]
            qna_info["offset"] = args[1]

            # 상품 상세페이지 상품 아이디
            qna_info["product_id"] = args[2]

            # 로그인한 유저의 문의인지 판단하기 위해 토큰을 통해 id를 가져온다.
            access_token = request.headers.get("Authorization")
            if access_token:
                payload = jwt.decode(access_token, SECRET_KEY, ALGORITHM)
            else:
                payload = None

            body = {
                "login_user_id": payload,
                "qna": [dict(qna) for qna in qna_service.get_qnas(qna_info, session)],
            }

            return jsonify(body), 200

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

        finally:
            session.close()

    @qna_app.route("/user", methods=["GET"])
    @login_required
    @validate_params(
        Param("limit", GET, int, default=100, required=False),
        Param("offset", GET, int, required=False),
        Param("product_id", GET, int, required=False),
        Param("is_answered", GET, int, rules=[Enum(0, 1)], required=False),
    )
    def user_qnas(*args):
        session = Session()
        try:
            qna_info = dict()

            # pagination
            qna_info["limit"] = args[0]
            qna_info["offset"] = args[1]

            # 상품 아이디 (특정 상품의 상세페이지에서 내가 쓴 문의 필터링을 위해)
            qna_info["product_id"] = args[2]

            # 유저의 아이디
            qna_info["user_id"] = g.user_id["user_id"]

            # 답변 여부
            qna_info["is_answered"] = args[3]

            body = [dict(qna) for qna in qna_service.get_qnas(qna_info, session)]

            return jsonify(body), 200

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

        finally:
            session.close()

    return qna_app