Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Django CI/CD
name: Moodico Server

on:
workflow_dispatch:
Expand Down
2 changes: 1 addition & 1 deletion moodico/moodtest/color_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
def product_result_by_mood(mood):
try:
mood_zones_path = 'static/data/mood_zones.json'
products_path = 'static/data/all_products_hex_update_tempk=4_2_1_1.json'
products_path = 'static/data/all_products.json'

with open(mood_zones_path, 'r', encoding='utf-8') as f:
mood_zones = json.load(f)
Expand Down
123 changes: 123 additions & 0 deletions moodico/products/management/commands/advertise_scraper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import json
import os
from django.conf import settings
from django.core.management.base import BaseCommand
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager


class Command(BaseCommand):
help = "Scrape products and save JSON under STATIC_ROOT/data/advertise_products.json"

def handle(self, *args, **options):
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument(
'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
)

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=chrome_options
)

try:
url = "https://www.oliveyoung.co.kr/store/main/getBestList.do?dispCatNo=900000100100001&fltDispCatNo=10000010002&pageIdx=1&rowsPerPage=10"
driver.get(url)
wait = WebDriverWait(driver, 15)

# Wait for <ul class="cate_prd_list"> to appear (parent of products)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'ul.cate_prd_list')))
# Now get all <li class="flag">
products = driver.find_elements(By.CSS_SELECTOR, 'li.flag')

all_products = []
for item in products:
try:
prd_info = item.find_element(By.CSS_SELECTOR, 'div.prd_info')

# Main image and link
link_tag = prd_info.find_element(By.CSS_SELECTOR, 'a.prd_thumb')
product_url = link_tag.get_attribute('href')
img_tag = link_tag.find_element(By.TAG_NAME, 'img')
image_src = img_tag.get_attribute('src')
image_alt = img_tag.get_attribute('alt')

# Brand and product name
prd_name_box = prd_info.find_element(By.CSS_SELECTOR, 'div.prd_name')
brand_name = ''
product_name = ''
try:
brand_name = prd_name_box.find_element(By.CSS_SELECTOR, 'span.tx_brand').text.strip()
except Exception:
pass
try:
product_name = prd_name_box.find_element(By.CSS_SELECTOR, 'p.tx_name').text.strip()
except Exception:
pass

# Price
price_original = ''
price_current = ''
try:
price_box = prd_info.find_element(By.CSS_SELECTOR, 'p.prd_price')
price_original_elem = price_box.find_elements(By.CSS_SELECTOR, 'span.tx_org')
price_original = price_original_elem[0].text.strip() if price_original_elem else ''
price_current_elem = price_box.find_elements(By.CSS_SELECTOR, 'span.tx_cur')
price_current = price_current_elem.text.strip() if price_current_elem else ''
except Exception:
pass

# Flags
flags = []
try:
prd_flag_box = prd_info.find_element(By.CSS_SELECTOR, 'p.prd_flag')
flag_spans = prd_flag_box.find_elements(By.CSS_SELECTOR, 'span.icon_flag')
flags = [flag.text.strip() for flag in flag_spans]
except Exception:
pass

# Review score
review_score = None
try:
prd_point_box = prd_info.find_element(By.CSS_SELECTOR, 'p.prd_point_area')
review_point_elem = prd_point_box.find_elements(By.CSS_SELECTOR, 'span.review_point')
if review_point_elem:
score_text = review_point_elem[0].text.strip()
if '10점만점에' in score_text and '점' in score_text:
review_score = score_text.replace('10점만점에', '').replace('점', '').strip()
except Exception:
pass

product_data = {
'product_url': product_url,
'brand_name': brand_name,
'product_name': product_name,
'image_src': image_src,
'image_alt': image_alt,
'price_original': price_original,
'price_current': price_current,
'flags': flags,
'review_score': review_score
}

all_products.append(product_data)
except Exception as e:
self.stderr.write(f"Error scraping item: {e}")
continue

SAVE_PATH = os.path.join(settings.BASE_DIR, 'static', 'data', 'advertise_products.json')
os.makedirs(os.path.dirname(SAVE_PATH), exist_ok=True)
with open(SAVE_PATH, 'w', encoding='utf-8') as f:
json.dump(all_products, f, ensure_ascii=False, indent=2)
print(f"\nSaved {len(all_products)} products to {SAVE_PATH}")

finally:
driver.quit()
2 changes: 0 additions & 2 deletions moodico/products/management/commands/generate_clusters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler


# -------- helpers (kept as in your script) --------
def hex_to_rgb(hex_code):
h = hex_code.lstrip('#')
if len(h) == 3:
Expand Down
7 changes: 3 additions & 4 deletions moodico/products/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
# Create your views here.
def color_matrix_explore(request):
"""색상 매트릭스 페이지 뷰"""
media_cluster = os.path.join(settings.BASE_DIR, 'static','data', 'products_clustered.json')
static_cluster = os.path.join(settings.BASE_DIR, 'static','data', 'products_clustered.json')
static_all = os.path.join(settings.BASE_DIR, 'static', 'data', 'all_products.json')

product_path = None
if os.path.exists(media_cluster):
product_path = media_cluster
if os.path.exists(static_cluster):
product_path = static_cluster
else:
product_path = static_all

Expand Down Expand Up @@ -725,7 +725,6 @@ def get_top_liked_products(limit=10, include_unliked=True, exclude_brands=None):

# 3) 정렬
products_with_likes.sort(key=lambda x: (-x['like_count'], x['product_name']))

return products_with_likes[:limit]


Expand Down
57 changes: 45 additions & 12 deletions moodico/recommendation/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,55 @@
import logging
logger = logging.getLogger(__name__)
from sklearn.metrics.pairwise import cosine_similarity
from moodico.products.views import get_top_liked_products

# Create your views here.
def my_item_recommendation(request):
# Get recommended or default products
search_results = get_top_liked_products(limit=10)
recommended_items = [] # Set this if you want a separate recommended section

return render(
request,
'upload/upload.html',
# def my_item_recommendation(request):
# # Get recommended or default products
# search_results = get_top_liked_products(limit=10)
# recommended_items = [] # Set this if you want a separate recommended section
# print("....",search_results)

# return render(
# request,
# 'upload/upload.html',
# {
# 'search_results': search_results,
# 'recommended_items': recommended_items
# }
# )

def get_recommendation_list():
# JSON 데이터를 파싱 (실제로는 DB나 API에서 받아올 수 있음)
products_path = 'static/data/advertise_products.json'
with open(products_path, 'r', encoding='utf-8') as f:
raw_data = json.load(f)

# 태그 추출 규칙 예시 (첫번째 flag 사용 or None)
def get_tag(flags):
for tag in ['글로시', 'matte', 'glossy', '증정', '세일', '쿠폰', '오늘드림']:
if tag in flags:
return tag
return flags[0] if flags else '-'

search_results = [
{
'search_results': search_results,
'recommended_items': recommended_items
"brand": item["brand_name"],
"name": item["product_name"],
"image": item["image_src"],
"price": item["price_original"].replace("~", ""),
"tag": get_tag(item.get("flags", [])),
"url": item["product_url"],
}
)
for item in raw_data
]
return search_results

def my_item_recommendation(request):
search_results = get_recommendation_list()
return render(request, 'upload/upload.html', {
"search_results": search_results
})


@csrf_exempt
def recommend_by_color(request):
Expand Down
7 changes: 5 additions & 2 deletions moodico/upload/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import requests
from django.http import HttpResponse
from moodico.upload.forms import UploadForm
from moodico.recommendation.views import get_recommendation_list

# Create your views here.
# 이미지 업로드
def upload_color_image(request):
search_results = get_recommendation_list()
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
Expand All @@ -19,11 +21,12 @@ def upload_color_image(request):
upload.save()
return render(request, 'upload/upload.html', {
'form': UploadForm(),
'uploaded_image_url': upload.image_path.url
'uploaded_image_url': upload.image_path.url,
'search_results':search_results,
})
else:
form = UploadForm()
return render(request, 'upload/upload.html', {'form': form})
return render(request, 'upload/upload.html', {'form': form, 'search_results': search_results})

# upload.html에서 검색한 제품의 이미지를 가져옴
def proxy_image(request):
Expand Down
Loading