# app.
py - Student Court Website
from flask import Flask, render_template, request, redirect, url_for, flash,
send_from_directory
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user,
login_required, current_user
from [Link] import generate_password_hash, check_password_hash
from [Link] import secure_filename
from docx import Document
import os
import re
from datetime import datetime
from config import Config
app = Flask(__name__)
[Link].from_object(Config)
instance_path = [Link]([Link]([Link](__file__)),
'instance')
[Link](instance_path, exist_ok=True)
upload_base = [Link]['UPLOAD_FOLDER']
[Link]([Link](upload_base, 'cases'), exist_ok=True)
[Link]([Link](upload_base, 'rules'), exist_ok=True)
[Link]([Link](upload_base, 'members'), exist_ok=True)
[Link]([Link](upload_base, 'pdfs'), exist_ok=True)
[Link]([Link](upload_base, 'temp'), exist_ok=True)
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'admin_login'
# Models
class AdminUser(UserMixin, [Link]):
__tablename__ = 'admin_users'
id = [Link]([Link], primary_key=True)
username = [Link]([Link](80), unique=True, nullable=False)
password_hash = [Link]([Link](200), nullable=False)
role = [Link]([Link](50), default='admin')
created_at = [Link]([Link], default=[Link])
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
class Case([Link]):
__tablename__ = 'cases'
id = [Link]([Link], primary_key=True)
slug = [Link]([Link](200), unique=True, nullable=False)
title = [Link]([Link](200), nullable=False)
case_number = [Link]([Link](50))
category = [Link]([Link](100), nullable=False)
status = [Link]([Link](50), nullable=False, default='진행중')
date = [Link]([Link](50), nullable=False)
summary = [Link]([Link])
content_html = [Link]([Link])
verdict = [Link]([Link])
cover_image = [Link]([Link](200))
pdf_file = [Link]([Link](200))
word_file = [Link]([Link](200)) # 원본 Word 파일 저장
is_public = [Link]([Link], default=True)
is_pinned = [Link]([Link], default=False)
created_at = [Link]([Link], default=[Link])
updated_at = [Link]([Link], default=[Link],
onupdate=[Link])
files = [Link]('CaseFile', backref='case', lazy=True, cascade='all,
delete-orphan')
class CaseFile([Link]):
__tablename__ = 'case_files'
id = [Link]([Link], primary_key=True)
case_id = [Link]([Link], [Link]('[Link]'), nullable=False)
name = [Link]([Link](200), nullable=False)
file_path = [Link]([Link](300), nullable=False)
file_type = [Link]([Link](50), nullable=False)
class Rule([Link]):
__tablename__ = 'rules'
id = [Link]([Link], primary_key=True)
slug = [Link]([Link](200), unique=True, nullable=False)
title = [Link]([Link](200), nullable=False)
category = [Link]([Link](100), nullable=False)
status = [Link]([Link](50), nullable=False, default='시행중')
date = [Link]([Link](50), nullable=False)
summary = [Link]([Link])
content_html = [Link]([Link])
cover_image = [Link]([Link](200))
pdf_file = [Link]([Link](200))
word_file = [Link]([Link](200)) # 원본 Word 파일 저장
is_pinned = [Link]([Link], default=False)
created_at = [Link]([Link], default=[Link])
updated_at = [Link]([Link], default=[Link],
onupdate=[Link])
files = [Link]('RuleFile', backref='rule', lazy=True, cascade='all,
delete-orphan')
class RuleFile([Link]):
__tablename__ = 'rule_files'
id = [Link]([Link], primary_key=True)
rule_id = [Link]([Link], [Link]('[Link]'), nullable=False)
name = [Link]([Link](200), nullable=False)
file_path = [Link]([Link](300), nullable=False)
file_type = [Link]([Link](50), nullable=False)
class Member([Link]):
__tablename__ = 'members'
id = [Link]([Link], primary_key=True)
name = [Link]([Link](100), nullable=False)
role = [Link]([Link](50), nullable=False)
description = [Link]([Link])
photo = [Link]([Link](255))
order = [Link]([Link], default=0)
is_active = [Link]([Link], default=True)
created_at = [Link]([Link], default=[Link])
# Helper Functions
@login_manager.user_loader
def load_user(user_id):
return [Link](int(user_id))
def allowed_file(filename):
if not filename:
return False
return '.' in filename and [Link]('.', 1)[1].lower() in
[Link]['ALLOWED_EXTENSIONS']
def create_slug(title):
from [Link] import quote
import uuid
slug = [Link]()
slug = [Link](r'[^\w\s-]', '', slug)
slug = [Link](r'[\s_]+', '-', slug)
slug = [Link]('-')
if not slug:
slug = str(uuid.uuid4())[:8]
slug = quote(slug, safe='-')
return slug
def extract_word_content(docx_path, slug, upload_subfolder='rules'):
"""Extract content from Word document"""
doc = Document(docx_path)
html_parts = []
image_counter = 0
upload_dir = [Link]([Link]['UPLOAD_FOLDER'], upload_subfolder)
[Link](upload_dir, exist_ok=True)
image_rels = {}
for rel in [Link]():
if "image" in rel.target_ref:
image_counter += 1
img_ext = rel.target_ref.split('.')[-1].lower()
if img_ext not in ['png', 'jpg', 'jpeg', 'gif', 'webp']:
img_ext = 'png'
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
img_filename = f"{slug}_img_{timestamp}_{image_counter}.{img_ext}"
img_path = [Link](upload_dir, img_filename)
try:
with open(img_path, 'wb') as f:
[Link](rel.target_part.blob)
img_url = f"/uploads/{upload_subfolder}/{img_filename}"
image_rels[[Link]] = img_url
except:
continue
for element in [Link]:
if [Link]('p'):
para = next((p for p in [Link] if p._element == element), None)
if para:
for run in [Link]:
for drawing in
run._element.findall('.//{[Link]
main}drawing'):
for blip in
[Link]('.//{[Link]
embed_id =
[Link]('{[Link]
d')
if embed_id and embed_id in image_rels:
html_parts.append(f'<p class="image-container"><img
src="{image_rels[embed_id]}" class="content-image"></p>')
text = [Link]()
if text:
style_name = [Link]() if [Link] else ''
if 'heading 1' in style_name or 'title' in style_name:
html_parts.append(f'<h1>{text}</h1>')
elif 'heading 2' in style_name:
html_parts.append(f'<h2>{text}</h2>')
elif 'heading 3' in style_name:
html_parts.append(f'<h3>{text}</h3>')
else:
html_parts.append(f'<p>{text}</p>')
elif [Link]('tbl'):
html_parts.append('<table class="word-table"><tbody>')
table = next((t for t in [Link] if t._element == element), None)
if table:
for row in [Link]:
html_parts.append('<tr>')
for cell in [Link]:
html_parts.append(f'<td>{[Link]}</td>')
html_parts.append('</tr>')
html_parts.append('</tbody></table>')
return '\n'.join(html_parts)
# Public Routes
@[Link]('/')
def home():
recent_cases =
[Link].filter_by(is_public=True).order_by(Case.created_at.desc()).limit(3).all(
)
recent_rules = [Link].order_by(Rule.created_at.desc()).limit(3).all()
pinned_rules =
[Link].filter_by(is_pinned=True).order_by(Rule.created_at.desc()).limit(2).all(
)
pinned_cases = [Link].filter_by(is_public=True,
is_pinned=True).order_by(Case.created_at.desc()).limit(3).all()
return render_template('[Link]', recent_cases=recent_cases,
recent_rules=recent_rules, pinned_rules=pinned_rules, pinned_cases=pinned_cases)
@[Link]('/cases')
def cases():
all_cases =
[Link].filter_by(is_public=True).order_by(Case.is_pinned.desc(),
Case.created_at.desc()).all()
categories = list(set([[Link] for c in all_cases if [Link]]))
[Link]()
return render_template('[Link]', cases=all_cases, categories=categories)
@[Link]('/cases/<slug>')
def case_detail(slug):
case = [Link].filter_by(slug=slug, is_public=True).first_or_404()
return render_template('case_detail.html', case=case)
@[Link]('/rules')
def rules():
all_rules = [Link].order_by(Rule.is_pinned.desc(),
Rule.created_at.desc()).all()
pinned_rules =
[Link].filter_by(is_pinned=True).order_by(Rule.created_at.desc()).all()
categories = list(set([[Link] for r in all_rules if [Link]]))
[Link]()
return render_template('[Link]', rules=all_rules,
pinned_rules=pinned_rules, categories=categories)
@[Link]('/rules/<slug>')
def rule_detail(slug):
rule = [Link].filter_by(slug=slug).first_or_404()
return render_template('rule_detail.html', rule=rule)
@[Link]('/about')
def about():
members = [Link].filter_by(is_active=True).order_by([Link],
[Link]).all()
return render_template('[Link]', members=members)
@[Link]('/contact')
def contact():
return render_template('[Link]')
@[Link]('/uploads/<path:filename>')
def serve_upload(filename):
return send_from_directory([Link]['UPLOAD_FOLDER'], filename)
# Admin Routes
@[Link]('/admin')
def admin_redirect():
return redirect(url_for('admin_login'))
@[Link]('/admin/login', methods=['GET', 'POST'])
def admin_login():
if current_user.is_authenticated:
return redirect(url_for('admin_dashboard'))
if [Link] == 'POST':
username = [Link]('username')
password = [Link]('password')
user = [Link].filter_by(username=username).first()
if user and user.check_password(password):
login_user(user)
flash('로그인되었습니다!', 'success')
return redirect(url_for('admin_dashboard'))
else:
flash('아이디 또는 비밀번호가 올바르지 않습니다', 'error')
return render_template('admin/[Link]')
@[Link]('/admin/logout')
@login_required
def admin_logout():
logout_user()
flash('로그아웃되었습니다', 'success')
return redirect(url_for('home'))
@[Link]('/admin/dashboard')
@login_required
def admin_dashboard():
cases = [Link].order_by(Case.created_at.desc()).all()
rules = [Link].order_by(Rule.created_at.desc()).all()
members = [Link].filter_by(is_active=True).order_by([Link],
[Link]).all()
return render_template('admin/[Link]', cases=cases, rules=rules,
members=members)
# Case Admin
@[Link]('/admin/cases/new', methods=['GET', 'POST'])
@login_required
def admin_new_case():
if [Link] == 'POST':
try:
title = [Link]('title')
category = [Link]('category')
status = [Link]('status', '진행중')
case_number = [Link]('case_number')
date = [Link]('date')
summary = [Link]('summary')
verdict = [Link]('verdict')
slug = create_slug(title)
is_public = 'is_public' in [Link]
is_pinned = 'is_pinned' in [Link]
base_slug = slug
counter = 1
while [Link].filter_by(slug=slug).first():
slug = f"{base_slug}-{counter}"
counter += 1
cover_image = None
if 'cover_image' in [Link]:
file = [Link]['cover_image']
if file and [Link] and allowed_file([Link]):
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{slug}_cover_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'], 'cases',
filename)
[Link](filepath)
cover_image = f'cases/{filename}'
content_html = ''
pdf_file = None
word_file = None
word_uploaded = False
if 'word_file' in [Link]:
doc_file = [Link]['word_file']
if doc_file and doc_file.filename:
file_ext = [Link](doc_file.filename)[1].lower()
timestamp = [Link]().strftime('%Y%m%d%H%M%S')
if file_ext == '.pdf':
pdf_filename =
secure_filename(f"{slug}_pdf_{timestamp}.pdf")
pdf_path = [Link]([Link]['UPLOAD_FOLDER'],
'pdfs', pdf_filename)
doc_file.save(pdf_path)
pdf_file = f'pdfs/{pdf_filename}'
elif file_ext in ['.docx', '.doc']:
# Word 파일 저장
word_filename = secure_filename(f"{slug}_word_{timestamp}
{file_ext}")
word_path = [Link]([Link]['UPLOAD_FOLDER'],
'cases', word_filename)
doc_file.save(word_path)
word_file = f'cases/{word_filename}'
# Word 내용 변환
content_html = extract_word_content(word_path, slug,
'cases')
word_uploaded = True
if not content_html and not pdf_file:
editor_content = [Link]('content')
if editor_content:
content_html = editor_content
case = Case(slug=slug, title=title, case_number=case_number,
category=category,
status=status, date=date, summary=summary,
content_html=content_html,
verdict=verdict, cover_image=cover_image, pdf_file=pdf_file,
word_file=word_file, is_public=is_public,
is_pinned=is_pinned)
[Link](case)
[Link]()
if 'additional_files' in [Link]:
files = [Link]('additional_files')
for file in files:
if file and [Link] and allowed_file([Link]):
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{slug}_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'],
'cases', filename)
[Link](filepath)
f_ext = [Link]('.', 1)[1].lower()
case_file = CaseFile(case_id=[Link], name=[Link],
file_path=f'cases/{filename}', file_type=f_ext)
[Link](case_file)
[Link]()
# Word 파일 업로드 시 자동으로 수정 페이지로 이동 (에디터에서 확인/수정 가능)
if word_uploaded:
flash('Word 문서가 변환되었습니다. 내용을 확인하세요.', 'success')
return redirect(url_for('admin_edit_case', id=[Link]))
flash('재판 기록이 등록되었습니다!', 'success')
return redirect(url_for('admin_dashboard'))
except Exception as e:
[Link]()
flash(f'오류가 발생했습니다: {str(e)}', 'error')
return redirect(url_for('admin_new_case'))
return render_template('admin/case_form.html', case=None)
@[Link]('/admin/cases/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def admin_edit_case(id):
case = [Link].get_or_404(id)
if [Link] == 'POST':
try:
[Link] = [Link]('title')
[Link] = [Link]('category')
[Link] = [Link]('status', '진행중')
case.case_number = [Link]('case_number')
[Link] = [Link]('date')
[Link] = [Link]('summary')
[Link] = [Link]('verdict')
case.is_public = 'is_public' in [Link]
case.is_pinned = 'is_pinned' in [Link]
new_slug = create_slug([Link])
if new_slug != [Link]:
base_slug = new_slug
counter = 1
while [Link]([Link] == new_slug, [Link] !=
[Link]).first():
new_slug = f"{base_slug}-{counter}"
counter += 1
[Link] = new_slug
if [Link]('remove_cover_image') == 'true' and
case.cover_image:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
case.cover_image)
if [Link](old_path):
[Link](old_path)
case.cover_image = None
if 'cover_image' in [Link]:
file = [Link]['cover_image']
if file and [Link] and allowed_file([Link]):
if case.cover_image:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
case.cover_image)
if [Link](old_path):
[Link](old_path)
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{[Link]}_cover_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'], 'cases',
filename)
[Link](filepath)
case.cover_image = f'cases/{filename}'
# PDF/Word 삭제
if [Link]('remove_doc') == 'true':
if case.pdf_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
case.pdf_file)
if [Link](old_path):
[Link](old_path)
case.pdf_file = None
if case.word_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
case.word_file)
if [Link](old_path):
[Link](old_path)
case.word_file = None
word_uploaded = False
if 'word_file' in [Link]:
doc_file = [Link]['word_file']
if doc_file and doc_file.filename:
file_ext = [Link](doc_file.filename)[1].lower()
timestamp = [Link]().strftime('%Y%m%d%H%M%S')
# 기존 파일 삭제
if case.pdf_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
case.pdf_file)
if [Link](old_path):
[Link](old_path)
case.pdf_file = None
if case.word_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
case.word_file)
if [Link](old_path):
[Link](old_path)
case.word_file = None
if file_ext == '.pdf':
pdf_filename =
secure_filename(f"{[Link]}_pdf_{timestamp}.pdf")
pdf_path = [Link]([Link]['UPLOAD_FOLDER'],
'pdfs', pdf_filename)
doc_file.save(pdf_path)
case.pdf_file = f'pdfs/{pdf_filename}'
elif file_ext in ['.docx', '.doc']:
word_filename =
secure_filename(f"{[Link]}_word_{timestamp}{file_ext}")
word_path = [Link]([Link]['UPLOAD_FOLDER'],
'cases', word_filename)
doc_file.save(word_path)
case.word_file = f'cases/{word_filename}'
content_html = extract_word_content(word_path, [Link],
'cases')
if content_html:
case.content_html = content_html
word_uploaded = True
if not word_uploaded:
content = [Link]('content')
if content:
case.content_html = content
if [Link]('remove_files'):
file_ids = [Link]('remove_files').split(',')
for file_id in file_ids:
if file_id:
cfile = [Link](int(file_id))
if cfile and cfile.case_id == [Link]:
file_path = [Link]([Link]['UPLOAD_FOLDER'],
cfile.file_path)
if [Link](file_path):
[Link](file_path)
[Link](cfile)
if 'additional_files' in [Link]:
files = [Link]('additional_files')
for file in files:
if file and [Link] and allowed_file([Link]):
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{[Link]}_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'],
'cases', filename)
[Link](filepath)
f_ext = [Link]('.', 1)[1].lower()
case_file = CaseFile(case_id=[Link], name=[Link],
file_path=f'cases/{filename}', file_type=f_ext)
[Link](case_file)
case.updated_at = [Link]()
[Link]()
if word_uploaded:
flash('Word 문서가 변환되었습니다. 내용을 확인하세요.', 'success')
return redirect(url_for('admin_edit_case', id=id))
flash('재판 기록이 수정되었습니다!', 'success')
return redirect(url_for('admin_dashboard'))
except Exception as e:
[Link]()
flash(f'오류가 발생했습니다: {str(e)}', 'error')
return render_template('admin/case_form.html', case=case)
@[Link]('/admin/cases/<int:id>/delete', methods=['POST'])
@login_required
def admin_delete_case(id):
try:
case = [Link].get_or_404(id)
if case.pdf_file:
pdf_path = [Link]([Link]['UPLOAD_FOLDER'], case.pdf_file)
if [Link](pdf_path):
[Link](pdf_path)
if case.word_file:
word_path = [Link]([Link]['UPLOAD_FOLDER'], case.word_file)
if [Link](word_path):
[Link](word_path)
if case.cover_image:
cover_path = [Link]([Link]['UPLOAD_FOLDER'],
case.cover_image)
if [Link](cover_path):
[Link](cover_path)
[Link](case)
[Link]()
flash('재판 기록이 삭제되었습니다!', 'success')
except Exception as e:
[Link]()
flash(f'오류가 발생했습니다: {str(e)}', 'error')
return redirect(url_for('admin_dashboard'))
# Rule Admin
@[Link]('/admin/rules/new', methods=['GET', 'POST'])
@login_required
def admin_new_rule():
if [Link] == 'POST':
try:
title = [Link]('title')
category = [Link]('category')
status = [Link]('status', '시행중')
date = [Link]('date')
summary = [Link]('summary', '')
is_pinned = 'is_pinned' in [Link]
slug = create_slug(title)
base_slug = slug
counter = 1
while [Link].filter_by(slug=slug).first():
slug = f"{base_slug}-{counter}"
counter += 1
cover_image = None
if 'cover_image' in [Link]:
file = [Link]['cover_image']
if file and [Link] and allowed_file([Link]):
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{slug}_cover_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'], 'rules',
filename)
[Link](filepath)
cover_image = f'rules/{filename}'
content_html = ''
pdf_file = None
word_file = None
word_uploaded = False
if 'word_file' in [Link]:
doc_file = [Link]['word_file']
if doc_file and doc_file.filename:
file_ext = [Link](doc_file.filename)[1].lower()
timestamp = [Link]().strftime('%Y%m%d%H%M%S')
if file_ext == '.pdf':
pdf_filename =
secure_filename(f"{slug}_pdf_{timestamp}.pdf")
pdf_path = [Link]([Link]['UPLOAD_FOLDER'],
'pdfs', pdf_filename)
doc_file.save(pdf_path)
pdf_file = f'pdfs/{pdf_filename}'
elif file_ext in ['.docx', '.doc']:
word_filename = secure_filename(f"{slug}_word_{timestamp}
{file_ext}")
word_path = [Link]([Link]['UPLOAD_FOLDER'],
'rules', word_filename)
doc_file.save(word_path)
word_file = f'rules/{word_filename}'
content_html = extract_word_content(word_path, slug,
'rules')
word_uploaded = True
if not content_html and not pdf_file:
editor_content = [Link]('content')
if editor_content:
content_html = editor_content
rule = Rule(slug=slug, title=title, category=category, status=status,
date=date,
summary=summary, content_html=content_html,
cover_image=cover_image,
pdf_file=pdf_file, word_file=word_file, is_pinned=is_pinned)
[Link](rule)
[Link]()
if 'additional_files' in [Link]:
files = [Link]('additional_files')
for file in files:
if file and [Link] and allowed_file([Link]):
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{slug}_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'],
'rules', filename)
[Link](filepath)
f_ext = [Link]('.', 1)[1].lower()
rule_file = RuleFile(rule_id=[Link], name=[Link],
file_path=f'rules/{filename}', file_type=f_ext)
[Link](rule_file)
[Link]()
if word_uploaded:
flash('Word 문서가 변환되었습니다. 내용을 확인하세요.', 'success')
return redirect(url_for('admin_edit_rule', id=[Link]))
flash('규정/공지가 등록되었습니다!', 'success')
return redirect(url_for('admin_dashboard'))
except Exception as e:
[Link]()
flash(f'오류가 발생했습니다: {str(e)}', 'error')
return redirect(url_for('admin_new_rule'))
return render_template('admin/rule_form.html', rule=None)
@[Link]('/admin/rules/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def admin_edit_rule(id):
rule = [Link].get_or_404(id)
if [Link] == 'POST':
try:
[Link] = [Link]('title')
[Link] = [Link]('category')
[Link] = [Link]('status', '시행중')
[Link] = [Link]('date')
[Link] = [Link]('summary', '')
rule.is_pinned = 'is_pinned' in [Link]
new_slug = create_slug([Link])
if new_slug != [Link]:
base_slug = new_slug
counter = 1
while [Link]([Link] == new_slug, [Link] !=
[Link]).first():
new_slug = f"{base_slug}-{counter}"
counter += 1
[Link] = new_slug
if [Link]('remove_cover_image') == 'true' and
rule.cover_image:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
rule.cover_image)
if [Link](old_path):
[Link](old_path)
rule.cover_image = None
if 'cover_image' in [Link]:
file = [Link]['cover_image']
if file and [Link] and allowed_file([Link]):
if rule.cover_image:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
rule.cover_image)
if [Link](old_path):
[Link](old_path)
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{[Link]}_cover_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'], 'rules',
filename)
[Link](filepath)
rule.cover_image = f'rules/{filename}'
# PDF/Word 삭제
if [Link]('remove_doc') == 'true':
if rule.pdf_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
rule.pdf_file)
if [Link](old_path):
[Link](old_path)
rule.pdf_file = None
if rule.word_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
rule.word_file)
if [Link](old_path):
[Link](old_path)
rule.word_file = None
word_uploaded = False
if 'word_file' in [Link]:
doc_file = [Link]['word_file']
if doc_file and doc_file.filename:
file_ext = [Link](doc_file.filename)[1].lower()
timestamp = [Link]().strftime('%Y%m%d%H%M%S')
if rule.pdf_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
rule.pdf_file)
if [Link](old_path):
[Link](old_path)
rule.pdf_file = None
if rule.word_file:
old_path = [Link]([Link]['UPLOAD_FOLDER'],
rule.word_file)
if [Link](old_path):
[Link](old_path)
rule.word_file = None
if file_ext == '.pdf':
pdf_filename =
secure_filename(f"{[Link]}_pdf_{timestamp}.pdf")
pdf_path = [Link]([Link]['UPLOAD_FOLDER'],
'pdfs', pdf_filename)
doc_file.save(pdf_path)
rule.pdf_file = f'pdfs/{pdf_filename}'
elif file_ext in ['.docx', '.doc']:
word_filename =
secure_filename(f"{[Link]}_word_{timestamp}{file_ext}")
word_path = [Link]([Link]['UPLOAD_FOLDER'],
'rules', word_filename)
doc_file.save(word_path)
rule.word_file = f'rules/{word_filename}'
content_html = extract_word_content(word_path, [Link],
'rules')
if content_html:
rule.content_html = content_html
word_uploaded = True
if not word_uploaded:
content = [Link]('content')
if content:
rule.content_html = content
if [Link]('remove_files'):
file_ids = [Link]('remove_files').split(',')
for file_id in file_ids:
if file_id:
rfile = [Link](int(file_id))
if rfile and rfile.rule_id == [Link]:
file_path = [Link]([Link]['UPLOAD_FOLDER'],
rfile.file_path)
if [Link](file_path):
[Link](file_path)
[Link](rfile)
if 'additional_files' in [Link]:
files = [Link]('additional_files')
for file in files:
if file and [Link] and allowed_file([Link]):
filename = secure_filename([Link])
timestamp = [Link]().strftime('%Y%m%d_%H%M%S')
filename = f"{[Link]}_{timestamp}_{filename}"
filepath = [Link]([Link]['UPLOAD_FOLDER'],
'rules', filename)
[Link](filepath)
f_ext = [Link]('.', 1)[1].lower()
rule_file = RuleFile(rule_id=[Link], name=[Link],
file_path=f'rules/{filename}', file_type=f_ext)
[Link](rule_file)
rule.updated_at = [Link]()
[Link]()
if word_uploaded:
flash('Word 문서가 변환되었습니다. 내용을 확인하세요.', 'success')
return redirect(url_for('admin_edit_rule', id=id))
flash('규정/공지가 수정되었습니다!', 'success')
return redirect(url_for('admin_dashboard'))
except Exception as e:
[Link]()
flash(f'오류가 발생했습니다: {str(e)}', 'error')
return render_template('admin/rule_form.html', rule=rule)
@[Link]('/admin/rules/<int:id>/delete', methods=['POST'])
@login_required
def admin_delete_rule(id):
try:
rule = [Link].get_or_404(id)
if rule.pdf_file:
pdf_path = [Link]([Link]['UPLOAD_FOLDER'], rule.pdf_file)
if [Link](pdf_path):
[Link](pdf_path)
if rule.word_file:
word_path = [Link]([Link]['UPLOAD_FOLDER'], rule.word_file)
if [Link](word_path):
[Link](word_path)
if rule.cover_image:
cover_path = [Link]([Link]['UPLOAD_FOLDER'],
rule.cover_image)
if [Link](cover_path):
[Link](cover_path)
[Link](rule)
[Link]()
flash('규정/공지가 삭제되었습니다!', 'success')
except Exception as e:
[Link]()
flash(f'오류가 발생했습니다: {str(e)}', 'error')
return redirect(url_for('admin_dashboard'))
# Member Admin
@[Link]('/admin/members/new', methods=['GET', 'POST'])
@login_required
def admin_new_member():
if [Link] == 'POST':
name = [Link]('name')
role = [Link]('role')
description = [Link]('description', '')
order = int([Link]('order', 0))
member = Member(name=name, role=role, description=description, order=order)
photo = [Link]('photo')
if photo and [Link]:
filename = secure_filename(f"member_{[Link]().strftime('%Y%m%d%H
%M%S')}_{[Link]}")
photo_path = [Link]([Link]['UPLOAD_FOLDER'], 'members',
filename)
[Link](photo_path)
[Link] = f"members/{filename}"
[Link](member)
[Link]()
flash('구성원이 등록되었습니다.', 'success')
return redirect(url_for('admin_dashboard'))
return render_template('admin/member_form.html', member=None)
@[Link]('/admin/members/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def admin_edit_member(id):
member = [Link].get_or_404(id)
if [Link] == 'POST':
[Link] = [Link]('name')
[Link] = [Link]('role')
[Link] = [Link]('description', '')
[Link] = int([Link]('order', 0))
if [Link]('remove_photo') == 'true' and [Link]:
old_path = [Link]([Link]['UPLOAD_FOLDER'], [Link])
if [Link](old_path):
[Link](old_path)
[Link] = None
photo = [Link]('photo')
if photo and [Link]:
if [Link]:
old_path = [Link]([Link]['UPLOAD_FOLDER'], [Link])
if [Link](old_path):
[Link](old_path)
filename = secure_filename(f"member_{[Link]().strftime('%Y%m%d%H
%M%S')}_{[Link]}")
photo_path = [Link]([Link]['UPLOAD_FOLDER'], 'members',
filename)
[Link](photo_path)
[Link] = f"members/{filename}"
[Link]()
flash('구성원 정보가 수정되었습니다.', 'success')
return redirect(url_for('admin_dashboard'))
return render_template('admin/member_form.html', member=member)
@[Link]('/admin/members/<int:id>/delete', methods=['POST'])
@login_required
def admin_delete_member(id):
member = [Link].get_or_404(id)
if [Link]:
photo_path = [Link]([Link]['UPLOAD_FOLDER'], [Link])
if [Link](photo_path):
[Link](photo_path)
[Link](member)
[Link]()
flash('구성원이 삭제되었습니다.', 'success')
return redirect(url_for('admin_dashboard'))
# Init & Error Handlers
def init_db():
with app.app_context():
db.create_all()
admin =
[Link].filter_by(username=[Link]['ADMIN_USERNAME']).first()
if not admin:
admin = AdminUser(username=[Link]['ADMIN_USERNAME'], role='admin')
admin.set_password([Link]['ADMIN_PASSWORD'])
[Link](admin)
[Link]()
@[Link](404)
def page_not_found(e):
return render_template('[Link]'), 404
@[Link](500)
def internal_error(e):
[Link]()
return render_template('[Link]'), 500
@app.template_filter('datetime')
def format_datetime(value, format='%Y-%m-%d'):
if isinstance(value, str):
return value
return [Link](format)
if __name__ == '__main__':
init_db()
[Link](debug=True, host="[Link]", port=9900)