aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/hooks/wolfram_alpha.rs171
-rw-r--r--src/util/web.rs29
-rw-r--r--tests/resources/wolfram_alpha_api_response_of_input_with_spaces.json244
-rw-r--r--tests/resources/wolfram_alpha_api_response_with_no_primaries.json300
6 files changed, 733 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 207cdef..a87d120 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -134,6 +134,7 @@ dependencies = [
"tracing",
"tracing-futures",
"tracing-subscriber",
+ "urlparse",
]
[[package]]
@@ -1506,6 +1507,12 @@ dependencies = [
]
[[package]]
+name = "urlparse"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "110352d4e9076c67839003c7788d8604e24dcded13e0b375af3efaa8cf468517"
+
+[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 525153a..6477100 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,6 +31,7 @@ sedregex = { version = "0.2", git = "https://gitlab.com/audron/sedregex" }
rand = "0.8.3"
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "gzip"] }
+urlparse = "0.7.3"
[dev-dependencies]
mockito = "0.30.0"
diff --git a/src/hooks/wolfram_alpha.rs b/src/hooks/wolfram_alpha.rs
index e440eb8..5584935 100644
--- a/src/hooks/wolfram_alpha.rs
+++ b/src/hooks/wolfram_alpha.rs
@@ -1,4 +1,7 @@
-use crate::util::{formatting::truncate, web::shorten_url};
+use crate::util::{
+ formatting::truncate,
+ web::{quote_plus, shorten_url},
+};
use anyhow::{bail, Context, Error, Result};
use futures::try_join;
use irc::client::prelude::*;
@@ -51,7 +54,7 @@ fn to_single_string(wa_res: WaResponse) -> String {
.join(" - ")
}
-fn get_url(query_str: &str, api_key: Option<&str>, base_url: Option<&str>) -> String {
+fn get_url(query_str: &str, api_key: Option<&str>, base_url: Option<&str>) -> Result<Url, Error> {
let wa_url = "http://api.wolframalpha.com";
let api_url = format!(
"{}/v2/query?input={}&appid={}&output=json",
@@ -59,11 +62,11 @@ fn get_url(query_str: &str, api_key: Option<&str>, base_url: Option<&str>) -> St
query_str,
api_key.unwrap_or("XXX"), // Allow tests to run without a key
);
- api_url
+ Url::parse(&api_url).context("Failed to parse URL")
}
-async fn send_wa_req(url: &str) -> Result<String, Error> {
- let body = get(Url::parse(url).context("Failed to parse url")?)
+async fn send_wa_req(url: &Url) -> Result<String, Error> {
+ let body = get(url.to_owned())
.await
.context("Failed to make request")?
.text()
@@ -72,12 +75,24 @@ async fn send_wa_req(url: &str) -> Result<String, Error> {
Ok(body)
}
-async fn handle_wa_req(url: &str) -> Result<WaResponse, Error> {
+async fn handle_wa_req(url: &Url) -> Result<WaResponse, Error> {
let res_body = send_wa_req(url).await?;
let parsed = serde_json::from_str(&res_body)?;
Ok(parsed)
}
+/// Gets a URL users can click, leading to the main WA page.
+async fn get_wa_user_short_url(input: &str) -> Result<String, Error> {
+ let user_url = format!(
+ "http://www.wolframalpha.com/input/?i={}",
+ // For some reason some inputs need double quote calls, e.g. '1 * 2'.
+ // Maybe only with is.gd though.
+ quote_plus(&quote_plus(input)?)?
+ );
+
+ shorten_url(&user_url).await
+}
+
/// Sends a request to the Wolfram Alpha API, returns a plain text response.
#[tracing::instrument]
async fn wa_query(
@@ -85,34 +100,42 @@ async fn wa_query(
api_key: Option<&str>,
base_url: Option<&str>,
) -> Result<String, Error> {
- let user_url = format!("http://www.wolframalpha.com/input/?i={}", query_str);
- let user_url_shortened_fut = shorten_url(&user_url);
-
- let url = get_url(query_str, api_key, base_url);
+ let user_url_shortened_fut = get_wa_user_short_url(query_str);
+ let url = get_url(query_str, api_key, base_url)?;
let wa_res_fut = handle_wa_req(&url);
// Can't just (foo.await, bar.await), smh
// https://rust-lang.github.io/async-book/06_multiple_futures/02_join.html
let (wa_res, user_url_shortened) = try_join!(wa_res_fut, user_url_shortened_fut)?;
+ let string_result = match to_single_string(wa_res) {
+ x if x.is_empty() => "No primary results.".to_string(),
+ x => x,
+ };
+
Ok(format!(
"{} - {}",
- truncate(&to_single_string(wa_res), 250), // Same length as in gonzobot
+ truncate(&string_result, 250), // Same length as in gonzobot
&user_url_shortened,
))
}
+fn get_input_query(text: &str) -> Result<String, Error> {
+ let input = text.chars().as_str().splitn(2, " ").collect::<Vec<&str>>();
+ if input.len() != 2 {
+ bail!("Empty input for WA query");
+ }
+ let content = input[1].trim();
+ Ok(content.to_string())
+}
+
pub async fn wa(bot: &crate::Bot, msg: Message) -> Result<()> {
privmsg!(msg, {
- let input = text.chars().as_str().splitn(2, " ").collect::<Vec<&str>>();
- if input.len() != 2 {
- bail!("Empty input for WA query");
- }
- let content = input[1].trim();
+ let content = get_input_query(text)?;
bot.send_privmsg(
msg.response_target()
.context("failed to get response target")?,
- &wa_query(content, Some(&bot.config.settings.wa_api_key), None).await?,
+ &wa_query(&content, Some(&bot.config.settings.wa_api_key), None).await?,
)?;
})
}
@@ -120,12 +143,86 @@ pub async fn wa(bot: &crate::Bot, msg: Message) -> Result<()> {
#[cfg(test)]
mod tests {
- use super::wa_query;
+ use super::{get_input_query, get_wa_user_short_url, wa_query};
use anyhow::{Error, Result};
use mockito::{self, Matcher};
+ #[test]
+ fn test_input_query_content_retrieval() -> Result<(), Error> {
+ let incoming = ":wa test";
+ let content = get_input_query(incoming)?;
+ assert_eq!(content, "test");
+ Ok(())
+ }
+
+ #[test]
+ fn test_input_query_content_retrieval_with_spaces() -> Result<(), Error> {
+ let incoming = ":wa foo bar";
+ let content = get_input_query(incoming)?;
+ assert_eq!(content, "foo bar");
+ Ok(())
+ }
+
+ #[test]
+ fn test_input_query_content_retrieval_with_more_spaces() -> Result<(), Error> {
+ let incoming = ":wa foo bar baz";
+ let content = get_input_query(incoming)?;
+ assert_eq!(content, "foo bar baz");
+ Ok(())
+ }
+
+ // These tests must be updated if a service other than is.gd is used
#[tokio::test]
- async fn test_query_result_json_parsing() -> Result<(), Error> {
+ async fn test_wa_user_short_url_1() -> Result<(), Error> {
+ let input = "5/10";
+ assert_eq!(get_wa_user_short_url(input).await?, "https://is.gd/kgsSb7");
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_wa_user_short_url_2() -> Result<(), Error> {
+ let input = "5 / 10";
+ assert_eq!(get_wa_user_short_url(input).await?, "https://is.gd/b4xgwD");
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_wa_user_short_url_3() -> Result<(), Error> {
+ let input = "1*2";
+ assert_eq!(get_wa_user_short_url(input).await?, "https://is.gd/TLGKih");
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_wa_user_short_url_4() -> Result<(), Error> {
+ let input = "1 * 2";
+ assert_eq!(get_wa_user_short_url(input).await?, "https://is.gd/ZXYAUR");
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_wa_user_short_url_5() -> Result<(), Error> {
+ let input = "3+4";
+ assert_eq!(get_wa_user_short_url(input).await?, "https://is.gd/AZRBWy");
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_wa_user_short_url_6() -> Result<(), Error> {
+ let input = "3 + 4";
+ assert_eq!(get_wa_user_short_url(input).await?, "https://is.gd/lBuhgK");
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_wa_user_short_url_7() -> Result<(), Error> {
+ let input = "test";
+ assert_eq!(get_wa_user_short_url(input).await?, "https://is.gd/NzIyUZ");
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_query_result_parsing() -> Result<(), Error> {
let body = include_str!("../../tests/resources/wolfram_alpha_api_response.json");
let _m = mockito::mock("GET", Matcher::Any)
// Trimmed down version of a full WA response:
@@ -141,4 +238,40 @@ mod tests {
);
Ok(())
}
+
+ #[tokio::test]
+ async fn test_query_with_spaces_result_parsing() -> Result<(), Error> {
+ let body = include_str!(
+ "../../tests/resources/wolfram_alpha_api_response_of_input_with_spaces.json"
+ );
+ let _m = mockito::mock("GET", Matcher::Any)
+ // Trimmed down version of a full WA response:
+ .with_body(body)
+ .create();
+ mockito::start();
+
+ let res = wa_query("5 / 10", None, Some(&mockito::server_url())).await?;
+ let res_without_link = res.rsplitn(2, "-").collect::<Vec<&str>>()[1..].join(" ");
+ assert_eq!(
+ res_without_link.trim(),
+ "Exact result: 1/2 - Decimal form: 0.5"
+ );
+ Ok(())
+ }
+
+ #[tokio::test]
+ async fn test_query_with_result_with_primary_pods_parsing() -> Result<(), Error> {
+ let body =
+ include_str!("../../tests/resources/wolfram_alpha_api_response_with_no_primaries.json");
+ let _m = mockito::mock("GET", Matcher::Any)
+ // Trimmed down version of a full WA response:
+ .with_body(body)
+ .create();
+ mockito::start();
+
+ let res = wa_query("what is a url", None, Some(&mockito::server_url())).await?;
+ let res_without_link = res.rsplitn(2, "-").collect::<Vec<&str>>()[1..].join(" ");
+ assert_eq!(res_without_link.trim(), "No primary results.");
+ Ok(())
+ }
}
diff --git a/src/util/web.rs b/src/util/web.rs
index 77e20a3..bfa7003 100644
--- a/src/util/web.rs
+++ b/src/util/web.rs
@@ -1,5 +1,10 @@
use anyhow::{Context, Error, Result};
use reqwest::{get, Url};
+use urlparse::quote_plus as urlparse_quote_plus;
+
+pub(crate) fn quote_plus(text: &str) -> Result<String, Error> {
+ Ok(urlparse_quote_plus(text, b"")?)
+}
// TODO: Either catinator should have a URL shortening utility module,
// or we should start our own service
@@ -18,3 +23,27 @@ pub(crate) async fn shorten_url(url: &str) -> Result<String, Error> {
Ok(short_url)
}
+
+#[cfg(test)]
+mod tests {
+ use super::{quote_plus};
+ use anyhow::{Error, Result};
+
+ #[test]
+ fn test_quote_plus_1() -> Result<(), Error> {
+ assert_eq!(quote_plus("5/10")?, "5%2F10");
+ Ok(())
+ }
+
+ #[test]
+ fn test_quote_plus_2() -> Result<(), Error> {
+ assert_eq!(quote_plus("1 * 2")?, "1+%2A+2");
+ Ok(())
+ }
+
+ #[test]
+ fn test_quote_plus_3() -> Result<(), Error> {
+ assert_eq!(quote_plus(&quote_plus("1 * 2")?)?, "1%2B%252A%2B2");
+ Ok(())
+ }
+}
diff --git a/tests/resources/wolfram_alpha_api_response_of_input_with_spaces.json b/tests/resources/wolfram_alpha_api_response_of_input_with_spaces.json
new file mode 100644
index 0000000..014bb80
--- /dev/null
+++ b/tests/resources/wolfram_alpha_api_response_of_input_with_spaces.json
@@ -0,0 +1,244 @@
+{
+ "queryresult": {
+ "success": true,
+ "error": false,
+ "numpods": 7,
+ "datatypes": "Math",
+ "timedout": "",
+ "timedoutpods": "",
+ "timing": 0.921,
+ "parsetiming": 0.17500000000000002,
+ "parsetimedout": false,
+ "recalculate": "",
+ "id": "MSP485014edhg0e7d9e6fi500002a5e4i117igf338i",
+ "host": "https://www4c.wolframalpha.com",
+ "server": "5",
+ "related": "https://www4c.wolframalpha.com/api/v1/relatedQueries.jsp?id=MSPa485114edhg0e7d9e6fi500005h567id42e4fib623794414399634972455",
+ "version": "2.6",
+ "inputstring": "5 / 10",
+ "pods": [
+ {
+ "title": "Input",
+ "scanner": "Identity",
+ "id": "Input",
+ "position": 100,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www4c.wolframalpha.com/Calculate/MSP/MSP485214edhg0e7d9e6fi500003b0hcbg1cbai5gi4?MSPStoreType=image/gif&s=5",
+ "alt": "5/10",
+ "title": "5/10",
+ "width": 21,
+ "height": 37,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "5/10"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Exact result",
+ "scanner": "Rational",
+ "id": "Result",
+ "position": 200,
+ "error": false,
+ "numsubpods": 1,
+ "primary": true,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www4c.wolframalpha.com/Calculate/MSP/MSP485314edhg0e7d9e6fi5000041ee77342b27hf65?MSPStoreType=image/gif&s=5",
+ "alt": "1/2",
+ "title": "1/2",
+ "width": 13,
+ "height": 37,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "1/2"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ },
+ "states": [
+ {
+ "name": "Step-by-step solution",
+ "input": "Result__Step-by-step solution",
+ "stepbystep": true
+ }
+ ]
+ },
+ {
+ "title": "Decimal form",
+ "scanner": "Numeric",
+ "id": "DecimalApproximation",
+ "position": 300,
+ "error": false,
+ "numsubpods": 1,
+ "primary": true,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www4c.wolframalpha.com/Calculate/MSP/MSP485414edhg0e7d9e6fi50000379ibc1hdfcc9c3f?MSPStoreType=image/gif&s=5",
+ "alt": "0.5",
+ "title": "0.5",
+ "width": 30,
+ "height": 21,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "0.5"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Number line",
+ "scanner": "NumberLine",
+ "id": "NumberLine",
+ "position": 400,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www4c.wolframalpha.com/Calculate/MSP/MSP485514edhg0e7d9e6fi500001efh7fidi29hi9d0?MSPStoreType=image/gif&s=5",
+ "alt": "Number line",
+ "title": "",
+ "width": 330,
+ "height": 59,
+ "type": "1DMathPlot_1",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": ""
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Percentage",
+ "scanner": "Numeric",
+ "id": "Percentage",
+ "position": 500,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www4c.wolframalpha.com/Calculate/MSP/MSP485614edhg0e7d9e6fi500002de8fch4636c7c7c?MSPStoreType=image/gif&s=5",
+ "alt": "50%",
+ "title": "50%",
+ "width": 28,
+ "height": 19,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "50%"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Pie chart",
+ "scanner": "Rational",
+ "id": "PieChart",
+ "position": 600,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www4c.wolframalpha.com/Calculate/MSP/MSP485714edhg0e7d9e6fi500003d336g6gccgffg6i?MSPStoreType=image/gif&s=5",
+ "alt": "Pie chart",
+ "title": "",
+ "width": 60,
+ "height": 62,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": ""
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Continued fraction",
+ "scanner": "ContinuedFraction",
+ "id": "ContinuedFraction",
+ "position": 700,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www4c.wolframalpha.com/Calculate/MSP/MSP485814edhg0e7d9e6fi500001d7g1ciibagii80b?MSPStoreType=image/gif&s=5",
+ "alt": "[0; 2]",
+ "title": "[0; 2]",
+ "width": 548,
+ "height": 19,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "[0; 2]"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ },
+ "states": [
+ {
+ "name": "Fraction form",
+ "input": "ContinuedFraction__Fraction form"
+ }
+ ]
+ }
+ ],
+ "assumptions": {
+ "type": "Clash",
+ "word": "5 / 10",
+ "template": "Assuming \"${word}\" is ${desc1}. Use as ${desc2} instead",
+ "count": 2,
+ "values": [
+ {
+ "name": "FracNumber",
+ "desc": " referring to math",
+ "input": "*C.5+%2F+10-_*FracNumber-"
+ },
+ {
+ "name": "DateObject",
+ "desc": "a date",
+ "input": "*C.5+%2F+10-_*DateObject-"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/resources/wolfram_alpha_api_response_with_no_primaries.json b/tests/resources/wolfram_alpha_api_response_with_no_primaries.json
new file mode 100644
index 0000000..6b3b643
--- /dev/null
+++ b/tests/resources/wolfram_alpha_api_response_with_no_primaries.json
@@ -0,0 +1,300 @@
+{
+ "queryresult": {
+ "success": true,
+ "error": false,
+ "numpods": 8,
+ "datatypes": "",
+ "timedout": "",
+ "timedoutpods": "",
+ "timing": 3.658,
+ "parsetiming": 0.427,
+ "parsetimedout": false,
+ "recalculate": "",
+ "id": "MSP444231b2g3hi9dh4e7200002c8f4f93hd7e1534",
+ "host": "https://www3.wolframalpha.com",
+ "server": "10",
+ "related": "https://www3.wolframalpha.com/api/v1/relatedQueries.jsp?id=MSPa445231b2g3hi9dh4e720000400770heb2hg9i7g9176140364258442551",
+ "version": "2.6",
+ "inputstring": "what is a url",
+ "pods": [
+ {
+ "title": "Input interpretation",
+ "scanner": "Identity",
+ "id": "Input",
+ "position": 100,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP446231b2g3hi9dh4e72000046954560094a96de?MSPStoreType=image/gif&s=10",
+ "alt": "URL (Wolfram Language symbol)",
+ "title": "URL (Wolfram Language symbol)",
+ "width": 223,
+ "height": 19,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "URL (Wolfram Language symbol)"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Usage",
+ "scanner": "Data",
+ "id": "UsagePod:WolframLanguageData",
+ "position": 200,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP447231b2g3hi9dh4e7200001265a4a7ah5i8467?MSPStoreType=image/gif&s=10",
+ "alt": "URL[\"url\"] is a symbolic representation of a URL.",
+ "title": "URL[\"url\"] is a symbolic representation of a URL.",
+ "width": 321,
+ "height": 19,
+ "type": "Grid",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "URL[\"url\"] is a symbolic representation of a URL."
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ },
+ "infos": {
+ "links": {
+ "url": "http://reference.wolfram.com/language/ref/URL.html",
+ "text": "More information"
+ }
+ }
+ },
+ {
+ "title": "Basic examples",
+ "scanner": "Data",
+ "id": "ExamplesPod:WolframLanguageData",
+ "position": 300,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP448231b2g3hi9dh4e720000652fe8bedb0ega50?MSPStoreType=image/gif&s=10",
+ "alt": "Basic examples",
+ "title": "",
+ "width": 413,
+ "height": 247,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": ""
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Attributes",
+ "scanner": "Data",
+ "id": "AttributesPod:WolframLanguageData",
+ "position": 400,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP449231b2g3hi9dh4e7200002ccd5b8f44i414cd?MSPStoreType=image/gif&s=10",
+ "alt": "Protected | ReadProtected",
+ "title": "Protected | ReadProtected",
+ "width": 198,
+ "height": 19,
+ "type": "Grid",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "Protected | ReadProtected"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ }
+ },
+ {
+ "title": "Relationships with other entities",
+ "scanner": "Data",
+ "id": "RelatedEntitiesPod:WolframLanguageData",
+ "position": 500,
+ "error": false,
+ "numsubpods": 2,
+ "subpods": [
+ {
+ "title": "Related symbols",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP450231b2g3hi9dh4e7200005556cc653e32ddfg?MSPStoreType=image/gif&s=10",
+ "alt": "URLRead | URLExecute | URLParse | URLBuild | Hyperlink | CloudObject | File | CopyFile | RemoteFile",
+ "title": "URLRead | URLExecute | URLParse | URLBuild | Hyperlink | CloudObject | File | CopyFile | RemoteFile",
+ "width": 475,
+ "height": 40,
+ "type": "Grid",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "URLRead | URLExecute | URLParse | URLBuild | Hyperlink | CloudObject | File | CopyFile | RemoteFile"
+ },
+ {
+ "title": "",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP451231b2g3hi9dh4e72000031f9ac66460f5g41?MSPStoreType=image/gif&s=10",
+ "alt": "Relationships with other entities",
+ "title": "",
+ "width": 440,
+ "height": 376,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": ""
+ }
+ ],
+ "expressiontypes": [
+ {
+ "name": "Default"
+ },
+ {
+ "name": "Default"
+ }
+ ],
+ "states": [
+ {
+ "name": "Hide relationship communities",
+ "input": "RelatedEntitiesPod:WolframLanguageData__Hide relationship communities"
+ }
+ ]
+ },
+ {
+ "title": "Typical ranks of usage in programs",
+ "scanner": "Data",
+ "id": "RanksPod:WolframLanguageData",
+ "position": 600,
+ "error": false,
+ "numsubpods": 2,
+ "subpods": [
+ {
+ "title": "Frequency of usage in all corpuses",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP452231b2g3hi9dh4e720000643g1a5fc13b7ec5?MSPStoreType=image/gif&s=10",
+ "alt": "1727th most common (1 in 362000 symbols)",
+ "title": "1727th most common (1 in 362000 symbols)",
+ "width": 271,
+ "height": 22,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "1727th most common (1 in 362000 symbols)"
+ },
+ {
+ "title": "Rank of usage in the Wolfram documentation",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP453231b2g3hi9dh4e7200000g58105h71d404ig?MSPStoreType=image/gif&s=10",
+ "alt": "898th most common (1 in 16500 symbols)",
+ "title": "898th most common (1 in 16500 symbols)",
+ "width": 256,
+ "height": 22,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "898th most common (1 in 16500 symbols)"
+ }
+ ],
+ "expressiontypes": [
+ {
+ "name": "Default"
+ },
+ {
+ "name": "Default"
+ }
+ ],
+ "states": [
+ {
+ "name": "Show more ranks",
+ "input": "RanksPod:WolframLanguageData__Show more ranks"
+ }
+ ]
+ },
+ {
+ "title": "History",
+ "scanner": "Data",
+ "id": "HistoryPod:WolframLanguageData",
+ "position": 700,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "Version introduced or modified",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP454231b2g3hi9dh4e72000020hb6gd0ga7g319c?MSPStoreType=image/gif&s=10",
+ "alt": "introduced in Version 11 (August 2016)\nlast modified in Version 12 (April 2019)",
+ "title": "introduced in Version 11 (August 2016)\nlast modified in Version 12 (April 2019)",
+ "width": 260,
+ "height": 46,
+ "type": "Grid",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": "introduced in Version 11 (August 2016)\nlast modified in Version 12 (April 2019)"
+ }
+ ],
+ "expressiontypes": {
+ "name": "Grid"
+ }
+ },
+ {
+ "title": "Timeline",
+ "scanner": "Data",
+ "id": "TimelinePod:WolframLanguageData",
+ "position": 800,
+ "error": false,
+ "numsubpods": 1,
+ "subpods": [
+ {
+ "title": "",
+ "img": {
+ "src": "https://www3.wolframalpha.com/Calculate/MSP/MSP455231b2g3hi9dh4e7200002i8ha7aei9ce325g?MSPStoreType=image/gif&s=10",
+ "alt": "Timeline",
+ "title": "",
+ "width": 550,
+ "height": 38,
+ "type": "Default",
+ "themes": "1,2,3,4,5,6,7,8,9,10,11,12",
+ "colorinvertable": true
+ },
+ "plaintext": ""
+ }
+ ],
+ "expressiontypes": {
+ "name": "Default"
+ },
+ "states": [
+ {
+ "name": "Show release numbers",
+ "input": "TimelinePod:WolframLanguageData__Show release numbers"
+ }
+ ]
+ }
+ ]
+ }
+}