index-bank.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <html>
  2. <head>
  3. <script>
  4. // Bed
  5. function importRssFeed (feedUrl) {
  6. var rssRequest = new XMLHttpRequest();
  7. rssRequest.onreadystatechange = function (e) {
  8. var r = rssRequest;
  9. if (r.readyState == XMLHttpRequest.DONE) {
  10. if (r.status >= 200 && r.status < 300) {
  11. console.log("Got RSS (" + r.status + "): ");
  12. var items = importRSSDoc(r.responseXML);
  13. populateItems( items );
  14. createPlot( items );
  15. importTvFeed( "https://harlanji.com/tv.xml" );
  16. }
  17. }
  18. };
  19. //rssRequest.open("GET", "bed-harlanji.xml");
  20. rssRequest.open("GET", feedUrl);
  21. rssRequest.send();
  22. }
  23. function importRSSDoc( rssDoc ) {
  24. console.log("importRSSDoc:");
  25. console.log( rssDoc );
  26. var feedLink = rssDoc.querySelector("link").textContent.trim();
  27. var items = Array.from( rssDoc.querySelectorAll("item") );
  28. items = items.map(function (i) {
  29. var item = {
  30. title: i.querySelector("title").textContent.trim(),
  31. date: new Date( i.querySelector("pubDate").textContent.trim() ),
  32. description: i.querySelector("description").textContent.trim(),
  33. link: i.querySelector("link").textContent.trim(),
  34. feedLink: feedLink
  35. };
  36. var occurenceParts = splitString( item.description, ":", 2);
  37. item.occurence = parseInt( occurenceParts[0] ) || 1;
  38. item.occurenceNote = occurenceParts[1].trim();
  39. if (item.occurenceNote == "") {
  40. item.occurenceNote = "Morning routine";
  41. }
  42. return item;
  43. });
  44. items = items.sort(function (a, b) {
  45. return b.date - a.date;
  46. });
  47. console.log("items:");
  48. console.log(items);
  49. return items;
  50. }
  51. function populateItems( items ) {
  52. console.log("populateItems");
  53. console.log( items );
  54. var rows = items.map(function (item) {
  55. var row = document.createElement( "tr" );
  56. var date = document.createElement( "td" );
  57. var occurence = document.createElement( "td" );
  58. var note = document.createElement( "td" );
  59. date.textContent = dateString( item.date );
  60. occurence.textContent = item.occurence;
  61. note.textContent = item.occurenceNote;
  62. row.appendChild( date );
  63. row.appendChild( occurence );
  64. row.appendChild( note );
  65. return row;
  66. });
  67. var tableBody = document.querySelector("#bed-makings tbody");
  68. console.log("populate: ");
  69. console.log(tableBody);
  70. console.log(rows);
  71. while (tableBody.lastChild) {
  72. tableBody.removeChild(tableBody.lastChild);
  73. }
  74. rows.forEach(function (row) {
  75. tableBody.appendChild(row);
  76. });
  77. }
  78. var dataset;
  79. function initDataset () {
  80. dataset = new vis.DataSet();
  81. }
  82. var groups = [];
  83. function groupFor (str) {
  84. var idx = groups.indexOf(str);
  85. if (idx == -1) {
  86. idx = groups.length;
  87. groups.push(str);
  88. }
  89. return idx;
  90. }
  91. function createPlot ( feedItems ) {
  92. console.log("createPlot");
  93. var container = document.getElementById('visualization');
  94. while( container.lastChild ) {
  95. container.removeChild( container.lastChild );
  96. }
  97. if (feedItems.length == 0) {
  98. return;
  99. }
  100. var items = feedItems.map(function (item) {
  101. var startDate = startOfDay(item.date);
  102. return {
  103. x: startDate,
  104. y: (item.date - startDate) / (1000 * 60),
  105. group: groupFor(item.feedLink),
  106. id: item.feedLink + item.link
  107. };
  108. });
  109. // feed items are in descending order
  110. var firstDate = startOfDay( feedItems[ feedItems.length - 1 ].date );
  111. var lastDate = startOfDay( feedItems[ 0 ].date );
  112. var currentFirstDate = dataset.get("firstDateSunrise");
  113. if (!currentFirstDate || currentFirstDate.x > firstDate) {
  114. var firstTimes = SunCalc.getTimes(firstDate, 44.986656, -93.258133)
  115. items.push({id: "firstDateSunrise",
  116. x: firstDate,
  117. y: (firstTimes.sunrise - firstDate) / (1000 * 60),
  118. group: groupFor("sunrise")});
  119. }
  120. var currentLastDate = dataset.get("lastDateSunrise");
  121. if (!currentLastDate || currentLastDate.x < lastDate) {
  122. var lastTimes = SunCalc.getTimes(lastDate, 44.986656, -93.258133)
  123. items.push({id: "lastDateSunrise",
  124. x: lastDate,
  125. y: (lastTimes.sunrise - lastDate) / (1000 * 60),
  126. group: groupFor("sunrise")});
  127. }
  128. dataset.update(items);
  129. var options = {
  130. sort: false,
  131. sampling:false,
  132. style:'points',
  133. dataAxis: {
  134. left: {
  135. range: {
  136. min: 0, max: (60 * 24)
  137. }
  138. }
  139. },
  140. drawPoints: {
  141. enabled: true,
  142. size: 6,
  143. style: 'circle' // square, circle
  144. },
  145. defaultGroup: 'Scatterplot',
  146. height: '400px',
  147. width: '90%'
  148. };
  149. var graph2d = new vis.Graph2d(container, dataset, options);
  150. // sunrise is the earliest and latest feed items
  151. var windowStartDate = new Date(dataset.get("firstDateSunrise").x);
  152. var windowEndDate = new Date(dataset.get("lastDateSunrise").x);
  153. // day before first and after the latest day
  154. windowStartDate.setDate( windowStartDate.getDate() - 1 );
  155. windowEndDate.setDate( windowEndDate.getDate() + 1 );
  156. graph2d.setWindow(windowStartDate, windowEndDate, {animation: false});
  157. }
  158. // TV
  159. function importTvFeed (feedUrl) {
  160. var rssRequest = new XMLHttpRequest();
  161. rssRequest.onreadystatechange = function (e) {
  162. var r = rssRequest;
  163. if (r.readyState == XMLHttpRequest.DONE) {
  164. if (r.status >= 200 && r.status < 300) {
  165. console.log("Got TV RSS (" + r.status + "): ");
  166. var items = importTvDoc(r.responseXML);
  167. addTvToPlot( items );
  168. }
  169. }
  170. };
  171. //rssRequest.open("GET", "bed-harlanji.xml");
  172. rssRequest.open("GET", feedUrl);
  173. rssRequest.send();
  174. }
  175. function importTvDoc( rssDoc ) {
  176. console.log("importTvDoc:");
  177. console.log( rssDoc );
  178. var items = Array.from( rssDoc.querySelectorAll("item") );
  179. items = items.map(function (i) {
  180. var title = i.querySelector("title").textContent.trim();
  181. var state = title.indexOf("on") > -1;
  182. var item = {
  183. title: title,
  184. state: state,
  185. date: new Date( i.querySelector("pubDate").textContent.trim() ),
  186. description: i.querySelector("description").textContent.trim()
  187. };
  188. return item;
  189. });
  190. items = items.sort(function (a, b) {
  191. return b.date - a.date;
  192. });
  193. console.log("tv items:");
  194. console.log(items);
  195. return items;
  196. }
  197. // Steps CSV
  198. function importStepsFeed (feedUrl) {
  199. var rssRequest = new XMLHttpRequest();
  200. rssRequest.onreadystatechange = function (e) {
  201. var r = rssRequest;
  202. if (r.readyState == XMLHttpRequest.DONE) {
  203. if (r.status >= 200 && r.status < 300) {
  204. var parseHeader = true;
  205. console.log("Got Steps CSV (" + r.status + ", parseHeader=" + parseHeader + "): ");
  206. var items = r.responseText.split("\n")
  207. .map(function (line) { return line.trim(); })
  208. .filter(function (line, i) { return !(parseHeader && i == 0) && line != ""; })
  209. .map(function (line, i, arr) {
  210. var parts = line.split(",");
  211. var date = new Date(parts[0]),
  212. steps = parseInt(parts[1]);
  213. return {
  214. date: date,
  215. steps: steps
  216. }
  217. });
  218. addStepsToPlot( items );
  219. }
  220. }
  221. };
  222. //rssRequest.open("GET", "bed-harlanji.xml");
  223. rssRequest.open("GET", feedUrl);
  224. rssRequest.send();
  225. }
  226. // Bank CSV
  227. function importBnkFeed (feedUrl) {
  228. var rssRequest = new XMLHttpRequest();
  229. rssRequest.onreadystatechange = function (e) {
  230. var r = rssRequest;
  231. if (r.readyState == XMLHttpRequest.DONE) {
  232. if (r.status >= 200 && r.status < 300) {
  233. var parseHeader = true;
  234. console.log("Got Bank CSV (" + r.status + ", parseHeader=" + parseHeader + "): ");
  235. var items = r.responseText.split("\n")
  236. .map(function (line) { return line.trim(); })
  237. .filter(function (line, i) { return !(parseHeader && i == 0) && line != ""; })
  238. .map(function (line, i, arr) {
  239. var parts = line.split(",");
  240. var date = new Date(parts[0]),
  241. amt = parseInt(parts[1]),
  242. from = parts[2];
  243. return {
  244. date: date,
  245. amt: amt,
  246. from: from
  247. }
  248. })
  249. .filter(function (item) { return item.from == 'mi'; });
  250. addBankToPlot( items );
  251. }
  252. }
  253. };
  254. //rssRequest.open("GET", "bed-harlanji.xml");
  255. rssRequest.open("GET", feedUrl);
  256. rssRequest.send();
  257. }
  258. function dateString (date) {
  259. // return "d: " + date;
  260. var str = (date.getYear() + 1900) + "-"
  261. + new String(date.getMonth() + 1).padStart(2, "0") + "-"
  262. + new String(date.getDate()).padStart(2, "0") + " "
  263. + new String(date.getHours()).padStart(2, "0") + ":"
  264. + new String(date.getMinutes()).padStart(2, "0") + ":"
  265. + new String(date.getSeconds()).padStart(2, "0") + " ";
  266. var tzOffset = date.getTimezoneOffset() / 60;
  267. if (tzOffset >= 0) {
  268. str += "+";
  269. }
  270. str += new String(tzOffset * 100).padStart(4, "0");
  271. return str;
  272. }
  273. function addStepsToPlot ( feedItems ) {
  274. console.log("addStepsToPlot");
  275. var items = feedItems.map(function (item) {
  276. var startDate = startOfDay(item.date);
  277. var group = 99;
  278. return {
  279. x: startDate,
  280. y: item.steps / 8, // fixme: normalize over 1440 seconds
  281. group: group,
  282. id: "steps-" + item.date
  283. };
  284. });
  285. dataset.update( items );
  286. }
  287. function addBankToPlot ( feedItems ) {
  288. console.log("addBankToPlot");
  289. var items = feedItems.map(function (item) {
  290. var startDate = startOfDay(item.date);
  291. var group = 199;
  292. return {
  293. x: startDate,
  294. y: item.amt <= 1440 ? item.amt : 1440, // fixme: normalize over 1440 seconds
  295. group: group,
  296. id: "bank-" + item.date
  297. };
  298. });
  299. dataset.update( items );
  300. }
  301. function addTvToPlot ( feedItems ) {
  302. console.log("addTvToPlot");
  303. var items = feedItems.map(function (item) {
  304. var startDate = startOfDay(item.date);
  305. var group = item.state ? 3 : 4;
  306. return {
  307. x: startDate,
  308. y: (item.date - startDate) / (1000 * 60),
  309. group: group
  310. };
  311. });
  312. dataset.add( items );
  313. }
  314. function splitString(string, delimiter, n) {
  315. var parts = string.split(delimiter);
  316. return parts.slice(0, n - 1).concat([parts.slice(n - 1).join(delimiter)]);
  317. }
  318. </script>
  319. <script src="vis-timeline-graph2d.min.js"></script>
  320. <link href="vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" />
  321. <script src="suncalc.js"></script>
  322. </head>
  323. <body>
  324. <script>
  325. function importAuthorSubmitted( form ) {
  326. console.log("submitted:");
  327. console.log(form);
  328. }
  329. var authors = {
  330. "HarlanJI": "https://harlanji.com/bed.xml",
  331. "Marty": "https://harlanji.com/bed-marty.xml"
  332. }
  333. function populateAuthors () {
  334. var authorsElem = document.querySelector("#author");
  335. while(authorsElem.lastChild) {
  336. authorsElem.removeChild(authorsElem.lastChild);
  337. }
  338. Object.entries(authors).forEach(function (e, i) {
  339. var option = document.createElement("option");
  340. option.value = e[1];
  341. option.textContent = e[0];
  342. if ( i == 0 ) {
  343. option.selected = "selected";
  344. }
  345. authorsElem.appendChild(option);
  346. console.log(e);
  347. });
  348. authorsElem.dispatchEvent(new Event('change'));
  349. }
  350. function startOfDay (date) {
  351. var startDate = new Date(date);
  352. startDate.setHours(0);
  353. startDate.setMinutes(0);
  354. startDate.setSeconds(0);
  355. startDate.setMilliseconds(0);
  356. return startDate;
  357. }
  358. </script>
  359. <h1>Author Made Bed</h1>
  360. <form id="import-author" action="#" onsubmit="console.log('s')">
  361. <p>
  362. Author:
  363. <select id="author" onchange="importRssFeed(this.options[this.selectedIndex].value)">
  364. </select>
  365. </p>
  366. </form>
  367. <div id="visualization"></div>
  368. <table id="bed-makings">
  369. <thead>
  370. <tr>
  371. <th>Date Time</th>
  372. <th>#</th>
  373. <th>Note</th>
  374. </tr>
  375. </thead>
  376. <tbody>
  377. </tbody>
  378. </table>
  379. <script>
  380. //importRssFeed( document.querySelector("#author").value );
  381. populateAuthors();
  382. initDataset();
  383. </script>
  384. </body>
  385. </html>