1. PlayerStats

한국과 관련있는 선수들의 스텟을 추출을 하기 위해 statsapi를 활용하였다. api를 활용해서 선수들의 스텟을 추출하기 위해 선수의 그룹유형과 id가 필요하였기 때문에 데이터를 뽑아 둘의 값을 지정하였다.

class PlayerStats(APIView):
    permission_classes = [AllowAny]
    player_id = {
        "류현진": 547943,
        "오승환": 493200,
        "추신수": 425783,
        "강정호": 628356,
        "최지만": 596847,
    }
    default_group = {
        "류현진": "pitching",
        "오승환": "pitching",
        "추신수": "hitting",
        "강정호": "fielding",
        "최지만": "fielding",
    }

NUGU를 통해 값이 들어오면 available_picher_stats.json 파일을 활용하여 값을 api로 활용할 수 있게 변환시킨다. available_pitcher_stats으로 NUGU에서 들어온 한글발화값을 원하는 속성으로 바꾸게끔 한다.

(avilable_picher_stats.json)
{
  "출장횟수": "gamesPlayed",
  "땅볼아웃": "groundOuts",
  "플라이아웃": "airOuts",
  "득점": "runs",
  "2루타": "doubles",
  "3루타": "triples",
  "홈런": "homeRuns",
  "삼진아웃": "strikeOuts",
  "볼넷": "baseOnBalls",
  "고의사구": "intentionalWalks",
  "안타": "hits",
  "데드볼": "hitByPitch",
  "타율": "avg",
  "타수": "atBats",
  "출루율": "obp",
  "장타율": "slg",
  "OPS": "ops",
  "도루사": "caughtStealing",
  "도루": "stolenBases",
  "도루성공률": "stolenBasePercentage",
  "더블플레이": "groundIntoDoublePlay",
  "투구수": "numberOfPitches",
  "타석수": "plateAppearances",
  "총 루타수": "totalBases",
  "득점타": "rbi",
  "잔루": "leftOnBase",
  "희생번트": "sacBunts",
  "희생플라이": "sacFlies",
  "바빕": "babip",
  "땅볼아웃/뜬공아웃 비율": "groundOutsToAirouts",
  "홈런 타율": "atBatsPerHomeRun"
}
def post(self, request):
    data = request.data["action"]
    last_url = request.get_full_path().split("/")[-1]
    if last_url == "pitcher-stat":
        position = "pitcher"
    else:
        position = "hitter"
    with open(f"configure_package/available_{position}_stats.json") as pitcher_json:
        available_pitcher_stats = json.load(pitcher_json)
    request_stat = data["parameters"][f"{position}_stat"]["value"]
    player = data["parameters"][position]["value"]
    player_stat = available_pitcher_stats[request_stat]

다음은 직접 api로 스텟을 직접 추출한다. statsapi에서 원하는 선수의 스텟을 뽑기 위해서는 playerid, groupid, season를 요구하기 때문에 각각 알맞는 value를 넣어준다.

return_stat = statsapi.player_stat_data(
            self.player_id[player], self.default_group[player], "season"
        )["stats"][0]["stats"][player_stat]

이제 리턴값의 종류를 str인지 int인지 구별하여 적절한 대답을 할 수 있도록 구분한뒤, 선수이름,요구한 스텟,요구한 스텟의 결과값을 NUGU에 전송한다.

 if type(return_stat) is str:
            return_stat = float(return_stat)
        else:
            return_stat = str(return_stat) + "회"

        response_builder = {
            "version": "2.0",
            "resultCode": "OK",
            "output": {
                "pitcher": player,
                "pitcher_stat": request_stat,
                "return_pitcher_stat": return_stat,
            },
        }
        return Response(response_builder)

2. NextGame

MLB 리그에 속한 팀 중 하나의 팀을 선정하여 다음 경기를 물어보면, time data를 이용하여 현재 시간으로부터 365일 사이 내에서 오늘과 가장 가까운 경기의 상대팀과 경기 날짜를 불러와서 누구스피커에서 알려준다. 경기 일정을 추출을 하기 위해 statsapi를 활용하였다. api를 활용해서 선수들의 스텟을 추출하기 위해 팀의 id가 필요하였기 때문에 데이터를 뽑아 값을 지정하였다. NUGU를 통해 값이 들어오면 mlb_team_list.json 파일을 활용하여 값을 api로 활용할 수 있게 변환시킨다. mlb_team_list로 NUGU에서 들어온 한글발화값을 원하는 속성으로 바꾸게끔 한다.

{
  "LA 다저스": 108,
  "애리조나 다이아몬드백스": 109,
  "볼티모어 오리올스": 110,
  "보스턴 레드삭스": 111,
  "시카고 컵스": 112,
  "신시내티 레즈": 113,
  "클리블랜드 인디언스": 114,
  "콜로라도 로키스": 115,
  "디트로이트 타이거스": 116,
  "휴스턴 애스트로스": 117,
  "캔자스시티 로열스": 118,
  "로스앤젤레스 에인절스": 119,
  "워싱턴 내셔널스": 120,
  "뉴욕 메츠": 121,
  "오클랜드 애슬레틱스": 133,
  "피츠버그 파이리츠": 134,
  "샌디에이고 파드리스": 135,
  "시애틀 매리너스": 136,
  "샌프란시스코 자이언츠": 137,
  "세인트루이스 카디널스": 138,
  "탬파베이 레이스": 139,
  "텍사스 레인저스": 140,
  "토론토 블루제이스": 141,
  "미네소타 트윈스": 142,
  "필라델피아 필리스": 143,
  "애틀랜타 브레이브스": 144,
  "시카고 화이트삭스": 145,
  "마이애미 말린스": 146,
  "뉴욕 양키스": 147,
  "밀워키 브루어스": 158,
  "추신수": 140,
  "류현진": 110,
  "강정호": 134,
  "오승환": 115,
  "최지만": 139
}

다음은 직접 api로 스텟을 직접 추출한다. statsapi에서 원하는 팀의 다음 경기 일정을 뽑기 위해서는 team_id를 요구하기 때문에 알맞는 value를 넣어준다.

class NextGame(APIView):
    permission_class = [AllowAny]

    def post(self, request):
        data = request.data["action"]
        with open("configure_package/available_schedule.json") as schedule:
            available_schedule = json.load(schedule)
        request_team = data["parameters"]["team"]["value"]
        team_id = available_schedule[request_team]

현재의 시간 기준으로 하기때문에 datetime을 이용하고 start_date와 end_date를 1년 단위로 지정해준다.

        time = datetime.now()
        date_and_team = []
        default_start_date = time.strftime("%Y-%m-%d")
        default_end_date = (time + timedelta(days=365)).strftime("%Y-%m-%d")

이제 요청한 팀의 이름,다음 경기 일정,상대팀의 이름의 결과값을 NUGU에 전송한다.

        team_schedule = statsapi.schedule(
            start_date=default_start_date, end_date=default_end_date, team=team_id
        )
        date_and_team.append(team_schedule[0]["game_date"])
        date_and_team.append(team_schedule[0]["away_name"])

        response_builder = {
            "version": "2.0",
            "resultCode": "OK",
            "output": {
                "our_team": request_team,
                "return_game_date": date_and_team[0],
                "return_away_name": date_and_team[1],
            },
        }
        return Response(response_builder)

3. Scheduler

처음에는 MLB 야구팀의 일정만 추가하려했지만, 더 많은 스포츠를 담기 위해 리그와 팀을 추가하였다. 검색할 수 있는 팀의 수는 137개로 해당 리그의 알 수 있는 모든 일정검색 기능을 추가하였다. 나중에 코드를 추가하였기 때문에 if문으로 기존의 야구팀의 일정 검색과 나머지 리그,팀의 일정 검색을 나누었다. 먼저, NUGU로 ‘team_name’을 받을 수 있게 request를 해주었다.

    def post(self, request):
        data = request.data["action"]
        request_team = data["parameters"]["team_name"]["value"]

        with open("configure_package/mlb_team_list.json") as mlb_team:
            mlb_team_list = json.load(mlb_team)
        if request_team in mlb_team_list:

야구팀의 일정 검색의 경우, Next_Game과 비슷한 로직을 사용하였다. 팀의 한글 이름을 NUGU를 통해 받고, configure_package에 저장되어 있는mlb_i19n_team_list.json을 통해 해당 팀id를 추출하였다. output인 date_and_team 배열을 길게하여 모든 일정을 소화하고 싶었지만, NUGU의 발화시간을 고려하여 다음 3개의 경기만 배열에 저장하도록 설정하였다.

team_id = mlb_team_list[request_team]
            time = datetime.now()
            default_start_date = time.strftime("%Y-%m-%d")
            default_end_date = (time + timedelta(days=90)).strftime("%Y-%m-%d")
            team_schedule = statsapi.schedule(
                start_date=default_start_date, end_date=default_end_date, team=team_id
            )
            date_and_team = [[0] * 2 for i in range(len(team_schedule) - 1)]
            for schedule in range(0, 3):
                date_and_team[schedule][0] = team_schedule[schedule]["game_date"]
                date_and_team[schedule][1] = team_schedule[schedule]["away_name"]

            with open("configure_package/mlb_i18n_team_list.json") as mlb_i18n_team:
                away_team_list = json.load(mlb_i18n_team)

            response_builder = {
                "version": "2.0",
                "resultCode": "OK",
                "output": {
                    "our_team_name": request_team,
                    "return_game_date1": date_and_team[0][0],
                    "return_away_name1": away_team_list[date_and_team[0][1]],
                    "return_game_date2": date_and_team[1][0],
                    "return_away_name2": away_team_list[date_and_team[1][1]],
                    "return_game_date3": date_and_team[2][0],
                    "return_away_name3": away_team_list[date_and_team[2][1]],
                },
            }

나머지 리그와 팀의 경우, NUGU를 통해 들어온 한국어로 된 리그나 팀명을 team_and_teamid.json으로 매칭하여 teamid를 구하였다. 구한 team_id로 검색 가능한 thesportsdb api를 활용하여 3개의 앞으로의 일정을 구하였다.

      else:
            with open("configure_package/team_and_teamid.json") as other_team:
                other_team_list = json.load(other_team)
            team_id = other_team_list[request_team]

            other_team_schedule_url = (
                f"https://www.thesportsdb.com/api/v1/json/1/eventsnext.php?id={team_id}"
            )
            r = requests.get(other_team_schedule_url).text
            three_events = json.loads(r)["events"][2:5]

            response_builder = {
                "version": "2.0",
                "resultCode": "OK",
                "output": {
                    "our_team_name": request_team,
                    "return_game_date1": three_events[-1]["dateEvent"],
                    "return_away_name1": three_events[-1]["strAwayTeam"],
                    "return_game_date2": three_events[-2]["dateEvent"],
                    "return_away_name2": three_events[-2]["strAwayTeam"],
                    "return_game_date3": three_events[0]["dateEvent"],
                    "return_away_name3": three_events[0]["strAwayTeam"],
                },
            }

        return Response(response_builder)

4. League_Scheduler

처음에는 MLB 야구팀의 일정만 추가하려했지만, 더 많은 스포츠를 담기 위해 리그와 팀을 추가하였다. 검색할 수 있는 리그의 수는 137개로 해당 리그의 알 수 있는 모든 일정검색 기능을 추가하였다. 나중에 코드를 추가하였기 때문에 if문으로 기존의 야구팀의 일정 검색과 나머지 리그,팀의 일정 검색을 나누었다. 먼저, NUGU로 ‘league_name’을 받을 수 있게 request를 해주었다.

    def post(self, request):
        data = request.data["action"]
        request_league = data["parameters"]["league_name"]["value"]
        with open("configure_package/available_league.json") as league_id:
            league_list = json.load(league_id)

야구팀의 일정 검색의 경우, Next_Game과 비슷한 로직을 사용하였다. 팀의 한글 이름을 NUGU를 통해 받고, configure_package에 저장되어 있는mlb_i19n_team_list.json을 통해 해당 팀id를 추출하였다. output인 date_and_teams 배열을 길게하여 모든 일정을 소화하고 싶었지만, NUGU의 발화시간을 고려하여 다음 3개의 경기만 배열에 저장하도록 설정하였다.

        league_id = league_list(request_league)
        other_league_schedule_url = f"https://www.thesportsdb.com/api/v1/json/1/eventsnextleague.php?id={league_id}"
        r = requests.get(other_league_schedule_url).text
        three_events = json.loads(r)["events"][2:5]
        response_builder = {
            "version": "2.0",
            "resultCode": "OK",
            "output": {
                "our_league_name": request_league,
                "return_league_date1": three_events[-1]["dateEvent"],
                "return_homename1": three_events[-1]["strHomeTeam"],
                "return_awayname1": three_events[-1]["strAwayTeam"],
                "return_league_date2": three_events[-2]["dateEvent"],
                "return_homename2": three_events[-2]["strHomeTeam"],
                "return_awayname2": three_events[-2]["strAwayTeam"],
            },
        }
        return Response(response_builder)

나머지 리그와 팀의 경우, NUGU를 통해 들어온 한국어로 된 리그나 팀명을 league_and_leagueid.json으로 매칭하여 leagueid를 구하였다. 구한 league_id로 검색 가능한 thesportsdb api를 활용하여 3개의 앞으로의 일정을 구하였다.

      else:
            with open("configure_package/team_and_teamid.json") as other_team:
                other_team_list = json.load(other_team)
            team_id = other_team_list[request_team]

            other_team_schedule_url = (
                f"https://www.thesportsdb.com/api/v1/json/1/eventsnext.php?id={team_id}"
            )
            r = requests.get(other_team_schedule_url).text
            three_events = json.loads(r)["events"][2:5]

            response_builder = {
                "version": "2.0",
                "resultCode": "OK",
                "output": {
                    "our_team_name": request_team,
                    "return_game_date1": three_events[-1]["dateEvent"],
                    "return_away_name1": three_events[-1]["strAwayTeam"],
                    "return_game_date2": three_events[-2]["dateEvent"],
                    "return_away_name2": three_events[-2]["strAwayTeam"],
                    "return_game_date3": three_events[0]["dateEvent"],
                    "return_away_name3": three_events[0]["strAwayTeam"],
                },
            }

        return Response(response_builder)

5. Terminology

야구 용어 중 어렵고 전문적인 것들이 많기 때문에 사용자가 용어에 대해 질문하면 이에 대해 대답해 주는 기능을 추가하였다.