major improvements to plenum pad logic
- a lot of new comments for better doc - removed unnecessary functions - main plenum pad logic still in progress - one additional comment in main
This commit is contained in:
parent
aca180d6ba
commit
bbd9b2c29e
|
@ -94,6 +94,8 @@ fn today() -> NaiveDate {
|
||||||
}
|
}
|
||||||
/// Gets either the state from the config or overrides it with the state from
|
/// Gets either the state from the config or overrides it with the state from
|
||||||
/// the environment variable `STATE_OVERRIDE` (for testing purposes.)
|
/// the environment variable `STATE_OVERRIDE` (for testing purposes.)
|
||||||
|
///
|
||||||
|
/// For example, `STATE_OVERRIDE=Waiting` can be used to debug mediawiki.
|
||||||
fn state(config: &KeyValueStore) -> ProgramState {
|
fn state(config: &KeyValueStore) -> ProgramState {
|
||||||
match env::var("STATE_OVERRIDE") {
|
match env::var("STATE_OVERRIDE") {
|
||||||
Ok(val) => ProgramState::parse(&val),
|
Ok(val) => ProgramState::parse(&val),
|
||||||
|
|
148
src/mediawiki.rs
148
src/mediawiki.rs
|
@ -1,10 +1,10 @@
|
||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
use chrono::{Datelike, Utc};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::{trace_var, verboseln};
|
use crate::{trace_var, verboseln};
|
||||||
use crate::config_spec::{CfgField, CfgGroup};
|
use crate::config_spec::{CfgField, CfgGroup};
|
||||||
|
@ -149,11 +149,13 @@ impl Mediawiki {
|
||||||
.send()
|
.send()
|
||||||
}
|
}
|
||||||
ValidRequestTypes::Post | ValidRequestTypes::PostForEditing => {
|
ValidRequestTypes::Post | ValidRequestTypes::PostForEditing => {
|
||||||
|
// convert the params into a HashMap for JSON
|
||||||
|
let params_map: std::collections::HashMap<_, _> = params.iter().cloned().collect();
|
||||||
self
|
self
|
||||||
.client
|
.client
|
||||||
.post(url)
|
.post(url)
|
||||||
//.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT
|
//.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT
|
||||||
.form(¶ms)
|
.json(¶ms_map)
|
||||||
.send()
|
.send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,8 +181,9 @@ impl Mediawiki {
|
||||||
};
|
};
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a completely new wiki page with page_content and page_title as inputs
|
/// Creates a completely new wiki page with page_content and page_title as inputs
|
||||||
pub fn new_wiki_page (&self, page_title: &str, page_content: &str) -> Result<String, Box<dyn Error>> {
|
pub fn new_wiki_page (&self, page_title: &str, page_content: &str, update_main_page: bool) -> Result<String, Box<dyn Error>> {
|
||||||
// action=edit&format=json&title=Wikipedia:Sandbox&appendtext=Hello&token=sampleCsrfToken123+\
|
// action=edit&format=json&title=Wikipedia:Sandbox&appendtext=Hello&token=sampleCsrfToken123+\
|
||||||
let url =
|
let url =
|
||||||
format!("{}/api.php?", self.server_url);
|
format!("{}/api.php?", self.server_url);
|
||||||
|
@ -192,28 +195,72 @@ impl Mediawiki {
|
||||||
("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens
|
("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens
|
||||||
("bot", "true"), // Mark this edit as a bot edit.
|
("bot", "true"), // Mark this edit as a bot edit.
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
println!("Making request to {} with params: {:?}", url, params);
|
||||||
|
|
||||||
let request_result = self.make_request(url, params, ValidRequestTypes::Post);
|
let request_result = self.make_request(url, params, ValidRequestTypes::Post);
|
||||||
|
|
||||||
self.update_plenum_page(page_title)?;
|
if update_main_page {
|
||||||
|
self.update_plenum_page(page_title)?;
|
||||||
|
}
|
||||||
request_result
|
request_result
|
||||||
}
|
}
|
||||||
/// Downloads the main Plenum Page from Mediawiki, inserts the Link to the new Page and replaces the content of the mediawiki-
|
/// This function is responsible for updating the main plenum page:
|
||||||
|
///
|
||||||
|
/// It downloads the main plenum page from Mediawiki, inserts the
|
||||||
|
/// new Link to the newly uploaded plenum pad and uploads the
|
||||||
|
/// page back to mediawiki.
|
||||||
pub fn update_plenum_page (&self, new_page_title_to_link_to: &str) -> Result<(), Box<dyn Error>> {
|
pub fn update_plenum_page (&self, new_page_title_to_link_to: &str) -> Result<(), Box<dyn Error>> {
|
||||||
// 1. Download Plenum page content
|
// 1. Get the current year:
|
||||||
|
let current_year = Utc::now().year().to_string();
|
||||||
|
let year_pattern = format!("=== {} ===", current_year);
|
||||||
|
|
||||||
|
// 2. Download Plenum page content
|
||||||
let page_content = self.get_page_content(&self.plenum_main_page_name)?;
|
let page_content = self.get_page_content(&self.plenum_main_page_name)?;
|
||||||
trace_var!(page_content);
|
//trace_var!(page_content);
|
||||||
let current_year = "2024"; // TODO: Datumslogik einbauen
|
|
||||||
let year_section = format!("=== {} ===\n", current_year);
|
// 3. Check if the current year exists
|
||||||
if page_content.contains(&year_section) {
|
let mut updated_section = String::new();
|
||||||
let mut content_split: Vec<&str> = page_content.split(&year_section).collect();
|
if page_content.contains(&year_pattern) {
|
||||||
println!("Length: {}", content_split.len());
|
// 4. Year Heading exists, inserting new link below
|
||||||
let rest_of_content = content_split.pop().unwrap_or_default();
|
let mut content_split: Vec<&str> = page_content.split(&year_pattern).collect();
|
||||||
let updated_section = format!("{}{}\n* {}", content_split.join(&year_section), year_section, new_page_title_to_link_to);
|
let site_content_below_year_heading = content_split.pop().unwrap_or_default();
|
||||||
//format!("{}{}", updated_section, rest_of_content)
|
let site_content_above_year_heading = content_split.pop().unwrap_or_default();
|
||||||
|
updated_section = format!(
|
||||||
|
"{}{}\n* [[{}]]{}",
|
||||||
|
site_content_above_year_heading,
|
||||||
|
year_pattern,
|
||||||
|
new_page_title_to_link_to,
|
||||||
|
site_content_below_year_heading);
|
||||||
|
verboseln!("updated page: {}", updated_section)
|
||||||
|
} else {
|
||||||
|
// Scenario: New year: Generating new heading for the current year
|
||||||
|
// 4. First checking if the last year exists:
|
||||||
|
let last_year = current_year.parse::<i32>()? - 1; // returns e.g. 2023 when given 2024
|
||||||
|
let last_year_pattern = format!("=== {} ===", last_year);
|
||||||
|
if page_content.contains(&last_year_pattern) {
|
||||||
|
// 2. Secondly, generate a new heading for this year above last year's
|
||||||
|
verboseln!("new year, adding this year's heading");
|
||||||
|
let mut content_split: Vec<&str> = page_content.split(&last_year_pattern).collect();
|
||||||
|
let site_content_below_year_heading = content_split.pop().unwrap_or_default();
|
||||||
|
let site_content_above_year_heading = content_split.pop().unwrap_or_default();
|
||||||
|
updated_section = format!(
|
||||||
|
"{}{}\n* [[{}]]\n\n{}{}",
|
||||||
|
site_content_above_year_heading,
|
||||||
|
year_pattern,
|
||||||
|
new_page_title_to_link_to,
|
||||||
|
last_year_pattern,
|
||||||
|
site_content_below_year_heading);
|
||||||
|
} else {
|
||||||
|
println!("There is neither a {}, nor a {} heading on the main plenum Page!", current_year, last_year);
|
||||||
|
panic!("The Plenum Main Page seems seriously damaged, aborting...")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// uploading the pad again
|
||||||
|
self.new_wiki_page(&self.plenum_main_page_name, &updated_section, false)?; // nicht auf true setzen, sonst entsteht eine Endlosschleife
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
/// This function downloads and returns the contents of a wiki page when given the page's title (e.g. `page_title = Plenum/13._August_2024`)
|
||||||
pub fn get_page_content (&self, page_title: &str) -> Result<String, Box<dyn Error>> {
|
pub fn get_page_content (&self, page_title: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let url =
|
let url =
|
||||||
format!("{}/api.php?", self.server_url);
|
format!("{}/api.php?", self.server_url);
|
||||||
|
@ -225,41 +272,20 @@ impl Mediawiki {
|
||||||
("formatversion", "2"),
|
("formatversion", "2"),
|
||||||
]);
|
]);
|
||||||
let resp = self.make_request(url, params, ValidRequestTypes::Get)?;
|
let resp = self.make_request(url, params, ValidRequestTypes::Get)?;
|
||||||
let resp = json!(resp);
|
let response_deserialized: serde_json::Value = serde_json::from_str(&resp)?;
|
||||||
Ok(resp["parse"]["wikitext"].to_string())
|
|
||||||
}
|
|
||||||
pub fn get_page_section_title (&self, page_title: &str, section_number: &str) -> Result<String, Box<dyn Error>> {
|
|
||||||
let url =
|
|
||||||
format!("{}/api.php?", self.server_url);
|
|
||||||
let params: Box<[(&str, &str)]> = Box::from([
|
|
||||||
("action", "parse"), // Create and edit pages.
|
|
||||||
("contentmodel", "wikitext"),
|
|
||||||
("format", "json"),
|
|
||||||
("page", page_title),
|
|
||||||
]);
|
|
||||||
let resp = self.make_request(url, params, ValidRequestTypes::Get)?;
|
|
||||||
todo!()
|
|
||||||
//let response_deserialized = serde_json::from_str(&resp)?;
|
|
||||||
//Ok(response_deserialized["parse"])
|
|
||||||
}
|
|
||||||
pub fn edit_section (&self, page_title: &str, text_to_prepend: &str, section_number: &str) -> Result<String, Box<dyn Error>> {
|
|
||||||
let url =
|
|
||||||
format!("{}/api.php?", self.server_url);
|
|
||||||
let params: Box<[(&str, &str)]> = Box::from([
|
|
||||||
("action", "edit"), // Create and edit pages.
|
|
||||||
("format", "json"),
|
|
||||||
("title", page_title), // Title of the page to edit. Cannot be used together with pageid.
|
|
||||||
("section", section_number), // Section identifier. 0 for the top section, new for a new section. Often a positive integer, but can also be non-numeric
|
|
||||||
("prependtext", text_to_prepend), // Add this text to the end of the page or section. Overrides text.
|
|
||||||
("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens
|
|
||||||
("bot", "true"), // Mark this edit as a bot edit.
|
|
||||||
]);
|
|
||||||
let request_result = self.make_request(url, params, ValidRequestTypes::Post);
|
|
||||||
|
|
||||||
request_result
|
let resp = response_deserialized
|
||||||
|
.get("parse")
|
||||||
|
.and_then(|parse | parse.get("wikitext"))
|
||||||
|
.ok_or("Expected field `wikitext` not found, likely no access to main plenum page")?;
|
||||||
|
Ok(resp.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This function runs at the end of do_protocol() and is responsible for
|
||||||
|
/// logging in to mediawiki, retrieving the necessary tokens, creating a
|
||||||
|
/// new wiki page for the current plenum protocol, and for linking the new
|
||||||
|
/// page to the main plenum overview page.
|
||||||
pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box<dyn Error>> {
|
pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box<dyn Error>> {
|
||||||
// Login to Wiki and get required tokens for logging in and writing
|
// Login to Wiki and get required tokens for logging in and writing
|
||||||
wiki.get_login_token()?;
|
wiki.get_login_token()?;
|
||||||
|
@ -273,14 +299,25 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box
|
||||||
// Convert to mediawiki and make new page
|
// Convert to mediawiki and make new page
|
||||||
let pad_converted = pandoc_convert(old_pad_content)?;
|
let pad_converted = pandoc_convert(old_pad_content)?;
|
||||||
trace_var!(pad_converted);
|
trace_var!(pad_converted);
|
||||||
let page_title = "Page Test 5";
|
|
||||||
let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); // Example: Plenum/13._August_2024
|
// Create a new wiki page plenum_main_page_name/page_title, e.g. under Plenum/13._August_2024
|
||||||
wiki.new_wiki_page(&page_title, &pad_converted)?;
|
// and past the converted pad there
|
||||||
|
verboseln!("wiki: uploading converted pad");
|
||||||
|
// let page_title = "14._August_2024";
|
||||||
|
let page_title = "14._AUGUST_2024";
|
||||||
|
let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title);
|
||||||
|
wiki.new_wiki_page(&page_title, &pad_converted, true)?;
|
||||||
|
|
||||||
|
// Download the main plenum page and insert the new page_title as a link
|
||||||
|
verboseln!("wiki: updating main plenum page");
|
||||||
|
|
||||||
|
wiki.update_plenum_page(&page_title)?;
|
||||||
|
verboseln!("Finished successfully with wiki");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a String in the Markdown format and returns a String in the mediawiki Format
|
/// Takes a String in the Markdown format, converts it and returns it in
|
||||||
|
/// the Mediawiki format
|
||||||
fn pandoc_convert(markdown: String) -> Result<String, Box<dyn Error>> {
|
fn pandoc_convert(markdown: String) -> Result<String, Box<dyn Error>> {
|
||||||
let (output, errors, status) = crate::pipe(
|
let (output, errors, status) = crate::pipe(
|
||||||
"pandoc",
|
"pandoc",
|
||||||
|
@ -288,6 +325,7 @@ fn pandoc_convert(markdown: String) -> Result<String, Box<dyn Error>> {
|
||||||
markdown,
|
markdown,
|
||||||
)?;
|
)?;
|
||||||
if status.success() {
|
if status.success() {
|
||||||
|
println!("Resultat von Pandoc: {}", output);
|
||||||
Ok(output)
|
Ok(output)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Pandoc error, exit code {:?}\n{}", status, errors).into())
|
Err(format!("Pandoc error, exit code {:?}\n{}", status, errors).into())
|
||||||
|
@ -302,8 +340,10 @@ fn create_title (nächster_plenumstermin: String) {
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// This has to be defined that way, because both in the login and csrf token,
|
/// Deserialization must be done this way because the response contains
|
||||||
// the response contains two \\ characters which break the usual deserialization
|
/// two `\\` characters in both the login and csrf tokens, which breaks the
|
||||||
|
/// usual deserialization
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct QueryResponseLogin {
|
struct QueryResponseLogin {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
Loading…
Reference in a new issue