diff --git a/app.py b/app.py index 8913d71..40214c5 100644 --- a/app.py +++ b/app.py @@ -64,6 +64,11 @@ def init_db(): def index(): return render_template('index.html') +@app.route('/survey/') +def survey_page(survey_token): + """문진표 페이지 (모바일)""" + return render_template('survey.html') + # ==================== 환자 관리 API ==================== @app.route('/api/patients', methods=['GET']) @@ -320,18 +325,57 @@ def create_formula(): @app.route('/api/formulas//ingredients', methods=['GET']) def get_formula_ingredients(formula_id): - """처방 구성 약재 조회""" + """처방 구성 약재 조회 (ingredient_code 기반, 사용 가능한 모든 제품 포함)""" try: with get_db() as conn: cursor = conn.cursor() + + # 처방 구성 약재 조회 (ingredient_code 기반) cursor.execute(""" - SELECT fi.*, h.herb_name, h.insurance_code + SELECT + fi.ingredient_id, + fi.formula_id, + fi.ingredient_code, + fi.grams_per_cheop, + fi.notes, + fi.sort_order, + hm.herb_name, + hm.herb_name_hanja FROM formula_ingredients fi - JOIN herb_items h ON fi.herb_item_id = h.herb_item_id + LEFT JOIN herb_masters hm ON fi.ingredient_code = hm.ingredient_code WHERE fi.formula_id = ? ORDER BY fi.sort_order """, (formula_id,)) - ingredients = [dict(row) for row in cursor.fetchall()] + + ingredients = [] + for row in cursor.fetchall(): + ingredient = dict(row) + ingredient_code = ingredient['ingredient_code'] + + # 해당 주성분을 가진 사용 가능한 모든 제품 찾기 + cursor.execute(""" + SELECT + h.herb_item_id, + h.herb_name, + h.insurance_code, + h.specification, + COALESCE(SUM(il.quantity_onhand), 0) as stock, + COALESCE(AVG(il.unit_price_per_g), 0) as avg_price + FROM herb_items h + LEFT JOIN inventory_lots il ON h.herb_item_id = il.herb_item_id AND il.is_depleted = 0 + WHERE h.ingredient_code = ? + GROUP BY h.herb_item_id + HAVING stock > 0 + ORDER BY stock DESC + """, (ingredient_code,)) + + available_products = [dict(row) for row in cursor.fetchall()] + ingredient['available_products'] = available_products + ingredient['total_available_stock'] = sum(p['stock'] for p in available_products) + ingredient['product_count'] = len(available_products) + + ingredients.append(ingredient) + return jsonify({'success': True, 'data': ingredients}) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 @@ -1599,6 +1643,219 @@ def create_stock_adjustment(): except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 +# ==================== 문진표 API ==================== + +@app.route('/api/surveys/templates', methods=['GET']) +def get_survey_templates(): + """문진표 템플릿 조회""" + try: + category = request.args.get('category') + + with get_db() as conn: + cursor = conn.cursor() + + if category: + cursor.execute(""" + SELECT * FROM survey_templates + WHERE category = ? AND is_active = 1 + ORDER BY sort_order + """, (category,)) + else: + cursor.execute(""" + SELECT * FROM survey_templates + WHERE is_active = 1 + ORDER BY sort_order + """) + + templates = [dict(row) for row in cursor.fetchall()] + + # JSON 파싱 + for template in templates: + if template['options']: + template['options'] = json.loads(template['options']) + + return jsonify({'success': True, 'data': templates}) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + +@app.route('/api/surveys/categories', methods=['GET']) +def get_survey_categories(): + """문진표 카테고리 목록""" + try: + with get_db() as conn: + cursor = conn.cursor() + cursor.execute(""" + SELECT DISTINCT category, category_name, + MIN(sort_order) as min_order, + COUNT(*) as question_count + FROM survey_templates + WHERE is_active = 1 + GROUP BY category + ORDER BY min_order + """) + + categories = [dict(row) for row in cursor.fetchall()] + return jsonify({'success': True, 'data': categories}) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + +@app.route('/api/surveys', methods=['POST']) +def create_survey(): + """새 문진표 생성""" + try: + data = request.json + patient_id = data.get('patient_id') + + # 고유 토큰 생성 + import secrets + survey_token = secrets.token_urlsafe(16) + + with get_db() as conn: + cursor = conn.cursor() + cursor.execute(""" + INSERT INTO patient_surveys (patient_id, survey_token, status) + VALUES (?, ?, 'PENDING') + """, (patient_id, survey_token)) + + survey_id = cursor.lastrowid + conn.commit() + + return jsonify({ + 'success': True, + 'survey_id': survey_id, + 'survey_token': survey_token, + 'survey_url': f'/survey/{survey_token}' + }) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + +@app.route('/api/surveys/', methods=['GET']) +def get_survey(survey_token): + """문진표 조회 (토큰으로)""" + try: + with get_db() as conn: + cursor = conn.cursor() + + # 문진표 기본 정보 + cursor.execute(""" + SELECT s.*, p.name as patient_name, p.phone as patient_phone + FROM patient_surveys s + LEFT JOIN patients p ON s.patient_id = p.patient_id + WHERE s.survey_token = ? + """, (survey_token,)) + + survey_row = cursor.fetchone() + if not survey_row: + return jsonify({'success': False, 'error': '문진표를 찾을 수 없습니다'}), 404 + + survey = dict(survey_row) + + # 진행 상태 조회 + cursor.execute(""" + SELECT * FROM survey_progress + WHERE survey_id = ? + """, (survey['survey_id'],)) + + progress = [dict(row) for row in cursor.fetchall()] + survey['progress'] = progress + + # 기존 응답 조회 + cursor.execute(""" + SELECT * FROM survey_responses + WHERE survey_id = ? + """, (survey['survey_id'],)) + + responses = [dict(row) for row in cursor.fetchall()] + survey['responses'] = responses + + return jsonify({'success': True, 'data': survey}) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + +@app.route('/api/surveys//responses', methods=['POST']) +def save_survey_responses(survey_token): + """문진 응답 저장""" + try: + data = request.json + responses = data.get('responses', []) + + with get_db() as conn: + cursor = conn.cursor() + + # 문진표 확인 + cursor.execute(""" + SELECT survey_id FROM patient_surveys + WHERE survey_token = ? + """, (survey_token,)) + + survey_row = cursor.fetchone() + if not survey_row: + return jsonify({'success': False, 'error': '문진표를 찾을 수 없습니다'}), 404 + + survey_id = survey_row['survey_id'] + + # 기존 응답 삭제 후 새로 저장 (upsert 방식) + for response in responses: + # 기존 응답 삭제 + cursor.execute(""" + DELETE FROM survey_responses + WHERE survey_id = ? AND question_code = ? + """, (survey_id, response['question_code'])) + + # 새 응답 저장 + cursor.execute(""" + INSERT INTO survey_responses + (survey_id, category, question_code, question_text, answer_value, answer_type) + VALUES (?, ?, ?, ?, ?, ?) + """, ( + survey_id, + response['category'], + response['question_code'], + response.get('question_text'), + json.dumps(response['answer_value'], ensure_ascii=False) if isinstance(response['answer_value'], (list, dict)) else response['answer_value'], + response.get('answer_type', 'SINGLE') + )) + + # 상태 업데이트 + cursor.execute(""" + UPDATE patient_surveys + SET status = 'IN_PROGRESS', + updated_at = CURRENT_TIMESTAMP + WHERE survey_id = ? + """, (survey_id,)) + + conn.commit() + + return jsonify({ + 'success': True, + 'message': f'{len(responses)}개 응답 저장 완료' + }) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + +@app.route('/api/surveys//complete', methods=['POST']) +def complete_survey(survey_token): + """문진표 제출 완료""" + try: + with get_db() as conn: + cursor = conn.cursor() + + cursor.execute(""" + UPDATE patient_surveys + SET status = 'COMPLETED', + completed_at = CURRENT_TIMESTAMP + WHERE survey_token = ? + """, (survey_token,)) + + conn.commit() + + return jsonify({ + 'success': True, + 'message': '문진표가 제출되었습니다' + }) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + if __name__ == '__main__': # 데이터베이스 초기화 if not os.path.exists(app.config['DATABASE']):