크롤링에 마케팅 분석 추가, 태그 추가, 부대 시설 추가 완료
parent
79ec5daa0d
commit
b5b8e88005
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
@ -19,8 +20,10 @@ from app.home.schemas.home import (
|
||||||
GenerateResponse,
|
GenerateResponse,
|
||||||
GenerateUploadResponse,
|
GenerateUploadResponse,
|
||||||
GenerateUrlsRequest,
|
GenerateUrlsRequest,
|
||||||
|
MarketingAnalysis,
|
||||||
ProcessedInfo,
|
ProcessedInfo,
|
||||||
)
|
)
|
||||||
|
from app.utils.chatgpt_prompt import ChatgptService
|
||||||
from app.home.worker.main_task import task_process
|
from app.home.worker.main_task import task_process
|
||||||
from app.utils.nvMapScraper import NvMapScraper
|
from app.utils.nvMapScraper import NvMapScraper
|
||||||
|
|
||||||
|
|
@ -68,6 +71,32 @@ def _extract_region_from_address(road_address: str | None) -> str:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_marketing_analysis(raw_response: str) -> MarketingAnalysis:
|
||||||
|
"""ChatGPT 마케팅 분석 응답을 파싱하여 MarketingAnalysis 객체로 변환"""
|
||||||
|
tags: list[str] = []
|
||||||
|
facilities: list[str] = []
|
||||||
|
report = raw_response
|
||||||
|
|
||||||
|
# JSON 블록 추출 시도
|
||||||
|
json_match = re.search(r"```json\s*(\{.*?\})\s*```", raw_response, re.DOTALL)
|
||||||
|
if json_match:
|
||||||
|
try:
|
||||||
|
json_data = json.loads(json_match.group(1))
|
||||||
|
tags = json_data.get("tags", [])
|
||||||
|
facilities = json_data.get("facilities", [])
|
||||||
|
# JSON 블록을 제외한 리포트 부분 추출
|
||||||
|
report = raw_response[: json_match.start()].strip()
|
||||||
|
# --- 구분자 제거
|
||||||
|
if report.startswith("---"):
|
||||||
|
report = report[3:].strip()
|
||||||
|
if report.endswith("---"):
|
||||||
|
report = report[:-3].strip()
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return MarketingAnalysis(report=report, tags=tags, facilities=facilities)
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/crawling",
|
"/crawling",
|
||||||
summary="네이버 지도 크롤링",
|
summary="네이버 지도 크롤링",
|
||||||
|
|
@ -100,18 +129,34 @@ async def crawling(request_body: CrawlingRequest):
|
||||||
|
|
||||||
# 가공된 정보 생성
|
# 가공된 정보 생성
|
||||||
processed_info = None
|
processed_info = None
|
||||||
|
marketing_analysis = None
|
||||||
|
|
||||||
if scraper.base_info:
|
if scraper.base_info:
|
||||||
road_address = scraper.base_info.get("roadAddress", "")
|
road_address = scraper.base_info.get("roadAddress", "")
|
||||||
|
customer_name = scraper.base_info.get("name", "")
|
||||||
|
region = _extract_region_from_address(road_address)
|
||||||
|
|
||||||
processed_info = ProcessedInfo(
|
processed_info = ProcessedInfo(
|
||||||
customer_name=scraper.base_info.get("name", ""),
|
customer_name=customer_name,
|
||||||
region=_extract_region_from_address(road_address),
|
region=region,
|
||||||
detail_region_info=road_address or "",
|
detail_region_info=road_address or "",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ChatGPT를 이용한 마케팅 분석
|
||||||
|
chatgpt_service = ChatgptService(
|
||||||
|
customer_name=customer_name,
|
||||||
|
region=region,
|
||||||
|
detail_region_info=road_address or "",
|
||||||
|
)
|
||||||
|
prompt = chatgpt_service.build_market_analysis_prompt()
|
||||||
|
raw_response = await chatgpt_service.generate(prompt)
|
||||||
|
marketing_analysis = _parse_marketing_analysis(raw_response)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"image_list": scraper.image_link_list,
|
"image_list": scraper.image_link_list,
|
||||||
"image_count": len(scraper.image_link_list) if scraper.image_link_list else 0,
|
"image_count": len(scraper.image_link_list) if scraper.image_link_list else 0,
|
||||||
"processed_info": processed_info,
|
"processed_info": processed_info,
|
||||||
|
"marketing_analysis": marketing_analysis,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,14 @@ class ProcessedInfo(BaseModel):
|
||||||
detail_region_info: str = Field(..., description="상세 지역 정보 (roadAddress)")
|
detail_region_info: str = Field(..., description="상세 지역 정보 (roadAddress)")
|
||||||
|
|
||||||
|
|
||||||
|
class MarketingAnalysis(BaseModel):
|
||||||
|
"""마케팅 분석 결과 스키마"""
|
||||||
|
|
||||||
|
report: str = Field(..., description="마케팅 분석 리포트")
|
||||||
|
tags: list[str] = Field(default_factory=list, description="추천 태그 목록")
|
||||||
|
facilities: list[str] = Field(default_factory=list, description="추천 부대시설 목록")
|
||||||
|
|
||||||
|
|
||||||
class CrawlingResponse(BaseModel):
|
class CrawlingResponse(BaseModel):
|
||||||
"""크롤링 응답 스키마"""
|
"""크롤링 응답 스키마"""
|
||||||
|
|
||||||
|
|
@ -133,6 +141,9 @@ class CrawlingResponse(BaseModel):
|
||||||
processed_info: Optional[ProcessedInfo] = Field(
|
processed_info: Optional[ProcessedInfo] = Field(
|
||||||
None, description="가공된 장소 정보 (customer_name, region, detail_region_info)"
|
None, description="가공된 장소 정보 (customer_name, region, detail_region_info)"
|
||||||
)
|
)
|
||||||
|
marketing_analysis: Optional[MarketingAnalysis] = Field(
|
||||||
|
None, description="마케팅 분석 결과 (report, tags, facilities)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ErrorResponse(BaseModel):
|
class ErrorResponse(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ async def lyric_task(
|
||||||
lyric_id = await _save_lyric(task_id, project_id, lyric_prompt)
|
lyric_id = await _save_lyric(task_id, project_id, lyric_prompt)
|
||||||
|
|
||||||
# GPT 호출
|
# GPT 호출
|
||||||
result = await service.generate_lyrics(prompt=lyric_prompt)
|
result = await service.generate(prompt=lyric_prompt)
|
||||||
|
|
||||||
print(f"GPT Response:\n{result}")
|
print(f"GPT Response:\n{result}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ async def make_song_result(request: Request, conn: Connection):
|
||||||
print(f"\n[업데이트된 프롬프트]\n{updated_prompt}\n")
|
print(f"\n[업데이트된 프롬프트]\n{updated_prompt}\n")
|
||||||
|
|
||||||
# 7. 모델에게 요청
|
# 7. 모델에게 요청
|
||||||
generated_lyrics = await chatgpt_api.generate_lyrics(prompt=updated_prompt)
|
generated_lyrics = await chatgpt_api.generate(prompt=updated_prompt)
|
||||||
|
|
||||||
# 글자 수 계산
|
# 글자 수 계산
|
||||||
total_chars_with_space = len(generated_lyrics)
|
total_chars_with_space = len(generated_lyrics)
|
||||||
|
|
@ -733,7 +733,7 @@ async def make_automation(request: Request, conn: Connection):
|
||||||
print(f"\n[최종 프롬프트 길이: {len(updated_prompt)} 자]\n")
|
print(f"\n[최종 프롬프트 길이: {len(updated_prompt)} 자]\n")
|
||||||
|
|
||||||
# 7. 모델에게 요청
|
# 7. 모델에게 요청
|
||||||
generated_lyrics = await chatgpt_api.generate_lyrics(prompt=updated_prompt)
|
generated_lyrics = await chatgpt_api.generate(prompt=updated_prompt)
|
||||||
|
|
||||||
# 글자 수 계산
|
# 글자 수 계산
|
||||||
total_chars_with_space = len(generated_lyrics)
|
total_chars_with_space = len(generated_lyrics)
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ async def make_song_result(request: Request, conn: Connection):
|
||||||
print(f"\n[업데이트된 프롬프트]\n{updated_prompt}\n")
|
print(f"\n[업데이트된 프롬프트]\n{updated_prompt}\n")
|
||||||
|
|
||||||
# 7. 모델에게 요청
|
# 7. 모델에게 요청
|
||||||
generated_lyrics = await chatgpt_api.generate_lyrics(prompt=updated_prompt)
|
generated_lyrics = await chatgpt_api.generate(prompt=updated_prompt)
|
||||||
|
|
||||||
# 글자 수 계산
|
# 글자 수 계산
|
||||||
total_chars_with_space = len(generated_lyrics)
|
total_chars_with_space = len(generated_lyrics)
|
||||||
|
|
@ -733,7 +733,7 @@ async def make_automation(request: Request, conn: Connection):
|
||||||
print(f"\n[최종 프롬프트 길이: {len(updated_prompt)} 자]\n")
|
print(f"\n[최종 프롬프트 길이: {len(updated_prompt)} 자]\n")
|
||||||
|
|
||||||
# 7. 모델에게 요청
|
# 7. 모델에게 요청
|
||||||
generated_lyrics = await chatgpt_api.generate_lyrics(prompt=updated_prompt)
|
generated_lyrics = await chatgpt_api.generate(prompt=updated_prompt)
|
||||||
|
|
||||||
# 글자 수 계산
|
# 글자 수 계산
|
||||||
total_chars_with_space = len(generated_lyrics)
|
total_chars_with_space = len(generated_lyrics)
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,99 @@ ERROR: [Brief reason for failure in English]
|
||||||
""".strip()
|
""".strip()
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
MARKETING_ANALYSIS_PROMPT_TEMPLATE = """
|
||||||
|
[ROLE]
|
||||||
|
Content marketing expert specializing in pension/accommodation services in Korea
|
||||||
|
|
||||||
|
[INPUT]
|
||||||
|
- Business Name: {customer_name}
|
||||||
|
- Region: {region}
|
||||||
|
- Region Details: {detail_region_info}
|
||||||
|
|
||||||
|
[ANALYSIS REQUIREMENTS]
|
||||||
|
Provide comprehensive marketing analysis including:
|
||||||
|
1. Target Customer Segments
|
||||||
|
- Primary and secondary target personas
|
||||||
|
- Age groups, travel preferences, booking patterns
|
||||||
|
2. Unique Selling Propositions (USPs)
|
||||||
|
- Key differentiators based on location and region details
|
||||||
|
- Competitive advantages
|
||||||
|
3. Regional Characteristics
|
||||||
|
- Nearby attractions and famous places (within 10 min access)
|
||||||
|
- Local food, activities, and experiences
|
||||||
|
- Transportation accessibility
|
||||||
|
4. Seasonal Appeal Points
|
||||||
|
- Best seasons to visit
|
||||||
|
- Seasonal activities and events
|
||||||
|
- Peak/off-peak marketing opportunities
|
||||||
|
5. Marketing Keywords
|
||||||
|
- Recommended hashtags and search keywords
|
||||||
|
- Trending terms relevant to the property
|
||||||
|
|
||||||
|
[ADDITIONAL REQUIREMENTS]
|
||||||
|
1. Recommended Tags
|
||||||
|
- Generate 5 recommended hashtags/tags based on the business characteristics
|
||||||
|
- Tags should be trendy, searchable, and relevant to accommodation marketing
|
||||||
|
- Return as JSON with key "tags"
|
||||||
|
- **MUST be written in Korean (한국어)**
|
||||||
|
|
||||||
|
2. Facilities
|
||||||
|
- Based on the business name and region details, identify 5 likely facilities/amenities
|
||||||
|
- Consider typical facilities for accommodations in the given region
|
||||||
|
- Examples: 바베큐장, 수영장, 주차장, 와이파이, 주방, 테라스, 정원, etc.
|
||||||
|
- Return as JSON with key "facilities"
|
||||||
|
- **MUST be written in Korean (한국어)**
|
||||||
|
|
||||||
|
[CRITICAL LANGUAGE REQUIREMENT - ABSOLUTE RULE]
|
||||||
|
ALL OUTPUT MUST BE WRITTEN IN KOREAN (한국어)
|
||||||
|
- Analysis sections: Korean only
|
||||||
|
- Tags: Korean only
|
||||||
|
- Facilities: Korean only
|
||||||
|
- This is a NON-NEGOTIABLE requirement
|
||||||
|
- Any output in English or other languages is considered a FAILURE
|
||||||
|
- Violation of this rule invalidates the entire response
|
||||||
|
|
||||||
|
[OUTPUT RULES - STRICTLY ENFORCED]
|
||||||
|
- Output analysis ONLY
|
||||||
|
- ALL content MUST be written in Korean (한국어) - NO EXCEPTIONS
|
||||||
|
- NO greetings or closing remarks
|
||||||
|
- NO additional commentary before or after analysis
|
||||||
|
- Follow the exact format below
|
||||||
|
|
||||||
|
[OUTPUT FORMAT - SUCCESS]
|
||||||
|
---
|
||||||
|
## 타겟 고객 분석
|
||||||
|
[한국어로 작성된 타겟 고객 분석]
|
||||||
|
|
||||||
|
## 핵심 차별점 (USP)
|
||||||
|
[한국어로 작성된 USP 분석]
|
||||||
|
|
||||||
|
## 지역 특성
|
||||||
|
[한국어로 작성된 지역 특성 분석]
|
||||||
|
|
||||||
|
## 시즌별 매력 포인트
|
||||||
|
[한국어로 작성된 시즌별 분석]
|
||||||
|
|
||||||
|
## 마케팅 키워드
|
||||||
|
[한국어로 작성된 마케팅 키워드]
|
||||||
|
|
||||||
|
## JSON Data
|
||||||
|
```json
|
||||||
|
{{
|
||||||
|
"tags": ["태그1", "태그2", "태그3", "태그4", "태그5"],
|
||||||
|
"facilities": ["부대시설1", "부대시설2", "부대시설3", "부대시설4", "부대시설5"]
|
||||||
|
}}
|
||||||
|
```
|
||||||
|
---
|
||||||
|
|
||||||
|
[OUTPUT FORMAT - FAILURE]
|
||||||
|
If you cannot generate analysis due to insufficient information, invalid input, or any other reason:
|
||||||
|
---
|
||||||
|
ERROR: [Brief reason for failure in English]
|
||||||
|
---
|
||||||
|
""".strip()
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
|
||||||
class ChatgptService:
|
class ChatgptService:
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -105,7 +198,15 @@ class ChatgptService:
|
||||||
detail_region_info=self.detail_region_info,
|
detail_region_info=self.detail_region_info,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def generate_lyrics(self, prompt: str | None = None) -> str:
|
def build_market_analysis_prompt(self) -> str:
|
||||||
|
"""MARKETING_ANALYSIS_PROMPT_TEMPLATE에 고객 정보를 대입하여 완성된 프롬프트 반환"""
|
||||||
|
return MARKETING_ANALYSIS_PROMPT_TEMPLATE.format(
|
||||||
|
customer_name=self.customer_name,
|
||||||
|
region=self.region,
|
||||||
|
detail_region_info=self.detail_region_info,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def generate(self, prompt: str | None = None) -> str:
|
||||||
"""GPT에게 프롬프트를 전달하여 결과를 반환"""
|
"""GPT에게 프롬프트를 전달하여 결과를 반환"""
|
||||||
if prompt is None:
|
if prompt is None:
|
||||||
prompt = self.build_lyrics_prompt()
|
prompt = self.build_lyrics_prompt()
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,8 @@ query getAccommodation($id: String!, $deviceType: String) {
|
||||||
|
|
||||||
|
|
||||||
# if __name__ == "__main__":
|
# if __name__ == "__main__":
|
||||||
|
# import asyncio
|
||||||
|
|
||||||
# url = "https://map.naver.com/p/search/%EC%8A%A4%ED%85%8C%EC%9D%B4%EB%A8%B8%EB%AD%84/place/1133638931?c=14.70,0,0,0,dh&placePath=/photo?businessCategory=pension&fromPanelNum=2&locale=ko&searchText=%EC%8A%A4%ED%85%8C%EC%9D%B4%EB%A8%B8%EB%AD%84&svcName=map_pcv5×tamp=202512191123&fromPanelNum=2&locale=ko&searchText=%EC%8A%A4%ED%85%8C%EC%9D%B4%EB%A8%B8%EB%AD%84&svcName=map_pcv5×tamp=202512191007&from=map&entry=bmp&filterType=%EC%97%85%EC%B2%B4&businessCategory=pension"
|
# url = "https://map.naver.com/p/search/%EC%8A%A4%ED%85%8C%EC%9D%B4%EB%A8%B8%EB%AD%84/place/1133638931?c=14.70,0,0,0,dh&placePath=/photo?businessCategory=pension&fromPanelNum=2&locale=ko&searchText=%EC%8A%A4%ED%85%8C%EC%9D%B4%EB%A8%B8%EB%AD%84&svcName=map_pcv5×tamp=202512191123&fromPanelNum=2&locale=ko&searchText=%EC%8A%A4%ED%85%8C%EC%9D%B4%EB%A8%B8%EB%AD%84&svcName=map_pcv5×tamp=202512191007&from=map&entry=bmp&filterType=%EC%97%85%EC%B2%B4&businessCategory=pension"
|
||||||
# scraper = NvMapScraper(url)
|
# scraper = NvMapScraper(url)
|
||||||
# asyncio.run(scraper.scrap())
|
# asyncio.run(scraper.scrap())
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import requests
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
SAS_TOKEN = "sp=racwdl&st=2025-12-01T00:13:29Z&se=2026-07-31T08:28:29Z&spr=https&sv=2024-11-04&sr=c&sig=7fE2ozVBPu3Gq43%2FZDxEYdEcPLDXyNVfTf16IBasmVQ%3D"
|
||||||
|
|
||||||
|
def upload_music_to_azure_blob(file_path = "스테이 머뭄_1.mp3", url = "https://ado2mediastoragepublic.blob.core.windows.net/ado2-media-public-access/ado2-media-original/dev-user-idx/dev-task-idx/test_my_mp3_should_now_exist.mp3"):
|
||||||
|
access_url = f"{url}?{SAS_TOKEN}"
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "audio/mpeg",
|
||||||
|
"x-ms-blob-type": "BlockBlob"
|
||||||
|
}
|
||||||
|
with open(file_path, "rb") as file:
|
||||||
|
response = requests.put(access_url, data=file, headers=headers)
|
||||||
|
if response.status_code in [200, 201]:
|
||||||
|
print(f"Success Status Code: {response.status_code}")
|
||||||
|
else:
|
||||||
|
print(f"Failed Status Code: {response.status_code}")
|
||||||
|
print(f"Response: {response.text}")
|
||||||
|
|
||||||
|
def upload_video_to_azure_blob(file_path = "스테이 머뭄.mp4", url = "https://ado2mediastoragepublic.blob.core.windows.net/ado2-media-public-access/ado2-media-original/dev-user-idx/dev-task-idx/test_my_mp3_should_now_exist.mp4"):
|
||||||
|
access_url = f"{url}?{SAS_TOKEN}"
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "video/mp4",
|
||||||
|
"x-ms-blob-type": "BlockBlob"
|
||||||
|
}
|
||||||
|
with open(file_path, "rb") as file:
|
||||||
|
response = requests.put(access_url, data=file, headers=headers)
|
||||||
|
|
||||||
|
if response.status_code in [200, 201]:
|
||||||
|
print(f"Success Status Code: {response.status_code}")
|
||||||
|
else:
|
||||||
|
print(f"Failed Status Code: {response.status_code}")
|
||||||
|
print(f"Response: {response.text}")
|
||||||
|
|
||||||
|
|
||||||
|
def upload_image_to_azure_blob(file_path = "스테이 머뭄.png", url = "https://ado2mediastoragepublic.blob.core.windows.net/ado2-media-public-access/ado2-media-original/dev-user-idx/dev-task-idx/test_my_mp3_should_now_exist.png"):
|
||||||
|
access_url = f"{url}?{SAS_TOKEN}"
|
||||||
|
extension = Path(file_path).suffix.lower()
|
||||||
|
content_types = {
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".png": "image/png",
|
||||||
|
".gif": "image/gif",
|
||||||
|
".webp": "image/webp",
|
||||||
|
".bmp": "image/bmp"
|
||||||
|
}
|
||||||
|
content_type = content_types.get(extension, "image/jpeg")
|
||||||
|
headers = {
|
||||||
|
"Content-Type": content_type,
|
||||||
|
"x-ms-blob-type": "BlockBlob"
|
||||||
|
}
|
||||||
|
with open(file_path, "rb") as file:
|
||||||
|
response = requests.put(access_url, data=file, headers=headers)
|
||||||
|
|
||||||
|
if response.status_code in [200, 201]:
|
||||||
|
print(f"Success Status Code: {response.status_code}")
|
||||||
|
else:
|
||||||
|
print(f"Failed Status Code: {response.status_code}")
|
||||||
|
print(f"Response: {response.text}")
|
||||||
|
|
||||||
|
|
||||||
|
upload_video_to_azure_blob()
|
||||||
|
|
||||||
|
upload_image_to_azure_blob()
|
||||||
|
|
@ -332,7 +332,7 @@ async def make_song_result(request: Request, conn: Connection):
|
||||||
print(f"\n[업데이트된 프롬프트]\n{updated_prompt}\n")
|
print(f"\n[업데이트된 프롬프트]\n{updated_prompt}\n")
|
||||||
|
|
||||||
# 7. 모델에게 요청
|
# 7. 모델에게 요청
|
||||||
generated_lyrics = await chatgpt_api.generate_lyrics(prompt=updated_prompt)
|
generated_lyrics = await chatgpt_api.generate(prompt=updated_prompt)
|
||||||
|
|
||||||
# 글자 수 계산
|
# 글자 수 계산
|
||||||
total_chars_with_space = len(generated_lyrics)
|
total_chars_with_space = len(generated_lyrics)
|
||||||
|
|
@ -733,7 +733,7 @@ async def make_automation(request: Request, conn: Connection):
|
||||||
print(f"\n[최종 프롬프트 길이: {len(updated_prompt)} 자]\n")
|
print(f"\n[최종 프롬프트 길이: {len(updated_prompt)} 자]\n")
|
||||||
|
|
||||||
# 7. 모델에게 요청
|
# 7. 모델에게 요청
|
||||||
generated_lyrics = await chatgpt_api.generate_lyrics(prompt=updated_prompt)
|
generated_lyrics = await chatgpt_api.generate(prompt=updated_prompt)
|
||||||
|
|
||||||
# 글자 수 계산
|
# 글자 수 계산
|
||||||
total_chars_with_space = len(generated_lyrics)
|
total_chars_with_space = len(generated_lyrics)
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ app/home/tests/home/__init__.py
|
||||||
app/home/tests/home/conftest.py
|
app/home/tests/home/conftest.py
|
||||||
app/home/tests/home/test_db.py
|
app/home/tests/home/test_db.py
|
||||||
app/home/worker/__init__.py
|
app/home/worker/__init__.py
|
||||||
|
app/home/worker/main_task.py
|
||||||
app/lyric/__init__.py
|
app/lyric/__init__.py
|
||||||
app/lyric/dependencies.py
|
app/lyric/dependencies.py
|
||||||
app/lyric/models.py
|
app/lyric/models.py
|
||||||
|
|
@ -44,8 +45,9 @@ app/lyric/api/__init__.py
|
||||||
app/lyric/api/lyrics_admin.py
|
app/lyric/api/lyrics_admin.py
|
||||||
app/lyric/api/routers/__init__.py
|
app/lyric/api/routers/__init__.py
|
||||||
app/lyric/api/routers/v1/__init__.py
|
app/lyric/api/routers/v1/__init__.py
|
||||||
app/lyric/api/routers/v1/router.py
|
app/lyric/api/routers/v1/lyric.py
|
||||||
app/lyric/schemas/__init__.py
|
app/lyric/schemas/__init__.py
|
||||||
|
app/lyric/schemas/lyric.py
|
||||||
app/lyric/schemas/lyrics_schema.py
|
app/lyric/schemas/lyrics_schema.py
|
||||||
app/lyric/services/__init__.py
|
app/lyric/services/__init__.py
|
||||||
app/lyric/services/base.py
|
app/lyric/services/base.py
|
||||||
|
|
@ -77,6 +79,7 @@ app/utils/__init__.py
|
||||||
app/utils/chatgpt_prompt.py
|
app/utils/chatgpt_prompt.py
|
||||||
app/utils/cors.py
|
app/utils/cors.py
|
||||||
app/utils/nvMapScraper.py
|
app/utils/nvMapScraper.py
|
||||||
|
app/utils/upload_blob_as_request.py
|
||||||
app/video/__init__.py
|
app/video/__init__.py
|
||||||
app/video/dependencies.py
|
app/video/dependencies.py
|
||||||
app/video/models.py
|
app/video/models.py
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue