Files
Doctrine-Check/app.py
2025-12-12 16:43:57 +09:00

284 lines
12 KiB
Python

from flask import Flask, render_template, request, jsonify, session, redirect, url_for
import os
import logging
from functools import wraps
from datetime import datetime
from utils.word_processor import WordProcessor
from utils.google_drive import GoogleDriveUploader
from config import Config
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.config.from_object(Config)
# 세션 설정
app.secret_key = app.config['SECRET_KEY']
# 인증 데코레이터
def require_auth(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not session.get('authenticated', False):
return redirect(url_for('auth'))
return f(*args, **kwargs)
return decorated_function
# 전역 객체 초기화
word_processor = WordProcessor(app.config['WORD_TEMPLATE_DIR'])
# 구글 드라이브 업로더 초기화 (선택사항)
drive_uploader = None
try:
# credentials.json 파일이 있는 경우에만 초기화 시도
if os.path.exists(app.config['GOOGLE_DRIVE_CREDENTIALS_FILE']):
logger.info(
"구글 드라이브 업로더 초기화 시도 | credentials=%s token=%s",
app.config['GOOGLE_DRIVE_CREDENTIALS_FILE'],
app.config['GOOGLE_DRIVE_TOKEN_FILE'],
)
drive_uploader = GoogleDriveUploader(
app.config['GOOGLE_DRIVE_CREDENTIALS_FILE'],
app.config['GOOGLE_DRIVE_TOKEN_FILE']
)
logger.info("구글 드라이브 업로더 초기화 성공")
else:
logger.info("credentials.json이 없어 구글 드라이브 업로더를 초기화하지 않았습니다.")
except FileNotFoundError:
logger.warning("구글 드라이브 인증 파일을 찾을 수 없습니다. 업로드 기능 비활성화.")
except Exception as e:
# 브라우저 관련 오류나 기타 오류는 조용히 처리
# 서버 환경에서는 브라우저가 없을 수 있으므로 정상적인 상황
logger.warning("구글 드라이브 초기화 중 오류 발생: %s", str(e))
@app.route('/auth', methods=['GET', 'POST'])
def auth():
"""인증 페이지"""
if request.method == 'POST':
access_key = request.form.get('access_key', '')
if access_key == app.config['ACCESS_KEY']:
session['authenticated'] = True
return redirect(url_for('index'))
else:
return render_template('auth.html', error='인증키가 올바르지 않습니다.')
return render_template('auth.html')
@app.route('/logout')
def logout():
"""로그아웃 - 세션 전체 삭제"""
session.clear()
return redirect(url_for('auth'))
@app.route('/')
@require_auth
def index():
"""메인 페이지"""
return render_template('index.html')
@app.route('/step1', methods=['GET', 'POST'])
@require_auth
def step1():
"""1단계: 기본 정보 입력"""
if request.method == 'POST':
data = request.get_json()
session['basic_info'] = data
return jsonify({'success': True, 'next_step': '/step2'})
basic_info = session.get('basic_info', {})
return render_template('step1.html',
districts=app.config['DISTRICTS'],
cults=app.config['CULTS'],
basic_info=basic_info)
@app.route('/step2', methods=['GET', 'POST'])
@require_auth
def step2():
"""2단계: 기독교대한감리회 신앙고백 교리 점검"""
if request.method == 'POST':
data = request.get_json()
session['methodist_doctrine'] = data
return jsonify({'success': True, 'next_step': '/step3'})
methodist_doctrine = session.get('methodist_doctrine', {})
return render_template('step2.html', methodist_doctrine=methodist_doctrine)
@app.route('/step3', methods=['GET', 'POST'])
@require_auth
def step3():
"""3단계: 이단 일반 교리 점검"""
if request.method == 'POST':
data = request.get_json()
# 7번 문항 검증 (기타의견란에 작성 필요)
other_opinions = data.get('other_opinions', '').strip()
if not other_opinions or '7번:' not in other_opinions:
return jsonify({
'success': False,
'message': '7번 문항은 기타의견란에 "7번: "으로 시작하여 답변해주세요.'
}), 400
session['general_cult_doctrine'] = data
# 기본 정보에서 이단교단 확인
basic_info = session.get('basic_info', {})
cult_name = basic_info.get('cult', '')
# "기타" 선택 시 step4 건너뛰고 step5로
if cult_name == '기타 (위 선택지에 없을 경우)':
session['specific_cult_doctrine'] = {} # 빈 딕셔너리로 설정
return jsonify({'success': True, 'next_step': '/step5'})
return jsonify({'success': True, 'next_step': '/step4'})
general_cult_doctrine = session.get('general_cult_doctrine', {})
return render_template('step3.html', general_cult_doctrine=general_cult_doctrine)
@app.route('/step4', methods=['GET', 'POST'])
@require_auth
def step4():
"""4단계: 출신 이단별 교리 점검"""
if request.method == 'POST':
data = request.get_json()
# 세션에서 출신 이단 정보 가져오기
basic_info = session.get('basic_info', {})
cult_name = basic_info.get('cult', '')
# 이단별 상세점검 문항 가져오기
questions = app.config.get('CULT_DETAIL_QUESTIONS', {}).get(cult_name, [])
# 기타의견란에 작성해야 하는 문항 번호 찾기
required_question_numbers = []
for i, question in enumerate(questions, 1):
if '기타의견란' in question or '기타의견' in question:
required_question_numbers.append(i)
# 기타의견란 검증
if required_question_numbers:
other_opinions = data.get('other_opinions', '').strip()
if not other_opinions:
return jsonify({
'success': False,
'message': f'{", ".join([str(n) + "" for n in required_question_numbers])} 문항은 기타의견란에 작성해주세요.'
}), 400
# 각 필수 문항 번호가 기타의견란에 포함되어 있는지 확인
missing_questions = []
for q_num in required_question_numbers:
if f'{q_num}번:' not in other_opinions:
missing_questions.append(q_num)
if missing_questions:
return jsonify({
'success': False,
'message': f'{", ".join([str(n) + "" for n in missing_questions])} 문항은 기타의견란에 "{", ".join([str(n) + "번:" for n in missing_questions])}"으로 시작하여 답변해주세요.'
}), 400
session['specific_cult_doctrine'] = data
return jsonify({'success': True, 'next_step': '/step5'})
# 세션에서 출신 이단 정보 가져오기
basic_info = session.get('basic_info', {})
cult_name = basic_info.get('cult', '')
# 이단별 상세점검 문항 가져오기
questions = app.config.get('CULT_DETAIL_QUESTIONS', {}).get(cult_name, [])
# 문항이 없으면 step5로 리다이렉트 (안전장치)
if not questions:
return redirect(url_for('step5'))
specific_cult_doctrine = session.get('specific_cult_doctrine', {})
return render_template('step4.html', cult_name=cult_name, questions=questions, specific_cult_doctrine=specific_cult_doctrine)
@app.route('/step5', methods=['GET', 'POST'])
@require_auth
def step5():
"""5단계: 간증문 입력"""
if request.method == 'GET':
testimony = session.get('testimony', {})
return render_template('step5.html', testimony=testimony)
if request.method == 'POST':
data = request.get_json()
session['testimony'] = data
# 모든 데이터 수집 (specific_cult_doctrine는 없을 수 있음)
all_data = {
'basic_info': session.get('basic_info', {}),
'methodist_doctrine': session.get('methodist_doctrine', {}),
'general_cult_doctrine': session.get('general_cult_doctrine', {}),
'specific_cult_doctrine': session.get('specific_cult_doctrine', {}),
'testimony': session.get('testimony', {})
}
# specific_cult_doctrine가 없으면 빈 딕셔너리로 설정
if not all_data.get('specific_cult_doctrine'):
all_data['specific_cult_doctrine'] = {}
# 워드 문서 생성
try:
cult_name = all_data['basic_info'].get('cult', '기타')
# 이단별 상세점검 문항 가져오기
cult_questions = app.config.get('CULT_DETAIL_QUESTIONS', {}).get(cult_name, [])
output_path = word_processor.generate_document(all_data, cult_name, cult_questions)
# 구글 드라이브 업로드
if drive_uploader and app.config['GOOGLE_DRIVE_FOLDER_ID']:
upload_filename = f"교리점검표_{all_data['basic_info'].get('name', '무명')}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.docx"
logger.info("구글 드라이브 업로드 시도 | file=%s, folder=%s", upload_filename, app.config['GOOGLE_DRIVE_FOLDER_ID'])
try:
file_id = drive_uploader.upload_file(
output_path,
upload_filename,
app.config['GOOGLE_DRIVE_FOLDER_ID']
)
logger.info("구글 드라이브 업로드 성공 | file_id=%s", file_id)
# 업로드 성공 시 로컬 파일 삭제
try:
os.remove(output_path)
logger.info("로컬 파일 삭제 완료 | file=%s", output_path)
except Exception as e:
logger.warning("로컬 파일 삭제 실패: %s", str(e))
# 세션은 complete 페이지에서 로그아웃 시 삭제
return jsonify({
'success': True,
'message': '제출이 완료되었습니다. 구글 드라이브에 업로드되었습니다.',
'file_id': file_id
})
except Exception as e:
logger.warning("구글 드라이브 업로드 실패: %s", str(e))
# 업로드 실패 시 로컬 파일 유지
# 세션은 유지 (재시도 가능하도록)
return jsonify({
'success': True,
'message': f'제출이 완료되었습니다. (구글 드라이브 업로드 실패: {str(e)})',
'file_path': output_path
})
else:
# 구글 드라이브 설정이 없는 경우 로컬 파일 유지
# 세션은 유지
return jsonify({
'success': True,
'message': f'제출이 완료되었습니다. 파일: {output_path}',
'file_path': output_path
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류가 발생했습니다: {str(e)}'
}), 500
@app.route('/complete')
@require_auth
def complete():
"""완료 페이지"""
return render_template('complete.html')
if __name__ == '__main__':
# 필요한 디렉토리 생성
os.makedirs(app.config['WORD_TEMPLATE_DIR'], exist_ok=True)
os.makedirs('output', exist_ok=True)
app.run(host='0.0.0.0', port=5000, debug=True)