9 Commits

Author SHA1 Message Date
747a611a67 use chgk_ledb_lib 2022-11-11 20:56:35 +03:00
1eadfac11a Merge branch 'rocket' 2022-10-04 00:20:27 +03:00
1b833cff90 add README 2022-10-04 00:17:02 +03:00
3097ef9f08 add LICENSE 2022-10-04 00:11:17 +03:00
0f837db0a2 refactor show_question_details 2022-10-04 00:07:25 +03:00
b3fcc66b1b upd deps 2022-10-04 00:06:08 +03:00
8231274658 back to rand 0.7 2021-02-06 17:38:25 +03:00
ee2e8cd560 upd + show answer button 2021-02-06 00:46:53 +03:00
eb6349b51b upd ledb to 0.4 2020-08-18 21:51:46 +03:00
6 changed files with 1285 additions and 830 deletions

1897
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,21 @@
[package]
name = "qchgk_web"
version = "0.1.0"
version = "0.1.1"
authors = ["Dmitry <b4tm4n@mail.ru>"]
edition = "2018"
edition = "2021"
license = "MIT"
readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand="0.7"
rand="0.8"
serde="1.0"
serde_derive="1.0"
serde_json="1.0"
ledb="0.3"
ledb-derive="0.3"
ledb-types="0.3"
ledb = {git = "https://github.com/b4tman/ledb.git", rev="a646b90e", package="ledb"}
rocket="0.4"
chgk_ledb_lib = {git = "https://gitea.b4tman.ru/b4tman/chgk_ledb.git", rev="e521e39f5e", package="chgk_ledb_lib"}
# lmdb-zero="0.4"
@@ -28,3 +28,4 @@ features = ["serve", "tera_templates"]
opt-level = 3
debug = false
lto = true
strip = true

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Dmitry Belyaev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

10
README.md Normal file
View File

@@ -0,0 +1,10 @@
# qchgk_web
Простой веб интерфейс к базе вопросов ЧГК
## Ссылки
- Источник вопросов: http://db.chgk.info
- Копия файлов базы вопросов: https://gitlab.com/b4tman/db_chgk
- Конвертор в JSON: https://gitea.b4tman.ru/b4tman/chgk_txt2json
- Загрузчик в формат базы для этой штуки: https://gitea.b4tman.ru/b4tman/chgk_ledb

View File

@@ -1,18 +1,8 @@
#![feature(proc_macro_hygiene, decl_macro)]
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate ledb;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate ledb_derive;
extern crate ledb_types;
//#[macro_use]
//extern crate tera;
//use tera::Context;
#[macro_use]
extern crate rocket;
@@ -28,77 +18,18 @@ use rand::Rng;
use ledb::{Options, Storage};
#[derive(Debug, Default, Clone, Serialize, Deserialize, Document)]
struct BatchInfo {
#[document(primary)]
#[serde(default)]
filename: String,
#[serde(default)]
description: String,
#[serde(default)]
author: String,
#[serde(default)]
comment: String,
#[serde(default)]
url: String,
#[serde(default)]
date: String,
#[serde(default)]
processed_by: String,
#[serde(default)]
redacted_by: String,
#[serde(default)]
copyright: String,
#[serde(default)]
theme: String,
#[serde(default)]
kind: String,
#[serde(default)]
source: String,
#[serde(default)]
rating: String,
use chgk_ledb_lib::questions::Question;
trait ErrorEmpty {
type Output;
fn err_empty(self) -> Result<Self::Output, ()>;
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, Document)]
struct Question {
#[document(primary)]
#[serde(default)]
num: u32,
#[document(index)]
id: String,
description: String,
answer: String,
#[serde(default)]
author: String,
#[serde(default)]
comment: String,
#[serde(default)]
comment1: String,
#[serde(default)]
tour: String,
#[serde(default)]
url: String,
#[serde(default)]
date: String,
#[serde(default)]
processed_by: String,
#[serde(default)]
redacted_by: String,
#[serde(default)]
copyright: String,
#[serde(default)]
theme: String,
#[serde(default)]
kind: String,
#[serde(default)]
source: String,
#[serde(default)]
rating: String,
#[document(nested)]
#[serde(default)]
batch_info: BatchInfo,
impl<T, E> ErrorEmpty for Result<T, E> {
type Output = T;
fn err_empty(self) -> Result<Self::Output, ()> {
self.map_err(|_| ())
}
}
struct AppState {
@@ -120,66 +51,53 @@ fn random_question_id(database_distribution: &Uniform<u32>) -> u32 {
rng.sample(database_distribution)
}
fn get_question(storage: &Storage, id: u32) -> Result<Option<Question>, ()> {
if 0 == id {
return Ok(None);
}
let collection = storage
fn get_question(storage: &Storage, id: u32) -> Result<Question, ()> {
storage
.collection("questions")
.expect("collection \"questions\"");
let last_id = collection.last_id().expect("\"questions\" last_id");
if id > last_id {
Err(())
} else {
let question = collection.get::<Question>(id);
if question.is_err() {
Err(())
} else {
Ok(question.unwrap())
}
}
.err_empty()?
.get::<Question>(id)
.err_empty()?
.ok_or(())
}
fn show_question_details(
template_name: &'static str,
data: &AppState,
id: u32,
) -> Result<Template, Redirect> {
let question = get_question(&data.storage, id);
if question.is_ok() {
let question = question.unwrap();
if question.is_some() {
let question = question.unwrap();
fn show_question_details(template_name: &'static str, data: &AppState, id: u32) -> Template {
match get_question(&data.storage, id) {
Ok(question) => {
let mut context = serde_json::to_value(question).expect("question serialize");
if context.is_object() {
let next_id = random_question_id(&data.database_distribution);
context["next"] = serde_json::to_value(next_id).expect("question id serialize");
}
Ok(Template::render(template_name, &context))
} else {
Err(Redirect::permanent("/q/"))
Template::render(template_name, &context)
}
Err(_) => {
use std::collections::HashMap;
let context: HashMap<String, String> = HashMap::new();
Template::render("404", &context)
}
} else {
use std::collections::HashMap;
let context: HashMap<String, String> = HashMap::new();
Ok(Template::render("404", &context))
}
}
#[get("/q/<id>")]
fn show_question(data: State<AppState>, id: u32) -> Result<Template, Redirect> {
fn show_question(data: State<AppState>, id: u32) -> Template {
show_question_details("question", data.inner(), id)
}
#[get("/q/<id>/a")]
fn show_answer(data: State<AppState>, id: u32) -> Result<Template, Redirect> {
fn show_answer(data: State<AppState>, id: u32) -> Template {
show_question_details("answer", data.inner(), id)
}
#[get("/q/0")]
fn question0() -> Redirect {
Redirect::to("/")
}
#[get("/q/0/a")]
fn answer0() -> Redirect {
Redirect::to("/")
}
#[get("/")]
fn index(data: State<AppState>) -> Redirect {
let id = random_question_id(&data.database_distribution);
@@ -211,7 +129,10 @@ fn rocket() -> Rocket {
rocket::ignite()
.manage(state)
.register(catchers![not_found])
.mount("/", routes![index, show_question, show_answer])
.mount(
"/",
routes![index, show_question, show_answer, question0, answer0],
)
.mount("/q", routes![index])
.mount("/q/static", StaticFiles::from("static/"))
.attach(Template::fairing())

View File

@@ -11,9 +11,8 @@
</div></div>
<br/>
<br/>
<br/><details>
<summary>Ответ</summary><br/>
<a class="btn btn-dark shadow" href="/q/{{ num }}/a/">Показать ответ</a>
</details>
<br/>
<div class="justify-content-center text-center">
<a class="btn btn-dark shadow" href="/q/{{ num }}/a/">Показать ответ</a>
</div>
{% endblock content %}