1
use crate::server::{
2
    dataset::{Dataset, PredicateObject},
3
    DatasetSummary, DatasetVersionSummary,
4
};
5
use askama::Template;
6
use sophia::api::{prefix::PrefixMapPair, term::IriRef, MownStr};
7
use std::collections::BTreeSet;
8

            
9
2
#[derive(Template)]
10
2
#[template(path = "index.html")]
11
pub(crate) struct IndexTemplate<'a> {
12
    pub datasets: &'a [DatasetSummary<'a>],
13
}
14

            
15
1
#[derive(Template)]
16
1
#[template(path = "dataset.html")]
17
pub(crate) struct DatasetTemplate<'a> {
18
    pub dataset: &'a Dataset,
19
    pub versions: &'a [DatasetVersionSummary],
20
}
21

            
22
1063
#[derive(Template)]
23
1
#[template(path = "version.html")]
24
pub(crate) struct VersionTemplate<'a> {
25
    pub dataset: &'a DatasetSummary<'a>,
26
    pub version: &'a DatasetVersionSummary,
27
    pub subjects: BTreeSet<&'a IriRef<MownStr<'a>>>,
28
    pub datasets: &'a Vec<Dataset>,
29
    pub prefixes: &'a Vec<PrefixMapPair>,
30
}
31

            
32
6
#[derive(Template)]
33
1
#[template(path = "subject.html")]
34
pub(crate) struct SubjectTemplate<'a> {
35
    pub dataset: &'a DatasetSummary<'a>,
36
    pub subject: String,
37
    pub version: &'a DatasetVersionSummary,
38
    pub pos: Vec<PredicateObject<'a>>,
39
    pub datasets: &'a Vec<Dataset>,
40
    pub prefixes: &'a Vec<PrefixMapPair>,
41
}
42

            
43
mod filters {
44
    use std::{collections::BTreeSet, fmt};
45

            
46
    use askama::Error::Fmt;
47
    use chrono::{DateTime, Utc};
48
    use sophia::{
49
        api::{prefix::{PrefixMap, PrefixMapPair}, term::IriRef, MownStr},
50
        iri::Iri,
51
    };
52

            
53
    use crate::{constants::LABEL_RDFS_IRI, server::{dataset::Dataset, get_dataset_version}};
54

            
55
    /// Filter to replace the prefix in a resource IRI
56
124
    pub(crate) fn replace_prefix<T: std::fmt::Display>(s: T, prefixes: &Vec<PrefixMapPair>) -> ::askama::Result<String> {
57
124
        let prefix_found = prefixes.get_prefixed_pair(Iri::new_unchecked(s.to_string()));
58

            
59
124
        let out = if let Some(ps) = prefix_found {
60
122
            format!("{}:{}", ps.0.as_str(), ps.1)
61
        } else {
62
2
            s.to_string()
63
        };
64

            
65
124
        Ok(out)
66
124
    }
67

            
68
    /// Filter to find and display the rdfs:label resource for a subject
69
123
    pub(crate) fn rdfslabel<T: std::fmt::Display>(
70
123
        s: T,
71
123
        datasets: &Vec<Dataset>,
72
123
        id: &&str,
73
123
        version: &DateTime<Utc>,
74
123
    ) -> ::askama::Result<String> {
75
123
        let subject = Iri::new_unchecked(s.to_string());
76
123
        let Ok((_, dataset_version)) = get_dataset_version(&datasets, id, *version) else {
77
            return Err(Fmt(fmt::Error));
78
        };
79
123
        let pos = dataset_version.pos(&subject);
80
123

            
81
123
        let find_target_po = pos
82
123
            .into_iter()
83
379
            .find(|po| po.predicate.as_str() == LABEL_RDFS_IRI);
84
123
        let Some(target_po) = find_target_po else {
85
7
            return Ok("".to_string());
86
        };
87
116
        let Some(target_object) = target_po.object_literal else {
88
            return Ok("".to_string());
89
        };
90

            
91
116
        Ok(target_object.to_string())
92
123
    }
93

            
94
    /// Filter to sort subjects by prefix
95
    /// 
96
    /// Each prefix becomes a category.  
97
    /// This filter has a list of hardcoded prefixes that it will place in front of the other prefixes.  
98
    /// This filter returns a tuple of two vectors:
99
    ///   1. the list of names for each prefix/category
100
    ///   2. the index of the prefix/category assigned to each subject
101
1
    pub(crate) fn categorize_subjects<'a>(
102
1
        subs: &BTreeSet<&'a IriRef<MownStr<'a>>>,
103
1
        prefixes: &Vec<PrefixMapPair>,
104
1
    ) -> ::askama::Result<(Vec<String>, Vec<usize>)> {
105
1
        let special_categories: Vec<&str> = vec!["proposal", "nlnet"];
106
1
        let mut categories: Vec<String> = prefixes
107
1
            .iter()
108
7
            .map(|(p, _)| p.to_string())
109
7
            .filter(|cat| !special_categories.contains(&cat.as_str()))
110
1
            .collect();
111
1

            
112
1
        let cat_assigns: Vec<usize> = subs
113
1
            .into_iter()
114
118
            .map(|s| {
115
118
                let prefix_found = prefixes.get_prefixed_pair(Iri::new_unchecked(s.to_string()));
116

            
117
118
                if let Some((prefix, _)) = prefix_found {
118
116
                    if let Some(idx) = special_categories
119
116
                        .iter()
120
232
                        .position(|cat| cat == &prefix.as_str())
121
                    {
122
                        return idx;
123
116
                    } else if let Some(idx) =
124
471
                        categories.iter().position(|cat| cat == &prefix.as_str())
125
                    {
126
116
                        return idx + special_categories.len();
127
                    }
128
2
                }
129

            
130
2
                categories.len() + special_categories.len()
131
118
            })
132
1
            .collect();
133
1

            
134
1
        let mut all_categories = vec!["Proposals".to_string(), "NLnet".to_string()];
135
1
        let mut others = vec!["Other".to_string()];
136
1
        all_categories.append(&mut categories);
137
1
        all_categories.append(&mut others);
138
1

            
139
1
        Ok((all_categories, cat_assigns))
140
1
    }
141
}