• [캡스톤] 시간표 데이터 만들기

    2023. 9. 12.

    by. namudongs

    개요

    시간표 기능을 구현하기 위해서는 학교의 강의 정보를 데이터화해야 했다.
    내가 생각한 방법으로는 학교의 강의 정보가 엑셀로 주어지므로 이를 JSON으로 변환한 뒤,
    그 데이터를 플러터 내에 파싱해서 이용하는 것이었다.

     

    우리 학교에서 주어지는 강의 정보(수업시간표)는 이렇게 되어있다.

    데이터 변환

    이 데이터를 JSON으로 변환하는 것은 Mr. Data Converter를 이용했다.
    그냥 변환하고 싶은 시트를 드래그로 복사해서 붙여넣으면 된다.

    이렇게 변환한 기본 데이터는 아래와 같다.

    {
        "구분":"전공선택",
        "과목코드":4461062,
        "분반":1,
        "과목명":"운영체제",
        "시수":"3-3-0-0",
        "부문":"",
        "대상학과 및\n학년":"전기전자공학과()3",
        "대상\n인원":40,
        "대학":"IT대학",
        "학과":"전기전자공학과",
        "전공":"",
        "교번":102170,
        "담당교수":"최양호",
        "직종":"전임교원",
        "강의실":"월5,6,수1(공5호관 107)",
        "비고":null,
        "이러닝\n강좌":"N",
        "수강용과목":null,
        "폐강여부":"N"
    }

    😇

     

    데이터 튜닝

    이제 이 JSON 데이터에서 시간표에 필요한 데이터를 따로 튜닝하는 작업이 필요하다.
    예를 들면 강의실과 강의 시간은 모두 "강의실" 속성의 값에 들어있다.
    근데 강의실과 강의 시간이 분리되어 있지 않으므로 이를 분리하는 작업이 필요하다.

    분리하는 작업이 제일 어려웠는데 저저번 학기에 들었던 데이터분석프로그래밍 강의가 많이 도움이 됐다.
    아래와 같은 알고리즘으로 코랩을 통해 분류를 했는데 정말 많은 시행착오가 있었다..
    저녁 먹고 시작해서 해뜰때까지 시도했다.

    import re
    
    time_mapping = {
        'A1': [0, 7.5],
        'A2': [9, 16.5],
        'A3': [18, 25.5],
        'A4': [27, 34.5],
        'A5': [36, 43.5],
        'A6': [48, 55.5],
        '1': [0, 5],
        '2': [5, 10],
        '3': [10, 15],
        '4': [15, 20],
        '5': [20, 25],
        '6': [25, 30],
        '7': [30, 35],
        '8': [35, 40],
        '9': [40, 45],
        '10': [45, 50],
        '11': [50, 55],
        '12': [55, 60],
        '13': [60, 65],
        '14': [65, 70]
    }
    
    def transform_class_info_v4(class_info_str):
        class_info_parts = re.split(r'\([^)]+\)', class_info_str)
        classroom_names = re.findall(r'\(([^)]+)\)', class_info_str)
        result = {
            "day": [],
            "classroom": [],
            "start": [],
            "end": []
        }
        for idx, class_part in enumerate(class_info_parts[:-1]):
            days_with_times = re.split(r'(?=[월화수목금토])', class_part)
            for day_time in days_with_times:
                if not day_time:
                    continue
                day = re.findall(r'[월화수목금토]', day_time)
    
                if not day:
                    continue
    
                day = day[0]
                times = re.findall(r'\d+|A\d+', day_time)
                start_times = [time_mapping[t][0] for t in times]
                end_times = [time_mapping[t][-1] for t in times]
                result["day"].append(day)
                result["classroom"].append(classroom_names[idx])
                result["start"].append(min(start_times))
                result["end"].append(max(end_times))
    
        return result

    복잡해 보이는게 정상이다🙀


    이런 데이터 분석/변환을 직접 해보는건 처음이라 이것저것 찾아보며 만들어서, 지금 생각해 보면 어떻게 만들었는지 잘 기억도 안난다..
    그래도 간단히 코드를 설명해 보자면, "강의실"속성을 총 4개의 속성으로 분리했다.

    day, classroom, start, end
    각각 리스트로 만들어서 여러 개의 요일이라면 리스트의 인덱스를 기준으로 구분했다.

     

    성공적으로 분리된다면 다음처럼 된다.

    월A1,목A1(60주년기념관 404)
    { 
        day : [“월”, “목”], 
        classroom : [“60주년기념관 404”, “60주년기념관 404”], 
        start : [0, 0], 
        end : [7.5, 7.5] 
    }
    
    화5,6(자3호관 109),수3,4(자2호관 426)
    { 
        day : [“화”, “수”], 
        classroom : [“자3호관 109”, “자2호관 426”], 
        start : [20, 10], 
        end : [30, 20] 
    }
    
    월A6,목A6(인1호관 105)
    { 
        day : [“월”, “목”], 
        classroom : [“인1호관 105”, “인1호관 105”], 
        start : [48, 48], 
        end : [55.5, 55.5] 
    }
    
    월A4,목A4(글로벌경영관 002)
    { 
        day : [“월”, “목”], 
        classroom : [“글로벌경영관 002, “글로벌경영관 002”], 
        start : [27, 27], 
        end : [34.5, 34.5] 
    }

    startend 속성은 시간표에 강의를 추가할 때 시간을 따로 매핑해야해서 만든 속성인데,
    데이터도 튜닝하고, 기능을 구현하다보니 데이터에서 매핑을 하지 말고 기능 구현시에 매핑을 하는 게 더 나았을 것 같다.

    그렇지만 이런 것도 다 경험이니 좋게 생각하려 한다.


    왜 그런 생각이 들었냐면, 기능을 구현하다보니 시간 매핑 규칙을 바꿔야해서 데이터를 전부 다 다시 튜닝했기 때문이다.
    데이터 구조에서 startend 속성을 나누지말고 A1, A2, 1, 2 이런 식으로 몇 교시인지를 데이터 내에 한가지 속성으로 넣어놓고 구현 과정에서 시간을 매핑하는게 유지보수에 더 유연한 방법이다..


    위 데이터를 보면 알겠지만, 학교에서 주는 강의 정보에 쉼표가 하나 더 들어가는 등 분류를 어렵게 하는 부분이 너무 많았다.
    모든 데이터가 일관적이지도 않았고.. 그래서 이 부분을 분리하는 데에 시간을 많이 쏟았다.

     

    결과

    {
    "category": "전공선택",
    "code": 4461062,
    "division": 1,
    "lname": "운영체제",
    "peoplecount": 40,
    "college": "IT대학",
    "major": "",
    "procode": 102170,
    "professor": "최양호",
    "prowork": "전임교원",
    "day": [
    "월",
    "수"
    ],
    "classroom": [
    "공5호관 107",
    "공5호관 107"
    ],
    "start": [
    20,
    0
    ],
    "end": [
    30,
    5
    ],
    "department": "전기전자공학과",
    "number": 2447
    }

    최종적으로 튜닝을 모두 완료한 데이터는 이렇다.

     

    일단은 지금까지 필요한 속성들만 가지고 왔고, 앞으로 필요할때마다 속성을 가져오면 될 것 같다.
    number속성을 추가해서 데이터를 유지보수하기 조금 쉽게 해놓았다.

    데이터를 직접 만들다 보니 데이터를 만든다는게 얼마나 어렵고 복잡한 일인지 새삼 다시 깨닫게 되었다.


    처음에는 에브리타임에서 그냥 크롤링을 해버릴까 생각해 봤지만, 이왕 졸업작품을 만드는 건데 학교에서 배운 것을 써먹는것도 좋은 경험이겠다 싶었다. 만들고 나니 에브리타임이 대단하게 느껴졌다..

     

    그런데 찾아보니 이 에브리타임의 강의 정보 데이터를 크롤링 해 무단으로 사용한 업체의 대표와 이사가 실형을 받은 기사가 있었다.

    이렇게 만들기 어려운 데이터를 무단으로 가져가는 것은 범죄라는 점. 명심하자🤨

    댓글