use std::fs;
use std::io::Write;
use anyhow::Result;
use crate::charset::Charset;
use crate::example::expl_clean_word;
use crate::*;
// =====================================================================
// FORMATTING TO HTML
// =====================================================================
pub fn format_batch(dict_idx: &DictIndex, count: usize, (i, batch): (usize, &Batch)) {
let mut f = io::BufWriter::new(
fs::File::create(format!("public/{:03}.html", i)).expect("create batch file"),
);
format_batch_to(&mut f, dict_idx, count, i, batch).expect("format batch");
}
pub fn format_batch_to(
buf: &mut impl Write,
dict_idx: &DictIndex,
count: usize,
i: usize,
batch: &Batch,
) -> Result<()> {
write!(
buf,
r#"<!DOCTYPE html>
<html>
<head>
<meta charset=\"UTF-8\" />
<title>Batch #{:03}</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body><div class="batch_page">"#,
i
)?;
writeln!(buf, r#"<p><a href="index.html">index</a>"#)?;
for j in 0..count {
if j != i {
writeln!(buf, r#" <a href="{:03}.html">{:03}</a>"#, j, j)?;
} else {
writeln!(buf, " {:03}", j)?;
}
}
writeln!(buf, r#"</p>"#)?;
writeln!(buf, "<p>Level: {}</p>", batch.level)?;
write!(buf, r#"<p class="ja">"#)?;
let mut ex_prev = Charset::default();
for ex in batch.examples.iter() {
let ex_chars = ex.chars.inter(&batch.chars);
for c in ex_chars.diff(&ex_prev).chars().iter() {
write!(
buf,
r#"<a href="https://jisho.org/search/{}%20%23kanji">{}</a>"#,
c, c
)?;
}
ex_prev = ex_prev.union(&ex_chars);
}
writeln!(buf, r#"</p>"#)?;
for ex in batch.examples.iter() {
writeln!(buf, "<hr />")?;
write!(buf, r#"<p class="ja ja_main">"#)?;
let furi = ex.furigana_markup();
for c in furi.chars() {
let class = if batch.chars.contains(c) {
Some("char_cur")
} else if batch.chars_p1.contains(c) {
Some("char_p1")
} else if batch.chars_p2.contains(c) {
Some("char_p2")
} else if batch.chars_bad.contains(c) {
Some("char_bad")
} else {
None
};
if let Some(cls) = class {
write!(
buf,
r#"<a href="https://jisho.org/search/{}%20%23kanji" class="{}">{}</a>"#,
c, cls, c
)?;
} else {
write!(buf, "{}", c)?;
}
}
writeln!(buf, "</p>")?;
writeln!(buf, r#"<p class="en">{}</p>"#, ex.en)?;
writeln!(buf, r#"<details><summary>Explanation</summary>"#)?;
let mut expl_batch = Vec::new();
let mut expl_all = Vec::new();
for word in ex.expl.split(|c| c == ' ' || c == '~') {
let (keb, reb) = expl_clean_word(word);
let wchars = Charset::new(keb);
if !wchars.intersects(&ex.chars) {
continue;
}
if let Some(ents) = dict_idx.get(keb) {
for ent in ents.iter() {
if let Some(s) = dict_str(keb, reb, ent) {
if wchars.intersects(&batch.chars) {
expl_batch.push(s);
} else {
expl_all.push(s);
}
}
}
}
}
for be in expl_batch {
writeln!(buf, r#"<p>{}</p>"#, be)?;
}
writeln!(buf, r#"<p class="chars">"#)?;
for c in ex.chars.inter(&batch.chars).chars().iter() {
writeln!(
buf,
r#"<a href="https://jisho.org/search/{}%20%23kanji">{}</a>"#,
c, c
)?;
}
writeln!(buf, r#"</p>"#)?;
for be in expl_all {
writeln!(buf, r#"<p>{}</p>"#, be)?;
}
writeln!(buf, r#"</details>"#)?;
}
writeln!(buf, "<hr />")?;
format_vocab(
buf,
&batch
.extra_vocab
.iter()
.filter(|v| batch.level.contains(&v.level))
.collect::<Vec<_>>(),
"Extra vocabulary (this level)",
)?;
format_vocab(
buf,
&batch
.extra_vocab
.iter()
.filter(|v| !batch.level.contains(&v.level))
.collect::<Vec<_>>(),
"Extra vocabulary (previous levels)",
)?;
writeln!(
buf,
r#"<p><strong>Extra examples (reading practice)</strong></p>"#
)?;
writeln!(
buf,
r#"<form method="POST" action="gen.html">
<p>practice levels<p>
<p><select name="first_level" value="0">
"#
)?;
for val in 0..=i {
write!(buf, r#"<option value="{}">{:03}</option>"#, val, val)?;
}
writeln!(
buf,
r#"</select> - <input type="hidden" name="last_level" value="{}" />{:03}</p>
<p><input type="submit" value="practice random sentences" /></p></form>"#,
i, i
)?;
writeln!(buf, "<hr />")?;
writeln!(buf, "<p>\(≧▽≦)/</p>")?;
write!(buf, "<div></body></html>")?;
buf.flush()?;
Ok(())
}
fn format_vocab(buf: &mut impl Write, vocab: &[&JlptVocab], t: &str) -> Result<()> {
if !vocab.is_empty() {
writeln!(
buf,
r#"<p><strong>{}</strong></p><table class="vocabtable">"#,
t
)?;
for v in vocab {
writeln!(
buf,
r#"<tr><td>{}</td><td style="word-break: keep-all"> <span class="tab_large font_ja">{}</span> </td><td>{}</td><td class="font_ja" style="word-break: keep-all">{}</td></tr>"#,
v.level, v.kanji, v.en, v.kana
)?;
}
writeln!(buf, "</table>")?;
}
Ok(())
}
fn dict_str_short<'a>(qkeb: &str, qreb: Option<&str>, ent: &DictEntry) -> Option<String> {
if qreb.map(|x| x != ent.reb).unwrap_or(false) {
return None;
}
Some(format!(
r#"<span class="font_ja">{} 【{}】</span>"#,
qkeb, ent.reb
))
}
fn dict_str<'a>(qkeb: &str, qreb: Option<&str>, ent: &DictEntry) -> Option<String> {
let mut ret = dict_str_short(qkeb, qreb, ent)?;
for sense in ent.sense.iter() {
ret += &format!(" {};", sense);
}
if ret.chars().rev().next() == Some(';') {
ret.pop();
}
Some(ret)
}
pub fn format_index(batches: &[Batch], kanji_levels: &[(String, String)]) -> Result<()> {
let mut f = io::BufWriter::new(fs::File::create("public/index.html")?);
format_index_to(&mut f, batches, kanji_levels)
}
pub fn format_index_to(
buf: &mut impl Write,
batches: &[Batch],
kanji_levels: &[(String, String)],
) -> Result<()> {
write!(
buf,
r#"<!DOCTYPE html>
<html>
<head>
<meta charset=\"UTF-8\" />
<title>List of batches</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body><div class="index_page">"#
)?;
writeln!(
buf,
r#"<p><a href="about.html">About / How-to</a></p><hr />"#
)?;
writeln!(buf, "<table>")?;
writeln!(buf, "<tr><th>Num</th><th>Level</th><th>Kanji</th><th>Examples</th><th>Lesson-1</th><th>Lesson-2</th><th>Ignore</th></tr>")?;
for (i, batch) in batches.iter().enumerate() {
writeln!(
buf,
r#"<tr><td><a href="{:03}.html">{:03}</a></td><td>{}</td><td class="font_ja">{}</td><td> {}</td><td class="font_ja">{}</td><td class="font_ja">{}</td><td class="font_ja">{}</td></tr>"#,
i,
i,
batch.level,
batch.chars.to_string(),
batch.examples.len(),
batch.chars_p1.to_string(),
batch.chars_p2.to_string(),
batch.chars_bad.to_string()
)?;
}
writeln!(buf, r#"</table>"#)?;
writeln!(buf, "<hr />")?;
writeln!(buf, r#"<p><strong>Extra reading practice</strong></p>"#)?;
writeln!(
buf,
r#"<form method="POST" action="gen.html">
<p>practice levels<p>
<p><select name="first_level" value="0">
"#
)?;
for val in 0..batches.len() {
write!(buf, r#"<option value="{}">{:03}</option>"#, val, val)?;
}
writeln!(buf, r#"</select> - <select name="last_level" value="0">"#)?;
for val in 0..batches.len() {
write!(buf, r#"<option value="{}">{:03}</option>"#, val, val)?;
}
writeln!(
buf,
r#"</select></p>
<p><input type="submit" value="practice random sentences" /></p></form>"#
)?;
writeln!(buf, "<hr />")?;
let all_chars = Charset::from_iter(
batches
.iter()
.map(|x| x.chars.chars().iter().copied())
.flatten(),
);
writeln!(buf, "<table>")?;
writeln!(
buf,
r#"<tr><th>Level</th><th>Count</th><th width="60%">Kanji</th><th>Missing kanji</th></tr>"#
)?;
for (lvl, chars) in kanji_levels.iter() {
if lvl == "N0+" || lvl.ends_with("-10") {
continue;
}
let chars = Charset::new(chars);
let missing = chars.diff(&all_chars);
writeln!(
buf,
r#"<tr><td>{}</td><td>{}</td><td class="font_ja">{}</td><td><span class="font_ja">{}</span> ({})</td></tr>"#,
lvl,
chars.len(),
chars.to_string(),
missing.to_string(),
missing.len()
)?;
}
writeln!(buf, "</table>")?;
write!(buf, "</div></body></html>")?;
buf.flush()?;
Ok(())
}
pub fn format_about() -> Result<()> {
let mut f = io::BufWriter::new(fs::File::create("public/about.html")?);
format_about_to(&mut f)
}
pub fn format_about_to(buf: &mut impl Write) -> Result<()> {
write!(
buf,
r#"<!DOCTYPE html>
<html>
<head>
<meta charset=\"UTF-8\" />
<title>Datagengo README</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>"#
)?;
writeln!(buf, r#"<div class="about_page">"#)?;
writeln!(
buf,
r#"<p><a href="index.html">Back to lessons</a></p><hr />"#
)?;
writeln!(
buf,
"{}",
markdown::to_html(&fs::read_to_string("README.md")?)
)?;
writeln!(buf, r#"</div></body></html>"#)?;
Ok(())
}