흰둥이는 코드를 짤 때 짖어 (왈!왈!왈!왈!왈!왈!왈!왈!왈!왈!왈!)

(파이썬, MySQL, 시험) 학생 성적 관리 프로그램 본문

시험

(파이썬, MySQL, 시험) 학생 성적 관리 프로그램

흰둥아솜사탕 2023. 3. 31. 15:12
728x90
반응형

시험문제

 

1. 학생을 등록한다.
2. 학생의 등록된 정보를 성적(평균)으로 내림차순으로 출력한다.
(단, 학생의 점수가 있을 경우 점수도 같이 출력한다. 등록된 학생의 전체 수와 해당 학생의 석차를 같이 출력한다. 동점인 경우 학번으로 내림차순으로 함)
3. 학생정보를 수정한다.
4. 학생정보를 삭제한다. 학생정보를 삭제할 경우 점수도 같이 삭제한다.
5. 학생정보를 검색한다.
(단, 학생의 점수가 있을 경우 점수도 같이 출력한다.)
6. 학생점수를 등록한다. 점수를 등록할 때 총점, 평균을 계산하여 같이 저장한다.
7. 학생점수를 수정한다. 점수를 수정할 경우 총점, 평균을 계산하여 같이 저장한다.
8. 학생점수를 삭제한다.
9. DAO, DTO를 작성한다.
10. View를 이용하여 데이터를 불러온다.

MySQL

use kdt;

# 학생 테이블
create table tb_student(
	s_num int primary key,
    s_name varchar(20) not null,
    s_hp varchar(20) not null,
    s_email varchar(50) not null,
    s_address varchar(100) not null,
    s_regdate datetime default now()
);

# 성적 테이블
create table tb_grade(
	s_num int primary key,
    g_java int default 0,
    g_python int default 0,
    g_c int default 0,
    g_total int default 0,
    g_avg float default 0,
    g_regdate datetime default now(),
    
    foreign key(s_num) references tb_student(s_num)
);

# 학생 뷰
create view vw_student_profile as select s.s_num, s.s_name, s.s_hp, s.s_email, s.s_address, g.g_java, g.g_python, g.g_c, g.g_total, g.g_avg from tb_student as s left outer join tb_grade as g on s.s_num=g.s_num order by g.g_avg desc, s.s_num desc;

 

DTO

# 학생 DTO
class StudentDTO:
    def __init__(self, sNum, sName, sHp, sEmail, sAddress):
        self.sNum = sNum
        self.sName = sName
        self.sHp = sHp
        self.sEmail = sEmail
        self.sAddress = sAddress

    def setSNum(self, sNum):
        self.sNum = sNum

    def getSNum(self):
        return self.sNum

    def setSName(self, sName):
        self.sName = sName

    def getSName(self):
        return self.sName

    def setSHp(self, sHp):
        self.sHp = sHp

    def getSHp(self):
        return self.sHp

    def setSEmail(self, sEmail):
        self.sEmail = sEmail

    def getSEmail(self):
        return self.sEmail

    def setSAddress(self, sAddress):
        self.sAddress = sAddress

    def getSAddress(self):
        return self.sAddress
# 성적 DTO
class GradeDTO:
    def __init__(self, sNum, gJava, gPython, gC, gTotal, gAvg):
        self.sNum = sNum
        self.gJava = gJava
        self.gPython = gPython
        self.gC = gC
        self.gTotal = gTotal
        self.gAvg = gAvg

    def setSNum(self, sNum):
        self.sNum = sNum

    def getSNum(self):
        return self.sNum

    def setGJava(self, gJava):
        self.gJava = gJava

    def getGJava(self):
        return self.gJava

    def setGPython(self, gPython):
        self.gPython = gPython

    def getGPython(self):
        return self.gPython

    def setGC(self, gC):
        self.gC = gC

    def getGC(self):
        return self.gC

    def setGTotal(self, gTotal):
        self.gTotal = gTotal

    def getGTotal(self):
        return self.gTotal

    def setGAvg(self, gAvg):
        self.gAvg = gAvg

    def getGAvg(self):
        return self.gAvg

 

DAO

# MySQLdb 라이브러리
import MySQLdb
# 학생 DAO
class StudentDAO:
    def __init__(self):
        self.db = None

    # MySQL 연결
    def connect(self):
        self.db = MySQLdb.connect('localhost', 'root', '1234', 'kdt')

    # db 연결 해제
    def disconnect(self):
        self.db.close()

    # 학생 전체 조회
    def selectAll(self):
        self.connect()
        # 학생 정보를 딕셔너리로 가져온다.
        cur = self.db.cursor(MySQLdb.cursors.DictCursor)

        # 가져올 때 미리 성적순으로 정렬을 해둔 뷰를 select한다.
        sql = "select s_num, s_name, s_hp, s_email, s_address, g_java, g_python, g_c, g_total, g_avg from vw_student_profile"
        cur.execute(sql)

        # 가져올 값을 딕셔너리 형태로 한 row씩 리스트에 담는다.
        datas = []
        while True:
            data = cur.fetchone()
            if data:
                datas.append(data)
            else:
                break

        self.disconnect()
        return datas

    # 학생 등록
    def insert(self, sDto):
        self.connect()
        cur = self.db.cursor()
        sql = "insert into tb_student (s_num, s_name, s_hp, s_email, s_address) values(%s, %s, %s, %s, %s)"

        # sql에서 %s에 담을 값들을 학생DTO에서 getter를 사용하여 튜플형식으로 생성한다.
        data = (sDto.getSNum(), sDto.getSName(), sDto.getSHp(), sDto.getSEmail(), sDto.getSAddress())

        # 등록이 되었는지 여부를 result에 담아 반환한다.
        result = cur.execute(sql, data)
        self.db.commit()
        self.disconnect()

        return result

    # 학생 수정
    def update(self, column, content, sNum):
        self.connect()
        cur = self.db.cursor()

        # 수정할 컬럼도 입력을 받아 문자열 연결 연산자로 붙인다.
        sql = "update tb_student set " + column + " = %s where s_num = %s"
        data = (content, sNum)

        # 수정이 되었는지 여부를 result에 담아 반환한다.
        result = cur.execute(sql, data)
        self.db.commit()
        self.disconnect()

        return result

    # 학생 삭제
    def delete(self, sNum):
        self.connect()
        cur = self.db.cursor()

        data = (sNum,)
        # 외래키로 연결되어있는 row를 삭제하기 위해 먼저 성적 테이블에서 삭제를 진행한다.
        sql = "delete from tb_grade where s_num = %s"
        cur.execute(sql, data)

        # 성적 테이블에서 삭제가 완료되면 이후에 학생 테이블에서 삭제를 진행한다.
        sql = "delete from tb_student where s_num = %s"
        # 삭제가 되었는지 여부를 result에 담아 반환한다.
        result = cur.execute(sql, data)
        self.db.commit()
        self.disconnect()

        return result

    # 학생 검색
    def select(self, column, content):
        self.connect()
        # 학생 정보를 딕셔너리로 가져온다.
        cur = self.db.cursor(MySQLdb.cursors.DictCursor)

        # 검색할 컬럼도 입력을 받아 문자열 연결 연산자로 붙인다. 이후 한번 순서로 정렬을 한다.
        sql = "select s_num, s_name, s_hp, s_email, s_address, g_java, g_python, g_c, g_total, g_avg from vw_student_profile where " + column + " like %s order by s_num desc"
        # like로 검색어가 포함되어있는 모든 row를 가져오기위해 '% %'를 연결 연산자로 붙인다.
        data = ("%" + content + "%",)
        cur.execute(sql, data)

        # 가져올 값을 딕셔너리 형태로 한 row씩 리스트에 담는다.
        datas = []
        while True:
            data = cur.fetchone()
            if data:
                datas.append(data)
            else:
                break
        self.disconnect()

        return datas
# 성적 DAO
class GradeDAO:
    def __init__(self):
        self.db = None

    def connect(self):
        self.db = MySQLdb.connect('localhost', 'root', '1234', 'kdt')

    def disconnect(self):
        self.db.close()

    # 성적 등록
    def insert(self, gDto):
        self.connect()
        cur = self.db.cursor()
        sql = "insert into tb_grade (s_num, g_java, g_python, g_c, g_total, g_avg) values(%s, %s, %s, %s, %s, %s)"

        # sql에서 %s에 담을 값들을 성적DTO에서 getter를 사용하여 튜플형식으로 생성한다.
        data = (gDto.getSNum(), gDto.getGJava(), gDto.getGPython(), gDto.getGC(), gDto.getGTotal(), gDto.getGAvg())

        # 등록이 되었는지 여부를 result에 담아 반환한다.
        result = cur.execute(sql, data)
        self.db.commit()
        self.disconnect()

        return result

    # 성적 수정
    def update(self, gDto):
        self.connect()
        cur = self.db.cursor()
        sql = "update tb_grade set g_java = %s, g_python = %s, g_c = %s, g_total = %s, g_avg = %s where s_num = %s"

        # sql에서 %s에 담을 값들을 성적DTO에서 getter를 사용하여 튜플형식으로 생성한다.
        data = (gDto.getGJava(), gDto.getGPython(), gDto.getGC(), gDto.getGTotal(), gDto.getGAvg(), gDto.getSNum())

        # 수정이 되었는지 여부를 result에 담아 반환한다.
        result = cur.execute(sql, data)
        self.db.commit()
        self.disconnect()

        return result

    # 성적 삭제
    def delete(self, sNum):
        self.connect()
        cur = self.db.cursor()
        sql = "delete from tb_grade where s_num = %s"
        data = (sNum,)

        # 삭제가 되었는지 여부를 result에 담아 반환한다.
        result = cur.execute(sql, data)

        self.db.commit()
        self.disconnect()

        return result

 

Service

# 학생 Service
class StudentService:
    # 학생 DAO를 생성자로 먼저 호출해준다.
    def __init__(self):
        self.sDao = StudentDAO()

    # 학생 전체 조회
    def showAll(self):
        datas = self.sDao.selectAll()

        # 이미 datas안에 등수 순서대로 담겨 있기 때문에 순서대로 등수를 적을 i값을 설정해준다.
        i = 1
        for data in datas:

            # data안에 딕셔너리 구조로 되어있으므로 key값으로 value를 출력한다.
            print(f"\n등수: {i} | 학번: {data['s_num']} | 이름: {data['s_name']} | 연락처: {data['s_hp']} | 이메일: {data['s_email']} | 주소: {data['s_address']}")
            print(f"자바: {data['g_java']} | 파이썬: {data['g_python']} | C언어: {data['g_c']} | 총점: {data['g_total']} | 평균: {data['g_avg']}")

            # 다음 사람의 등수를 +1 해준다.
            i += 1

    # 학생 등록
    def create(self):
        # 등록할 학생 정보를 입력 받는다.
        sNum = int(input('학번을 입력하세요: '))
        sName = input('이름을 입력하세요: ')
        sHp = input('연락처를 입력하세요: ')
        sEmail = input('이메일을 입력하세요: ')
        sAddress = input('주소를 입력하세요: ')

        # 입력받은 값으로 학생DTO를 생성한다.
        sDto = StudentDTO(sNum, sName, sHp, sEmail, sAddress)

        # 학번이 Primary key로 설정되어 있으므로 중복할 경우 예외처리로 잡아내어 화면에 실패했다고 출력해준다.
        try:
            self.sDao.insert(sDto)
            print('학생이 등록되었습니다.')
        except:
            print('실패했습니다. 학번을 확인해주세요.')


    # 학생 수정
    def modify(self):
        # 사용자가 수정할 항목을 숫자로 선택시 column명을 value로 반환해줄 딕셔너리를 생성한다.
        columns = {1:'s_name', 2:'s_hp', 3:'s_email', 4:'s_address'}

        # 수정할 학번과 항목 그리고 수정값을 입력받는다.
        sNum = int(input('수정할 학생의 학번을 입력하세요: '))
        columnNum = int(input('수정할 항목 1.학생이름 2.연락처 3. 이메일 4.주소'))
        content = input('수정값을 입력하세요: ')

        # 수정할 column을 딕셔너리 key값으로 value를 가져와 저장한다.
        column = columns[columnNum]

        # 수정이 정상적으로 완료되었는지 조건문으로 검사한다.
        if self.sDao.update(column, content, sNum):
            print('학생수정이 완료되었습니다.')
        else:
            print('실패했습니다. 학번을 확인해주세요.')

    # 학생 삭제
    def drop(self):
        # 삭제할 학생의 학번을 입력한다.
        sNum = int(input('삭제할 학생 학번을 입력하세요: '))

        # 삭제가 정상적으로 완료되었는지 조건문으로 검사한다.
        if self.sDao.delete(sNum):
            print('학생삭제가 완료되었습니다.')
        else:
            print('실패했습니다. 학번을 확인해주세요.')

    # 학생 검색
    def show(self):
        # 사용자가 검색할 항목을 숫자로 선택시 column명을 value로 반환해줄 딕셔너리를 생성한다.
        columns = {1:'s_name', 2:'s_hp', 3:'s_email', 4:'s_address'}

        # 검색할 항목과 검색어를 입력받는다.
        columnNum = int(input('검색할 항목 1.학생이름 2.연락처 3. 이메일 4.주소'))
        content = input('검색단어를 입력하세요: ')

        # 검색할 column을 딕셔너리 key값으로 value를 가져와 저장한다.
        column = columns[columnNum]

        datas = self.sDao.select(column, content)
        for data in datas:

            # data안에 딕셔너리 구조로 되어있으므로 key값으로 value를 출력한다.
            print(f"\n학번: {data['s_num']} | 이름: {data['s_name']} | 연락처: {data['s_hp']} | 이메일: {data['s_email']} | 주소: {data['s_address']}")
            print(f"자바: {data['g_java']} | 파이썬: {data['g_python']} | C언어: {data['g_c']} | 총점: {data['g_total']} | 평균: {data['g_avg']}")

        # 검색 결과가 없을경우를 알려주는 출력문이다.
        if not datas:
            print('검색결과가 없습니다.')
# 성적 Service
class GradeService:
    # 성적 DAO를 생성자로 먼저 호출해준다.
    def __init__(self):
        self.gDao = GradeDAO()

    # 성적 등록
    def create(self):
        # 등록할 성적을 입력 받는다.
        sNum = int(input('학번을 입력하세요: '))
        gJava = int(input('Java 점수를 입력하세요: '))
        gPython = int(input('Python 점수를 입력하세요: '))
        gC = int(input('C언어 점수를 입력하세요: '))

        # 이후 총점과 평균값을 각각 연산하여 저장한다.
        gTotal = gJava + gPython + gC
        gAvg = gTotal / 3

        # 입력받은 값으로 성적DTO를 생성한다.
        gDto = GradeDTO(sNum, gJava, gPython, gC, gTotal, gAvg)

        # 학번이 Primary key와 Foreign key로 설정되어 있으므로 중복할 경우와 학생테이블에 학번이 없는경우를 예외처리로 잡아내어 화면에 실패했다고 출력해준다.
        try:
            self.gDao.insert(gDto)
            print('성적이 등록되었습니다.')
        except:
            print('실패했습니다. 학번을 확인해주세요.')

    # 성적 수정
    def modify(self):
        # 수정할 성적을 입력 받는다.
        sNum = int(input('수정할 학생의 학번을 입력하세요: '))
        gJava = int(input('Java 점수를 입력하세요: '))
        gPython = int(input('Python 점수를 입력하세요: '))
        gC = int(input('C언어 점수를 입력하세요: '))

        # 이후 총점과 평균값을 각각 연산하여 저장한다.
        gTotal = gJava + gPython + gC
        gAvg = gTotal / 3

        # 입력받은 값으로 성적DTO를 생성한다.
        gDto = GradeDTO(sNum, gJava, gPython, gC, gTotal, gAvg)

        # 수정이 정상적으로 완료되었는지 조건문으로 검사한다.
        if self.gDao.update(gDto):
            print('성적이 수정되었습니다.')
        else:
            print('실패했습니다. 학번을 확인해주세요.')

    # 성적 삭제
    def drop(self):
        # 삭제할 학생의 학번을 입력한다.
        sNum = int(input('삭제할 성적의 학번을 입력하세요: '))

        # 삭제가 정상적으로 완료되었는지 조건문으로 검사한다.
        if self.gDao.delete(sNum):
            print('성적이 삭제되었습니다.')
        else:
            print('실패했습니다. 학번을 확인해주세요.')

 

View

# 화면
class View:
    # 먼저 학생 Service와 성적 Service를 생성자로 호출해준다.
    def __init__(self):
        self.sService = StudentService()
        self.gService = GradeService()

    # 화면 동작
    def run(self):
        # 프로그램이 멈추지 않도록 무한루프를 돌려준다.
        while True:

            # 사용자가 이용할 서비스를 입력 받는다.
            menu = int(input('1.학생등록 2.전체학생출력 3.학생수정 4.학생삭제 5.학생검색 6.점수등록 7.점수수정 8.성적삭제 0.종료'))

            # 학생 등록
            if menu == 1:
                self.sService.create()
            # 학생 전체 조회
            elif menu == 2:
                self.sService.showAll()
            # 학생 수정
            elif menu == 3:
                self.sService.modify()
            # 학생 삭제
            elif menu == 4:
                self.sService.drop()
            # 학생 검색
            elif menu == 5:
                self.sService.show()
            # 성적 등록
            elif menu == 6:
                self.gService.create()
            # 성적 수정
            elif menu == 7:
                self.gService.modify()
            # 성적 삭제
            elif menu == 8:
                self.gService.drop()
            # 프로그램 종료
            elif menu == 0:
                break

            print()

 

프로그램 동작

start = View()
start.run()
728x90
반응형

'시험' 카테고리의 다른 글

(Node.js, MySQL, ORM) 학생 성적 API  (0) 2023.05.15
(파이썬, 시험) 전화번호부 만들기  (0) 2023.03.17
Comments