feat: 100처방 구성약재 수 표시, 등록 여부 백엔드 판정, 이름매칭 개선
- official_formulas API에 ingredient_count 추가 (LEFT JOIN COUNT) - 등록 여부를 백엔드에서 판정 (official_formula_id FK 1차 + 이름 포함 매칭 fallback) - 내 처방 100처방 뱃지 매칭: startsWith → includes 변경 - 100처방 목록에 구성약재 수 뱃지 컬럼 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1679f75d33
commit
50883a6a84
63
app.py
63
app.py
@ -455,23 +455,62 @@ def get_official_formulas():
|
||||
search = request.args.get('search', '').strip()
|
||||
with get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
base_query = """
|
||||
SELECT of2.official_formula_id, of2.formula_number, of2.formula_name,
|
||||
of2.formula_name_hanja, of2.source_text, of2.description, of2.reference_notes,
|
||||
COUNT(ofi.ingredient_id) as ingredient_count
|
||||
FROM official_formulas of2
|
||||
LEFT JOIN official_formula_ingredients ofi ON of2.official_formula_id = ofi.official_formula_id
|
||||
"""
|
||||
if search:
|
||||
cursor.execute("""
|
||||
SELECT official_formula_id, formula_number, formula_name,
|
||||
formula_name_hanja, source_text, description, reference_notes
|
||||
FROM official_formulas
|
||||
WHERE formula_name LIKE ? OR formula_name_hanja LIKE ?
|
||||
OR source_text LIKE ? OR reference_notes LIKE ?
|
||||
ORDER BY formula_number
|
||||
cursor.execute(base_query + """
|
||||
WHERE of2.formula_name LIKE ? OR of2.formula_name_hanja LIKE ?
|
||||
OR of2.source_text LIKE ? OR of2.reference_notes LIKE ?
|
||||
GROUP BY of2.official_formula_id
|
||||
ORDER BY of2.formula_number
|
||||
""", (f'%{search}%', f'%{search}%', f'%{search}%', f'%{search}%'))
|
||||
else:
|
||||
cursor.execute("""
|
||||
SELECT official_formula_id, formula_number, formula_name,
|
||||
formula_name_hanja, source_text, description, reference_notes
|
||||
FROM official_formulas
|
||||
ORDER BY formula_number
|
||||
cursor.execute(base_query + """
|
||||
GROUP BY of2.official_formula_id
|
||||
ORDER BY of2.formula_number
|
||||
""")
|
||||
formulas = [dict(row) for row in cursor.fetchall()]
|
||||
|
||||
# 등록 여부 판정: official_formula_id FK 매칭 (1차) + 이름 매칭 (fallback)
|
||||
# 1차: formulas.official_formula_id로 직접 연결된 처방
|
||||
cursor.execute("""
|
||||
SELECT official_formula_id, formula_name
|
||||
FROM formulas
|
||||
WHERE is_active = 1 AND official_formula_id IS NOT NULL
|
||||
""")
|
||||
registered_by_id = {}
|
||||
for row in cursor.fetchall():
|
||||
oid = row['official_formula_id']
|
||||
if oid not in registered_by_id:
|
||||
registered_by_id[oid] = []
|
||||
registered_by_id[oid].append(row['formula_name'])
|
||||
|
||||
# 2차 fallback: 이름 기반 매칭용
|
||||
cursor.execute("SELECT formula_name FROM formulas WHERE is_active = 1")
|
||||
my_formula_names = [row['formula_name'] for row in cursor.fetchall()]
|
||||
|
||||
for formula in formulas:
|
||||
oid = formula['official_formula_id']
|
||||
oname = formula['formula_name']
|
||||
|
||||
# 1차: FK 매칭
|
||||
if oid in registered_by_id:
|
||||
formula['is_registered'] = True
|
||||
formula['registered_names'] = registered_by_id[oid]
|
||||
# 2차: 이름 매칭 (정확 매칭 또는 내 처방명에 원방명이 포함)
|
||||
elif any(name == oname or oname in name for name in my_formula_names):
|
||||
matched = [name for name in my_formula_names if name == oname or oname in name]
|
||||
formula['is_registered'] = True
|
||||
formula['registered_names'] = matched
|
||||
else:
|
||||
formula['is_registered'] = False
|
||||
formula['registered_names'] = []
|
||||
|
||||
return jsonify({'success': True, 'data': formulas})
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
@ -590,11 +590,18 @@ $(document).ready(function() {
|
||||
tbody.empty();
|
||||
|
||||
response.data.forEach(formula => {
|
||||
// 100처방 매칭: 정확 매칭 우선, 없으면 내 처방명이 100처방명으로 시작하는지 확인
|
||||
let officialNum = officialNames.get(formula.formula_name);
|
||||
// 100처방 매칭: 1차 official_formula_id FK, 2차 이름 매칭 (원방명이 내 처방명에 포함)
|
||||
let officialNum = null;
|
||||
if (formula.official_formula_id) {
|
||||
// FK로 연결된 경우 — official_name은 API에서 JOIN으로 내려옴
|
||||
officialNum = officialNames.get(formula.official_name);
|
||||
}
|
||||
if (officialNum == null) {
|
||||
officialNum = officialNames.get(formula.formula_name);
|
||||
}
|
||||
if (officialNum == null) {
|
||||
for (const [name, num] of officialNames) {
|
||||
if (formula.formula_name.startsWith(name)) {
|
||||
if (formula.formula_name.includes(name)) {
|
||||
officialNum = num;
|
||||
break;
|
||||
}
|
||||
@ -697,13 +704,6 @@ $(document).ready(function() {
|
||||
function loadOfficialFormulas(search) {
|
||||
const params = search ? `?search=${encodeURIComponent(search)}` : '';
|
||||
|
||||
// 내 처방 이름 목록을 API에서 가져와서 비교
|
||||
$.get('/api/formulas', function(formulasRes) {
|
||||
const myFormulaNames = new Set();
|
||||
if (formulasRes.success) {
|
||||
formulasRes.data.forEach(f => myFormulaNames.add(f.formula_name));
|
||||
}
|
||||
|
||||
$.get(`/api/official-formulas${params}`, function(response) {
|
||||
if (response.success) {
|
||||
const tbody = $('#officialFormulasList');
|
||||
@ -712,15 +712,24 @@ $(document).ready(function() {
|
||||
$('#officialFormulaCount').text(response.data.length);
|
||||
|
||||
response.data.forEach(formula => {
|
||||
// 등록 여부: 정확 매칭 또는 내 처방명이 100처방명으로 시작
|
||||
const isRegistered = myFormulaNames.has(formula.formula_name)
|
||||
|| [...myFormulaNames].some(name => name.startsWith(formula.formula_name));
|
||||
const statusBadge = isRegistered
|
||||
? '<span class="badge bg-success">등록됨</span>'
|
||||
: '<span class="badge bg-outline-secondary text-muted">미등록</span>';
|
||||
// 등록 여부: 백엔드에서 판정 (official_formula_id FK + 이름 fallback)
|
||||
const isRegistered = formula.is_registered;
|
||||
let statusBadge;
|
||||
if (isRegistered && formula.registered_names && formula.registered_names.length > 0) {
|
||||
const names = formula.registered_names.map(n => n.length > 12 ? n.substring(0, 12) + '…' : n).join(', ');
|
||||
statusBadge = `<span class="badge bg-success" title="${formula.registered_names.join(', ')}">${formula.registered_names.length > 1 ? formula.registered_names.length + '개 등록' : '등록됨'}</span>`;
|
||||
} else {
|
||||
statusBadge = '<span class="badge bg-outline-secondary text-muted">미등록</span>';
|
||||
}
|
||||
|
||||
const hasNotes = formula.reference_notes ? '<i class="bi bi-journal-text text-info ms-1" title="참고자료 있음"></i>' : '';
|
||||
|
||||
// 구성 약재 수 표시
|
||||
const ingCount = formula.ingredient_count || 0;
|
||||
const ingBadge = ingCount > 0
|
||||
? `<span class="badge bg-success bg-opacity-75">${ingCount}종</span>`
|
||||
: `<span class="badge bg-light text-muted">미입력</span>`;
|
||||
|
||||
tbody.append(`
|
||||
<tr class="official-formula-row" style="cursor:pointer"
|
||||
data-id="${formula.official_formula_id}"
|
||||
@ -734,17 +743,17 @@ $(document).ready(function() {
|
||||
<td><strong>${formula.formula_name}</strong>${hasNotes}</td>
|
||||
<td class="text-muted">${formula.formula_name_hanja || '-'}</td>
|
||||
<td>${formula.source_text || '-'}</td>
|
||||
<td class="text-center">${ingBadge}</td>
|
||||
<td class="text-center">${statusBadge}</td>
|
||||
</tr>
|
||||
`);
|
||||
});
|
||||
|
||||
if (response.data.length === 0) {
|
||||
tbody.html('<tr><td colspan="5" class="text-center text-muted">검색 결과가 없습니다.</td></tr>');
|
||||
tbody.html('<tr><td colspan="6" class="text-center text-muted">검색 결과가 없습니다.</td></tr>');
|
||||
}
|
||||
}
|
||||
});
|
||||
}); // /api/formulas 콜백 닫기
|
||||
}
|
||||
|
||||
// 100처방 검색 이벤트
|
||||
|
||||
@ -403,6 +403,7 @@
|
||||
<th>처방명</th>
|
||||
<th>한자명</th>
|
||||
<th>출전</th>
|
||||
<th width="80">구성약재</th>
|
||||
<th width="80">상태</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user