1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use question::*;
use sea_orm::{sea_query::OnConflict, EntityTrait, IntoActiveModel};
use serde::{Deserialize, Serialize};
use tracing::error;

use crate::{
    dao::{glob_db, InsertToDB},
    entities::{index, prelude::*},
};

#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct Problems {
    pub user_name:         String,
    pub num_solved:        u32,
    pub num_total:         u32,
    pub ac_easy:           u32,
    pub ac_medium:         u32,
    pub ac_hard:           u32,
    pub stat_status_pairs: Vec<QsIndex>,
}

/// base info of question
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct QsIndex {
    #[serde(default)]
    pub stat:       Stat,
    #[serde(default)]
    pub status:     Option<String>,
    #[serde(default)]
    pub difficulty: Difficulty,
    #[serde(default)]
    pub paid_only:  bool,
    #[serde(default)]
    pub is_favor:   bool,
    #[serde(default)]
    pub frequency:  u32,
    #[serde(default)]
    pub progress:   u32,
}

pub mod question {
    use lcode_config::config::{global::USER_CONFIG, user_nest::Suffix};
    use serde::{Deserialize, Deserializer, Serialize};

    #[derive(Default, Debug, Clone, Deserialize, Serialize)]
    pub struct Stat {
        pub question_id:          u32,
        #[serde(rename = "question__title")]
        pub question_title:       String,
        #[serde(rename = "question__title_slug")]
        pub question_title_slug:  String,
        #[serde(rename = "question__hide")]
        pub question_hide:        bool,
        pub total_acs:            u32,
        pub total_submitted:      u32,
        #[serde(default, deserialize_with = "my_id_deserialize")]
        pub frontend_question_id: String,
    }

    fn my_id_deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
    where
        D: Deserializer<'de>,
    {
        let res = match USER_CONFIG.config.url_suffix {
            Suffix::Cn => String::deserialize(deserializer)?,
            Suffix::Com => u32::deserialize(deserializer)?.to_string(),
        };
        Ok(res)
    }

    #[derive(Default, Debug, Clone, Deserialize, Serialize)]
    pub struct Difficulty {
        pub level: u32,
    }
}

impl InsertToDB for QsIndex {
    type Value = String;
    type Entity = index::Entity;
    type Model = index::Model;
    type ActiveModel = index::ActiveModel;

    fn to_model(&self, category: String) -> Self::Model {
        let pat = serde_json::to_string(self).unwrap_or_default();
        let mut model: index::Model = serde_json::from_str(&pat).unwrap_or_default();
        model.category = category;
        model.pass_rate =
            Some(self.stat.total_acs as f64 / self.stat.total_submitted as f64 * 100.0);
        model.question_id = self.stat.question_id;
        model.question_title = self.stat.question_title.clone();
        model.question_title_slug = self
            .stat
            .question_title_slug
            .clone();
        model.total_acs = self.stat.total_acs;
        model.total_submitted = self.stat.total_submitted;
        model.frontend_question_id = self
            .stat
            .frontend_question_id
            .clone();
        model.difficulty = self.difficulty.level;

        model
    }

    async fn insert_to_db(&mut self, category: String) {
        match Index::insert(
            self.to_model(category)
                .into_active_model(),
        )
        .on_conflict(Self::on_conflict())
        .exec(glob_db().await)
        .await
        {
            Ok(_) => {},
            Err(err) => error!("{}", err),
        };
    }
    fn on_conflict() -> OnConflict {
        OnConflict::column(index::Column::QuestionId)
            .update_columns([
                index::Column::QuestionTitle,
                index::Column::QuestionTitleSlug,
                index::Column::QuestionId,
                index::Column::FrontendQuestionId,
                index::Column::TotalAcs,
                index::Column::TotalSubmitted,
                index::Column::Status,
                index::Column::Difficulty,
                index::Column::PaidOnly,
                index::Column::IsFavor,
                index::Column::Frequency,
                index::Column::Progress,
                index::Column::Category,
                index::Column::PassRate,
            ])
            .to_owned()
    }
}