1- import redis , random , os , json , re
1+ import redis , random , os , json , re , tempfile
22import openai
3+ from openai import OpenAI
4+ from pathlib import Path
35from django .conf import settings
46from django .core .files import File
57from django .shortcuts import render
68from rest_framework .views import APIView
79from rest_framework .response import Response
810from rest_framework .permissions import IsAuthenticated
11+ from rest_framework .parsers import MultiPartParser , FormParser
912
1013from .models import *
1114from .serializers import *
2831
2932load_dotenv (settings .BASE_DIR / ".env" )
3033# openai.api_key = os.getenv("OPENAI_API_KEY")
34+ OPENAI_API_KEY = os .getenv ("OPENAI_API_KEY" )
35+ client = OpenAI (api_key = OPENAI_API_KEY )
3136
3237User = get_user_model ()
3338
@@ -57,20 +62,89 @@ def post(self, request):
5762 port = getattr (settings , "REDIS_PORT" , 6379 ),
5863 db = 0 ,
5964 decode_responses = True ,
60- ssl = True ,
65+ ssl = False ,
6166 )
6267 redis_client .hset (f"story_option:{ request .user .id } " , mapping = {"runtime" : runtime , "age_group" : age_group })
6368
6469 return Response ({"next" : "/story/record/" }, status = status .HTTP_200_OK )
65-
70+
71+ def append_draft (user_id , new_text ):
72+ redis_client = redis .StrictRedis (
73+ host = getattr (settings , "REDIS_HOST" , "localhost" ),
74+ port = getattr (settings , "REDIS_PORT" , 6379 ),
75+ db = 0 ,
76+ decode_responses = True ,
77+ ssl = False ,
78+ )
79+
80+ key = f"story_draft:{ user_id } "
81+ existing = redis_client .get (key ) or ""
82+
83+ # 기존 draft 뒤에 자연스럽게 이어 붙이기
84+ if existing :
85+ updated = existing .rstrip () + " " + new_text .strip ()
86+ else :
87+ updated = new_text .strip ()
88+
89+ redis_client .set (key , updated )
90+ return updated
91+
92+ class StoryDraftAudioAppendView (APIView ):
93+ permission_classes = [IsAuthenticated ]
94+ parser_classes = [MultiPartParser , FormParser ]
95+
96+ def post (self , request ):
97+ audio = request .FILES .get ("audio" )
98+ if not audio :
99+ return Response ({"error" : "audio 파일이 필요합니다." }, status = 400 )
100+
101+ # 1) 파일 임시 저장
102+ suffix = Path (audio .name ).suffix or ".mp3"
103+ with tempfile .NamedTemporaryFile (suffix = suffix , delete = False ) as tmp :
104+ for chunk in audio .chunks ():
105+ tmp .write (chunk )
106+ tmp_path = tmp .name
107+
108+ # 2) Whisper-1 호출
109+ try :
110+ with open (tmp_path , "rb" ) as f :
111+ transcript = client .audio .transcriptions .create (
112+ model = "whisper-1" ,
113+ file = f
114+ )
115+ text = transcript .text .strip ()
116+ except Exception as e :
117+ return Response ({"error" : f"STT 실패: { str (e )} " }, status = 500 )
118+
119+ # 3) Redis draft에 append
120+ redis_client = redis .StrictRedis (
121+ host = getattr (settings , "REDIS_HOST" , "localhost" ),
122+ port = getattr (settings , "REDIS_PORT" , 6379 ),
123+ db = 0 ,
124+ decode_responses = True ,
125+ )
126+
127+ key = f"story_draft:{ request .user .id } "
128+ existing = redis_client .get (key ) or ""
129+
130+ updated = (existing + " " + text ).strip ()
131+
132+ redis_client .set (key , updated )
133+
134+ return Response ({
135+ "message" : "음성이 변환되어 draft에 추가되었습니다." ,
136+ "recognized_text" : text ,
137+ "draft" : updated
138+ }, status = 200 )
139+
66140class StoryDraftUpdateView (APIView ):
67141 """
68142 사용자가 텍스트 입력으로 최종 입력한 내용을 Redis에 저장하는 API
69143 POST /api/story/draft/
70144 """
71145 permission_classes = [IsAuthenticated ]
72146
73- def post (self , request ):
147+ ''' def post(self, request):
74148 text = request.data.get("text")
75149
76150 if not text:
@@ -85,7 +159,7 @@ def post(self, request):
85159 port=getattr(settings, "REDIS_PORT", 6379),
86160 db=0,
87161 decode_responses=True,
88- ssl = True ,
162+ ssl=False ,
89163 )
90164
91165 redis_key = f"story_draft:{request.user.id}"
@@ -96,7 +170,38 @@ def post(self, request):
96170 return Response(
97171 {"message": "draft 업데이트 완료되었습니다."},
98172 status=200
173+ )'''
174+
175+ def put (self , request ):
176+ text = request .data .get ("text" , "" ).strip ()
177+
178+ if text is None :
179+ return Response ({"error" : "text는 필수 항목입니다." }, status = 400 )
180+
181+ redis_client = redis .StrictRedis (
182+ host = getattr (settings , "REDIS_HOST" , "localhost" ),
183+ port = getattr (settings , "REDIS_PORT" , 6379 ),
184+ db = 0 ,
185+ decode_responses = True ,
186+ )
187+
188+ redis_client .set (f"story_draft:{ request .user .id } " , text )
189+
190+ return Response ({
191+ "message" : "Draft 전체가 저장되었습니다." ,
192+ "draft" : text
193+ }, status = 200 )
194+
195+ def get (self , request ):
196+ redis_client = redis .StrictRedis (
197+ host = getattr (settings , "REDIS_HOST" , "localhost" ),
198+ port = getattr (settings , "REDIS_PORT" , 6379 ),
199+ db = 0 ,
200+ decode_responses = True ,
201+ ssl = False ,
99202 )
203+ draft = redis_client .get (f"story_draft:{ request .user .id } " ) or ""
204+ return Response ({"draft" : draft }, status = 200 )
100205
101206
102207DEFAULT_MORALS = [
@@ -239,7 +344,7 @@ def post(self, request):
239344 port = getattr (settings , "REDIS_PORT" , 6379 ),
240345 db = 0 ,
241346 decode_responses = True ,
242- ssl = True ,
347+ ssl = False ,
243348 )
244349 user_id = request .user .id
245350 redis_key = f"story_morals:{ user_id } "
@@ -282,7 +387,7 @@ def post(self, request):
282387 port = getattr (settings , "REDIS_PORT" , 6379 ),
283388 db = 0 ,
284389 decode_responses = True ,
285- ssl = True ,
390+ ssl = False ,
286391 )
287392
288393 user_id = request .user .id
@@ -393,7 +498,7 @@ def post(self, request):
393498 port = getattr (settings , "REDIS_PORT" , 6379 ),
394499 db = 0 ,
395500 decode_responses = True ,
396- ssl = True ,
501+ ssl = False ,
397502 )
398503
399504 keys = [
@@ -839,7 +944,7 @@ def get(self, request):
839944 }
840945
841946 return Response (response , status = 200 )
842-
947+
843948class StoryNDWReportView (APIView ):
844949 permission_classes = [IsAuthenticated ]
845950
@@ -915,4 +1020,4 @@ def post(self, request):
9151020 "result" : result ,
9161021 "rationale" : rationale ,
9171022 "report" : report
918- })
1023+ })
0 commit comments