Fixes for site
This commit is contained in:
@@ -391,6 +391,7 @@ def view_project(id):
|
||||
other_projects = Project.query.filter(Project.user_id == current_user.id, Project.id != id).all()
|
||||
|
||||
artifacts = []
|
||||
cover_image = None
|
||||
generated_books = {} # Map book_number -> {status: 'generated', run_id: int, folder: str}
|
||||
|
||||
# Scan ALL completed runs to find the latest status of each book
|
||||
@@ -409,13 +410,28 @@ def view_project(id):
|
||||
b_num = int(parts[1])
|
||||
# Only add if we haven't found a newer version (runs are ordered desc)
|
||||
if b_num not in generated_books:
|
||||
generated_books[b_num] = {'status': 'generated', 'run_id': r.id, 'folder': d}
|
||||
# Find artifacts for direct download link
|
||||
book_path = os.path.join(run_dir, d)
|
||||
epub_file = next((f for f in os.listdir(book_path) if f.endswith('.epub')), None)
|
||||
docx_file = next((f for f in os.listdir(book_path) if f.endswith('.docx')), None)
|
||||
|
||||
generated_books[b_num] = {'status': 'generated', 'run_id': r.id, 'folder': d, 'epub': os.path.join(d, epub_file).replace("\\", "/") if epub_file else None, 'docx': os.path.join(d, docx_file).replace("\\", "/") if docx_file else None}
|
||||
except: pass
|
||||
|
||||
# Collect Artifacts from Latest Run
|
||||
if latest_run:
|
||||
run_dir = os.path.join(proj.folder_path, "runs", "bible", f"run_{latest_run.id}")
|
||||
if os.path.exists(run_dir):
|
||||
# Find Cover Image (Root or First Book)
|
||||
if os.path.exists(os.path.join(run_dir, "cover.png")):
|
||||
cover_image = "cover.png"
|
||||
else:
|
||||
subdirs = sorted([d for d in os.listdir(run_dir) if os.path.isdir(os.path.join(run_dir, d)) and d.startswith("Book_")])
|
||||
for d in subdirs:
|
||||
if os.path.exists(os.path.join(run_dir, d, "cover.png")):
|
||||
cover_image = os.path.join(d, "cover.png").replace("\\", "/")
|
||||
break
|
||||
|
||||
for root, dirs, files in os.walk(run_dir):
|
||||
for f in files:
|
||||
if f.lower().endswith(('.epub', '.docx')):
|
||||
@@ -426,7 +442,7 @@ def view_project(id):
|
||||
'type': f.split('.')[-1].upper()
|
||||
})
|
||||
|
||||
return render_template('project.html', project=proj, bible=bible_data, runs=runs, active_run=latest_run, artifacts=artifacts, personas=personas, generated_books=generated_books, other_projects=other_projects)
|
||||
return render_template('project.html', project=proj, bible=bible_data, runs=runs, active_run=latest_run, artifacts=artifacts, cover_image=cover_image, personas=personas, generated_books=generated_books, other_projects=other_projects)
|
||||
|
||||
@app.route('/project/<int:id>/run', methods=['POST'])
|
||||
@login_required
|
||||
@@ -718,18 +734,31 @@ def view_run(id):
|
||||
# Fetch Artifacts for Display
|
||||
run_dir = os.path.join(run.project.folder_path, "runs", "bible", f"run_{run.id}")
|
||||
|
||||
# Detect Book Subfolder (Series Support)
|
||||
book_dir = run_dir
|
||||
# Detect Books in Run (Series Support)
|
||||
books_data = []
|
||||
if os.path.exists(run_dir):
|
||||
subdirs = sorted([d for d in os.listdir(run_dir) if os.path.isdir(os.path.join(run_dir, d)) and d.startswith("Book_")])
|
||||
if subdirs: book_dir = os.path.join(run_dir, subdirs[0])
|
||||
|
||||
blurb_content = ""
|
||||
blurb_path = os.path.join(book_dir, "blurb.txt")
|
||||
if os.path.exists(blurb_path):
|
||||
with open(blurb_path, 'r', encoding='utf-8', errors='ignore') as f: blurb_content = f.read()
|
||||
if not subdirs: subdirs = ["."] # Handle legacy/flat runs
|
||||
|
||||
for d in subdirs:
|
||||
b_path = os.path.join(run_dir, d)
|
||||
b_info = {'folder': d, 'artifacts': [], 'cover': None, 'blurb': ''}
|
||||
|
||||
has_cover = os.path.exists(os.path.join(book_dir, "cover.png"))
|
||||
# Artifacts
|
||||
for f in os.listdir(b_path):
|
||||
if f.lower().endswith(('.epub', '.docx')):
|
||||
b_info['artifacts'].append({'name': f, 'path': os.path.join(d, f).replace("\\", "/")})
|
||||
|
||||
# Cover
|
||||
if os.path.exists(os.path.join(b_path, "cover.png")):
|
||||
b_info['cover'] = os.path.join(d, "cover.png").replace("\\", "/")
|
||||
|
||||
# Blurb
|
||||
blurb_p = os.path.join(b_path, "blurb.txt")
|
||||
if os.path.exists(blurb_p):
|
||||
with open(blurb_p, 'r', encoding='utf-8', errors='ignore') as f: b_info['blurb'] = f.read()
|
||||
|
||||
books_data.append(b_info)
|
||||
|
||||
# Load Bible Data for Dropdown
|
||||
bible_path = os.path.join(run.project.folder_path, "bible.json")
|
||||
@@ -737,6 +766,8 @@ def view_run(id):
|
||||
|
||||
# Load Tracking Data for Run Details
|
||||
tracking = {"events": [], "characters": {}, "content_warnings": []}
|
||||
# We load tracking from the first book found to populate the general stats
|
||||
book_dir = os.path.join(run_dir, books_data[0]['folder']) if books_data else run_dir
|
||||
if os.path.exists(book_dir):
|
||||
t_ev = os.path.join(book_dir, "tracking_events.json")
|
||||
t_ch = os.path.join(book_dir, "tracking_characters.json")
|
||||
@@ -748,7 +779,7 @@ def view_run(id):
|
||||
if os.path.exists(t_wn): tracking['content_warnings'] = utils.load_json(t_wn) or []
|
||||
|
||||
# Use dedicated run details template
|
||||
return render_template('run_details.html', run=run, log_content=log_content, blurb=blurb_content, has_cover=has_cover, bible=bible_data, tracking=tracking)
|
||||
return render_template('run_details.html', run=run, log_content=log_content, books=books_data, bible=bible_data, tracking=tracking)
|
||||
|
||||
@app.route('/run/<int:id>/status')
|
||||
@login_required
|
||||
@@ -817,7 +848,12 @@ def optimize_models():
|
||||
# Force refresh via AI module (safely handles failures)
|
||||
try:
|
||||
ai.init_models(force=True) # Force re-initialization and API scan
|
||||
flash("AI Models refreshed and optimized.")
|
||||
|
||||
# Refresh Style Guidelines
|
||||
if ai.model_logic:
|
||||
story.refresh_style_guidelines(ai.model_logic)
|
||||
|
||||
flash("AI Models refreshed and Style Guidelines updated.")
|
||||
except Exception as e:
|
||||
flash(f"Error refreshing models: {e}")
|
||||
|
||||
@@ -1080,6 +1116,29 @@ def admin_spend_report():
|
||||
|
||||
return render_template('admin_spend.html', report=report, days=days, total=total_period_spend)
|
||||
|
||||
@app.route('/admin/style', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def admin_style_guidelines():
|
||||
path = os.path.join(config.DATA_DIR, "style_guidelines.json")
|
||||
|
||||
if request.method == 'POST':
|
||||
ai_isms_raw = request.form.get('ai_isms', '')
|
||||
filter_words_raw = request.form.get('filter_words', '')
|
||||
|
||||
data = {
|
||||
"ai_isms": [x.strip() for x in ai_isms_raw.split('\n') if x.strip()],
|
||||
"filter_words": [x.strip() for x in filter_words_raw.split('\n') if x.strip()]
|
||||
}
|
||||
|
||||
with open(path, 'w') as f: json.dump(data, f, indent=2)
|
||||
flash("Style Guidelines updated successfully.")
|
||||
return redirect(url_for('admin_style_guidelines'))
|
||||
|
||||
# Load current (creates defaults if missing)
|
||||
data = story.get_style_guidelines()
|
||||
return render_template('admin_style.html', data=data)
|
||||
|
||||
@app.route('/admin/impersonate/<int:user_id>')
|
||||
@login_required
|
||||
@admin_required
|
||||
|
||||
Reference in New Issue
Block a user