Make the updates page interactive

This commit is contained in:
Clément Renault 2020-10-20 12:09:38 +02:00
parent 35c9a3c558
commit 03ca1ff634
No known key found for this signature in database
GPG Key ID: 92ADA4E935E71FA4
3 changed files with 102 additions and 29 deletions

View File

@ -1,13 +1,19 @@
$(window).on('load', function () { $(window).on('load', function () {
let url = 'ws://' + window.location.hostname + ':' + window.location.port + '/updates/ws'; let wsProtcol = "ws";
if (window.location.protocol === 'https') {
wsProtcol = 'wss';
}
let url = wsProtcol + '://' + window.location.hostname + ':' + window.location.port + '/updates/ws';
var socket = new WebSocket(url); var socket = new WebSocket(url);
socket.onmessage = function (event) { socket.onmessage = function (event) {
console.log(event.data); let status = JSON.parse(event.data);
if (event.data.endsWith("processed")) { if (status.type == 'Pending') {
const elem = document.createElement('li'); const elem = document.createElement('li');
elem.classList.add("document"); elem.classList.add("document");
elem.setAttribute("id", 'update-' + status.update_id);
const ol = document.createElement('ol'); const ol = document.createElement('ol');
const field = document.createElement('li'); const field = document.createElement('li');
@ -19,7 +25,7 @@ $(window).on('load', function () {
const content = document.createElement('div'); const content = document.createElement('div');
content.classList.add("content"); content.classList.add("content");
content.innerHTML = event.data; content.innerHTML = 'Pending ' + status.update_id;
field.appendChild(attribute); field.appendChild(attribute);
field.appendChild(content); field.appendChild(content);
@ -29,6 +35,18 @@ $(window).on('load', function () {
prependChild(results, elem); prependChild(results, elem);
} }
if (status.type == "Processing") {
const id = 'update-' + status.update_id;
const content = $(`#${id} .content`);
content.html('Processing ' + status.update_id);
}
if (status.type == "Processed") {
const id = 'update-' + status.update_id;
const content = $(`#${id} .content`);
content.html('Processed ' + status.update_id);
}
} }
}); });

View File

@ -11,7 +11,7 @@ use askama_warp::Template;
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use futures::stream; use futures::stream;
use heed::EnvOpenOptions; use heed::EnvOpenOptions;
use serde::Deserialize; use serde::{Serialize, Deserialize};
use structopt::StructOpt; use structopt::StructOpt;
use tokio::fs::File as TFile; use tokio::fs::File as TFile;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
@ -84,9 +84,19 @@ struct IndexTemplate {
#[derive(Template)] #[derive(Template)]
#[template(path = "updates.html")] #[template(path = "updates.html")]
struct UpdatesTemplate { struct UpdatesTemplate<M: Serialize + Send> {
db_name: String, db_name: String,
updates: Vec<String>, db_size: usize,
docs_count: usize,
updates: Vec<UpdateStatus<M>>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type")]
enum UpdateStatus<M> {
Pending { update_id: u64, meta: M },
Processing { update_id: u64, meta: M },
Processed { update_id: u64, meta: M },
} }
pub fn run(opt: Opt) -> anyhow::Result<()> { pub fn run(opt: Opt) -> anyhow::Result<()> {
@ -116,10 +126,14 @@ pub fn run(opt: Opt) -> anyhow::Result<()> {
let update_store = UpdateStore::open( let update_store = UpdateStore::open(
update_store_options, update_store_options,
update_store_path, update_store_path,
move |uid, meta: String, _content| { move |update_id, meta: String, _content| {
let _ = update_status_sender_cloned.send(format!("processing update {}", uid)); let processing = UpdateStatus::Processing { update_id, meta: meta.clone() };
let _ = update_status_sender_cloned.send(processing);
std::thread::sleep(Duration::from_secs(3)); std::thread::sleep(Duration::from_secs(3));
let _ = update_status_sender_cloned.send(format!("update {} processed", uid));
let processed = UpdateStatus::Processed { update_id, meta: meta.clone() };
let _ = update_status_sender_cloned.send(processed);
Ok(meta) Ok(meta)
})?; })?;
@ -149,19 +163,24 @@ pub fn run(opt: Opt) -> anyhow::Result<()> {
let mut updates = update_store.iter_metas(|processed, pending| { let mut updates = update_store.iter_metas(|processed, pending| {
let mut updates = Vec::new(); let mut updates = Vec::new();
for result in processed { for result in processed {
let (id, _) = result?; let (uid, meta) = result?;
updates.push(format!("update {} processed", id.get())); updates.push(UpdateStatus::Processed { update_id: uid.get(), meta });
} }
for result in pending { for result in pending {
let (id, _) = result?; let (uid, meta) = result?;
updates.push(format!("update {} pending", id.get())); updates.push(UpdateStatus::Pending { update_id: uid.get(), meta });
} }
Ok(updates) Ok(updates)
}).unwrap(); }).unwrap();
if header.contains("text/html") { if header.contains("text/html") {
updates.reverse(); updates.reverse();
let template = UpdatesTemplate { db_name: db_name.clone(), updates }; let template = UpdatesTemplate {
db_name: db_name.clone(),
db_size,
docs_count,
updates,
};
Box::new(template) as Box<dyn warp::Reply> Box::new(template) as Box<dyn warp::Reply>
} else { } else {
Box::new(warp::reply::json(&updates)) Box::new(warp::reply::json(&updates))
@ -289,7 +308,7 @@ pub fn run(opt: Opt) -> anyhow::Result<()> {
async fn buf_stream( async fn buf_stream(
update_store: Arc<UpdateStore<String>>, update_store: Arc<UpdateStore<String>>,
update_status_sender: broadcast::Sender<String>, update_status_sender: broadcast::Sender<UpdateStatus<String>>,
mut stream: impl futures::Stream<Item=Result<impl bytes::Buf, warp::Error>> + Unpin, mut stream: impl futures::Stream<Item=Result<impl bytes::Buf, warp::Error>> + Unpin,
) -> Result<impl warp::Reply, warp::Rejection> ) -> Result<impl warp::Reply, warp::Rejection>
{ {
@ -305,9 +324,9 @@ pub fn run(opt: Opt) -> anyhow::Result<()> {
let mmap = unsafe { memmap::Mmap::map(&file).unwrap() }; let mmap = unsafe { memmap::Mmap::map(&file).unwrap() };
let meta = String::from("I am the metadata"); let meta = String::from("I am the metadata");
let uid = update_store.register_update(&meta, &mmap[..]).unwrap(); let update_id = update_store.register_update(&meta, &mmap[..]).unwrap();
update_status_sender.send(format!("update {} pending", uid)).unwrap(); update_status_sender.send(UpdateStatus::Pending { update_id, meta }).unwrap();
eprintln!("Registering update {}", uid); eprintln!("update {} registered", update_id);
Ok(warp::reply()) Ok(warp::reply())
} }
@ -331,8 +350,11 @@ pub fn run(opt: Opt) -> anyhow::Result<()> {
update_status_receiver update_status_receiver
.into_stream() .into_stream()
.flat_map(|result| { .flat_map(|result| {
match result{ match result {
Ok(msg) => stream::iter(Some(Ok(Message::text(msg)))), Ok(status) => {
let msg = serde_json::to_string(&status).unwrap();
stream::iter(Some(Ok(Message::text(msg))))
},
Err(e) => { Err(e) => {
eprintln!("channel error: {:?}", e); eprintln!("channel error: {:?}", e);
stream::iter(None) stream::iter(None)

View File

@ -11,7 +11,7 @@
</head> </head>
<body> <body>
<section class="hero is-light"> <section class="hero is-light">
<div class="hero-body"> <div class="hero-body">
<div class="container"> <div class="container">
@ -21,6 +21,27 @@
<img id="logo-black" src="logo-black.svg" alt="milli logo in black"> <img id="logo-black" src="logo-black.svg" alt="milli logo in black">
</figure> </figure>
</div> </div>
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">Database Name</p>
<p class="title">{{ db_name }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Database Size</p>
<p class="title" id="db-size">{{ db_size }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Number of Documents</p>
<p class="title" id="docs-count">{{ docs_count }}</p>
</div>
</div>
</nav>
</div> </div>
</div> </div>
</section> </section>
@ -29,13 +50,25 @@
<ol id="results" class="content"> <ol id="results" class="content">
{% for update in updates %} {% for update in updates %}
<li class="document"> {% match update %}
<ol> {% when UpdateStatus::Pending with { update_id , meta } %}
<li class="field"> <li id="update-{{ update_id }}" class="document">
<div class="attribute">text</div><div class="content">{{ update }}</div> <ol>
</li> <li class="field">
</ol> <div class="attribute">text</div><div class="content">Pending {{ update_id }}</div>
</li> </li>
</ol>
</li>
{% when UpdateStatus::Processed with { update_id , meta } %}
<li id="update-{{ update_id }}" class="document">
<ol>
<li class="field">
<div class="attribute">text</div><div class="content">Processed {{ update_id }}</div>
</li>
</ol>
</li>
{% else %}
{% endmatch %}
{% endfor %} {% endfor %}
</ol> </ol>