leetcode_tui_db/models/
question.rs1use super::{topic::DbTopic, *};
2use crate::{
3 api::types::problemset_question_list::Question,
4 errors::{DBResult, DbErr},
5 get_db_client, save, save_multiple,
6};
7use std::fmt::Display;
8
9#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
10#[native_model(id = 1, version = 1)]
11#[native_db]
12pub struct DbQuestion {
13 #[primary_key]
14 pub id: u32,
15 pub title: String,
16 pub title_slug: String,
17 pub difficulty: String,
18 pub paid_only: bool,
19 pub status: Option<String>,
20 pub topics: Vec<DbTopic>,
21}
22
23impl Ord for DbQuestion {
24 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
25 self.id.cmp(&other.id)
26 }
27}
28
29impl PartialOrd for DbQuestion {
30 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
31 self.id.partial_cmp(&other.id)
32 }
33}
34
35impl Eq for DbQuestion {}
36
37impl Display for DbQuestion {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 let mut w = String::new();
40 w.push_str(if self.paid_only { "🔐" } else { " " });
41 w.push_str(if self.status.is_none() {
42 " "
43 } else if self.status == Some("ac".into()) {
44 "👑"
45 } else {
46 "🏃"
47 });
48 w.push_str(self.title.as_str());
49 write!(f, "{: >4}{w}", self.id)
50 }
51}
52
53impl DbQuestion {
54 pub fn is_hard(&self) -> bool {
55 self.difficulty == "Hard"
56 }
57
58 pub fn is_medium(&self) -> bool {
59 self.difficulty == "Medium"
60 }
61
62 pub fn is_easy(&self) -> bool {
63 self.difficulty == "Easy"
64 }
65}
66
67impl TryFrom<Question> for DbQuestion {
68 type Error = DbErr;
69
70 fn try_from(
71 value: crate::api::types::problemset_question_list::Question,
72 ) -> Result<Self, Self::Error> {
73 let mut db_quest = DbQuestion::new(
74 value.frontend_question_id.parse()?,
75 value.title.as_str(),
76 value.title_slug.as_str(),
77 value.difficulty,
78 value.paid_only,
79 value.status,
80 );
81 if let Some(tts) = value.topic_tags {
82 if !tts.is_empty() {
83 for topic in tts {
84 db_quest.add_topic(topic.slug.as_str());
85 }
86 } else {
87 db_quest.add_topic("unknown");
88 }
89 }
90 Ok(db_quest)
91 }
92}
93
94impl DbQuestion {
95 pub fn new(
96 id: u32,
97 title: &str,
98 title_slug: &str,
99 difficulty: String,
100 paid_only: bool,
101 status: Option<String>,
102 ) -> Self {
103 Self {
104 id,
105 title: title.into(),
106 title_slug: title_slug.into(),
107 topics: Default::default(),
108 difficulty,
109 paid_only,
110 status,
111 }
112 }
113
114 fn add_topic(&mut self, slug: &str) {
115 self.topics.push(DbTopic::new(slug))
116 }
117
118 pub fn mark_accepted<'a>(&mut self) -> DBResult<Option<Vec<Self>>> {
119 if self.status.is_none() || self.status == Some("notac".into()) {
120 self.status = Some("ac".into());
121 return Ok(Some(self.update_in_db()?));
122 }
123 Ok(None)
124 }
125
126 pub fn mark_attempted<'a>(&mut self) -> DBResult<Option<Vec<Self>>> {
127 if self.status.is_none() {
128 self.status = Some("notac".into());
129 return Ok(Some(self.update_in_db()?));
130 }
131 Ok(None)
132 }
133
134 fn update_in_db<'a>(&self) -> DBResult<Vec<Self>> {
135 let rw = get_db_client().rw_transaction()?;
136 let old = Self::get_question_by_id(self.id)?;
137 if let Some(old_q) = old {
138 rw.update(old_q, self.clone())?;
139 rw.commit()?;
140 }
141 Ok(vec![self.clone()])
142 }
143
144 pub fn get_total_questions<'a>() -> DBResult<usize> {
145 let r = get_db_client().r_transaction()?;
146 let x = r.scan().primary::<Self>()?;
147 Ok(x.all().count())
148 }
149
150 pub fn get_question_by_id<'a>(id: u32) -> DBResult<Option<Self>> {
151 let r = get_db_client().r_transaction()?;
152 let x = r.get().primary::<DbQuestion>(id)?;
153 Ok(x)
155 }
156
157 fn save_all_topics<'a>(&mut self) -> DBResult<()> {
158 save_multiple(&self.topics)
159 }
160
161 pub(crate) fn get_topics(&self) -> &Vec<DbTopic> {
162 &self.topics
163 }
164
165 fn get_topic_question_mapping(&self) -> Vec<TopicQuestionMap> {
166 self.get_topics()
167 .iter()
168 .map(|q| TopicQuestionMap::new(&q.slug, self.id))
169 .collect::<Vec<_>>()
170 }
171
172 fn get_question_topic_mapping(&self) -> Vec<QuestionTopicMap> {
173 self.get_topics()
174 .iter()
175 .map(|q| QuestionTopicMap::new(self.id, &q.slug))
176 .collect::<Vec<_>>()
177 }
178
179 pub fn save_multiple_to_db(questions: Vec<Self>) {
180 let topic_question_map = questions
181 .iter()
182 .map(|q| q.get_topic_question_mapping())
183 .flatten()
184 .collect::<Vec<_>>();
185
186 let question_topic_map = questions
187 .iter()
188 .map(|q| q.get_question_topic_mapping())
189 .flatten()
190 .collect::<Vec<_>>();
191
192 let topics = questions
193 .iter()
194 .map(|q| q.get_topics().iter().map(|t| t.clone()))
195 .flatten()
196 .collect::<Vec<_>>();
197
198 save_multiple(&topic_question_map).unwrap();
199 save_multiple(&question_topic_map).unwrap();
200 save_multiple(&topics).unwrap();
201 save_multiple(&questions).unwrap();
202 }
203
204 pub fn save_to_db<'a>(&mut self) -> DBResult<bool> {
205 TopicQuestionMap::save_mapping(self)?;
207
208 QuestionTopicMap::save_mapping(self)?;
210
211 self.save_all_topics()?;
213
214 save(self)?;
216 return Ok(true);
217 }
218}