aboutsummaryrefslogblamecommitdiff
path: root/src/format.rs
blob: 3519b13b3e12b3e320b2b156a1f686b780e91d19 (plain) (tree)
1
2
3
4
5
6
7
            
                   



                            
                                    






                                                                                              



                                                                                      

 

                           




                             
           
            










                                                                          
                                                           

                       
                                                                       
                
                                        

         

                                                    
 
                                      




                                                         
                    





                                                                             
                              

                                     

                                                  



                                                    
                                                  
                               
                                                  
                               
                                                   





                                      
                        


                                                                                            
                    
                                      

             

                                                         
 
                                                                     




















                                                              
                                               
         
                                               

                                                              
                    



                                                                             
                                  
                            
                                               
         
                                        

     
                             
                 
            







                                                       
            








                                                        
            
                                                                                                

                                           
                 
                
                                                                                                                                         
                                 
                 

           
                                  
 

                                               
 

                                        


          
                                                                                    

                          
                
                                                                      



                        
                    
                                                                                                                                                                                                                    


                                              
                                   



          



































                                                                                                  







                                                  
           
            









                                                                          



                                                                 
 

                                                                                                                                          

                                                  
                










                                                                                                                                                                                                                
                                  
 
                             






                                                     
                              
             
            








                                                                                                  
                







                                                                                                                         
                               
 

                                         




                                                                           



                                                            
           
            









                                                                          
                                                  
             
            



                                                                  
            



                                                            
                                              


          
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<'a>(dict_idx: &DictIndex<'a>, 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<'a>(
    buf: &mut impl Write,
    dict_idx: &DictIndex<'a>,
    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><table class="extratable">"#
    )?;
    for ex in batch.extra_examples.iter() {
        writeln!(
            buf,
            r#"<tr><td><div class="extra_example"><div class="extra_ja font_ja">{}</div><div class="extra_en">{}</div></div></td></tr>"#,
            ex.furigana_markup(),
            ex.en
        )?;
    }
    writeln!(buf, r#"</table>"#)?;

    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">&nbsp;&nbsp;<span class="tab_large font_ja">{}</span>&nbsp;&nbsp;</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: &roxmltree::Node<'a, 'a>,
) -> Option<String> {
    let r_ele = ent.children().find(|x| x.has_tag_name("r_ele")).unwrap();
    let reb = r_ele.children().find(|x| x.has_tag_name("reb")).unwrap();
    let reb = reb.text().unwrap().trim();

    if qreb.map(|x| x != reb).unwrap_or(false) {
        return None;
    }

    Some(format!(
        r#"<span class="font_ja">{} 【{}】</span>"#,
        qkeb, reb
    ))
}

fn dict_str<'a>(qkeb: &str, qreb: Option<&str>, ent: &roxmltree::Node<'a, 'a>) -> Option<String> {
    let mut ret = dict_str_short(qkeb, qreb, ent)?;

    for sense in ent.children().filter(|x| x.has_tag_name("sense")) {
        if let Some(s) = sense.children().find(|x| x.has_tag_name("gloss")) {
            ret.extend(format!(" {};", s.text().unwrap().trim()).chars());
        }
    }

    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>&nbsp;&nbsp;{}</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 />")?;

    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(())
}