개발했던 봇의 줄 서는 기능을 보완하는 느낌으로 결과를 DB처럼 저장하면 괜찮겠다는 생각이 떠올랐다.
그렇다고 해서 DB까지 갈 필요까진 없는 일이라서 csv 형식으로 결과를 저장하고자 했다.
Pandas
파이썬에서 기본으로 제공하는 csv 도구를 이용해도 되지만, Pandas가 정말 많이 쓰인다고 해서 이걸 사용하기로 했다.
나에게 필요한 기능은 다음과 같다.
- csv 파일을 읽는다.
없으면 템플릿대로 생성 - 줄의 결과가 나온다.
- 템플릿에 맞게 데이터프레임에 추가.
- csv에 데이터프레임 저장
난 읽기/쓰기의 기능만을 수행할 클래스인 pdCsv를 만들었다.
def load_csv(guild_id):
pdCsv.addr = f'./results/{guild_id}.csv'
if(not os.path.exists('./results')):
os.mkdir('results')
if(not os.path.exists(pdCsv.addr)):
dictTemp = {
'#': [],
'품목': [],
'시작 시간': [],
'마감 시간': [],
'줄 세운 사람': [],
'주작 결과': []
}
dfTemp = pd.DataFrame(dictTemp)
dfTemp.to_csv(pdCsv.addr, encoding='utf-8-sig')
pdCsv.csv = pd.read_csv(pdCsv.addr, encoding='utf-8-sig', index_col = 0)
return 0
pdCsv.csv = pd.read_csv(pdCsv.addr, encoding='utf-8-sig', index_col = 0)
try:
last = pdCsv.csv.iat[pdCsv.csv.index[-1], 0]
except:
return 0
return last
그 아래에 load_csv()라는 함수를 만들었다. 인자로는 길드 ID를 받는데, 이는 줄의 결과를 서버 별로 저장하기 위함이다.
폴더가 없으면 만들고 폴더 안에 {guild_id}.csv가 없으면 템플릿에 따라 빈 csv 파일을 생성한다.
리턴값으로 현재의 마지막 인덱스 번호를 리턴한다. 빈 파일을 금방 생성했으면 바로 -0을 리턴.
여기에선 인덱스와는 별도로 '#'을 통해 순서를 기록할 것이다.
한글의 깨짐을 방지하기 위해 'utf-8-sig'로 인코딩하고, Pandas로 csv를 읽을 때 생기는 Unnamed 열을 방지하기 위해 'index_col = 0'을 추가했다.
만약 이미 파일이 있지만 비어있다면 'last = pdCsv.csv.iat[pdCsv.csv.index[-1], 0]'가 리눅스 환경에서 에러를 일으키므로 예외처리를 통해 예외 발생 시 0을 리턴하게 했다.
그 외엔 알아서 마지막 인덱스를 리턴한다.
저장은 매우 간단하다.
def save_csv(dict):
dfTemp = pd.DataFrame(dict)
pdCsv.csv = pd.concat([pdCsv.csv, dfTemp], ignore_index=True)
pdCsv.csv.to_csv(pdCsv.addr, encoding='utf-8-sig')
#print(pdCsv.csv.iat[pdCsv.csv.index[-1], 0])
딕셔너리를 받아서 데이터프레임으로 변환한다.
이를 concat()으로 합치는데, 'ignore_index=True'를 추가해 인덱스 꼬임을 방지한다.
Pandas에서의 인덱스는 일반적인 DB의 Primary Key처럼 중복이 없는 고유한 값이 아니기 때문에 인덱스가 겹칠 수 있어 조심해야 한다.
클래스 작성도 끝났으니 이제 봇에 적용해 보자.
from PandasCsv import pdCsv as pc
lastIdx = pc.load_csv(interaction.guild_id) + 1
if (isTaskEnd):
if (len(view.entryList) == 0):
# Temp Data
dictTemp = {
'#': [lastIdx],
'품목': [prize],
'시작 시간': [datetime.datetime.fromtimestamp(start_time)],
'마감 시간': [datetime.datetime.fromtimestamp(ts)],
'줄 세운 사람': [f'({interaction.user.id} / {interaction.user.display_name})'],
'주작 결과': ['No Entry']
#'주작 결과': [f'{winner[1]} / {winner[0]}']
}
pc.save_csv(dictTemp)
대충 이런 코드들이 추가된다.
작성한 Pandas 클래스가 있는 파일에서 클래스를 임포트 한다.
인덱스는 0부터 시작하므로 알아보기 좋게 1을 더해 마지막 인덱스 번호를 갖고 온다.
작업이 끝나면 임시 딕셔너리를 만든다.
시간은 유닉스가 아닌 사람이 보는 시간 형식을 기록할 수 있도록 했다.
세운 사람 및 당첨자 모두의 고유 ID와 닉네임을 기록한다.
역시 ID만 있으면 알아보기 어렵고, 닉네임만 있으면 나중에 그 사람이 닉변 했을 때 알아볼 수가 없다.
마지막으로 임시 딕셔너리를 데이터프레임화 하여 저장한다.
초단위까지는 굳이 기록할 필요가 없다고 생각했기 때문에 시간 함수에서 제공하는 출력을 그대로 사용했다.
데스크탑 상에서는 멀쩡했는데 리눅스 환경인 클라우드에 올리니 문제가 좀 있었기에 귀찮았던 부분이 있었다.
기록도 잘 되고 당장 눈에 보이는 에러가 없기 때문에 아마 '줄'에 관련한 개선은 이 정도가 아닐까 한다.
평소에 뭔가 활용 기회가 없었던 csv 파일이었기에 이번 기회에 활용해 봤는데, DB까지 쓸 필요가 없는 곳에서 상당히 효과적으로 활용할 수 있을 것이라는 생각이 들었다.
'Study > Python' 카테고리의 다른 글
[Python, AWS] csv에서 DynamoDB에로의 교체 (0) | 2023.05.10 |
---|---|
[Python] 로깅 클래스 추가 (0) | 2023.04.03 |
[Python] 봇에 Epoch Converter 추가 (0) | 2023.03.29 |
[Python] 디스코드 봇에 환율 확인 기능 추가 (0) | 2023.02.12 |
[Python] CLOUDTYPE으로 디스코드 봇 배포 (0) | 2023.01.17 |