Skip to content

Commit

Permalink
Checkpoint: Fetch/Parse/Convert Roam/sr practice Data
Browse files Browse the repository at this point in the history
  • Loading branch information
digitalmaster committed Nov 6, 2022
1 parent 81afa8f commit 43872cc
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 33 deletions.
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[*]
max_line_length = 100
insert_final_newline = true
indent_size = 2
indent_style = space
2 changes: 2 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const App = () => {
if (!refUid) return;

const cardData = practiceCardsData[refUid];
console.log('DEBUG:: ~ file: app.jsx ~ line 26 ~ cardData', cardData);

await practice({ ...cardData, grade, refUid, pluginPageTitle });
};

Expand Down
4 changes: 2 additions & 2 deletions src/components/overlay/PracticeOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import styled from '@emotion/styled';
import useBlockInfo from '~/hooks/useBlockInfo';
import * as asyncUtils from '~/utils/async';
import * as dateUtils from '~/utils/date';
import { getPracticeResultData } from '~/practice';
import { generatePracticeData } from '~/practice';
import Lottie from 'react-lottie';
import doneAnimationData from '~/lotties/done.json';
import Tooltip from '~/components/Tooltip';
Expand Down Expand Up @@ -510,7 +510,7 @@ const Footer = ({
const estimates = {};

for (const grade of grades) {
const practiceResultData = getPracticeResultData({ grade, interval, repetitions, eFactor });
const practiceResultData = generatePracticeData({ grade, interval, repetitions, eFactor });
estimates[grade] = practiceResultData;
}

Expand Down
9 changes: 6 additions & 3 deletions src/hooks/useSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import settingsPanelConfig from '~/settingsPanelConfig';

const defaultSettings = {
tagsListString: 'memo',
pluginPageTitle: 'roam/memo',
// pluginPageTitle: 'roam/memo',
pluginPageTitle: 'roam/memo/debug',
};

// @TODO: Refactor/Hoist this so we can call useSettings in multiple places
Expand All @@ -23,8 +24,10 @@ const useSettings = () => {

React.useEffect(() => {
// Init config panel
window.roamMemo.extensionAPI.settings.panel.create(settingsPanelConfig({ setSettings }));
}, [setSettings]);
window.roamMemo.extensionAPI.settings.panel.create(
settingsPanelConfig({ setSettings, pluginPageTitle: settings.pluginPageTitle })
);
}, [setSettings, settings.pluginPageTitle]);

React.useEffect(() => {
const allSettings = window.roamMemo.extensionAPI.settings.getAll() || {};
Expand Down
45 changes: 35 additions & 10 deletions src/practice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ export const supermemo = (item, grade) => {
};
};

export const getPracticeResultData = ({ grade, interval, repetitions, eFactor }) => {
export const generatePracticeData = ({
grade,
interval,
repetitions,
eFactor,
baseDate = new Date(),
}) => {
const supermemoInput = {
interval,
repetition: repetitions,
Expand All @@ -48,7 +54,7 @@ export const getPracticeResultData = ({ grade, interval, repetitions, eFactor })
// call supermemo API
const supermemoResults = supermemo(supermemoInput, grade);

const nextDueDate = dateUtils.addDays(new Date(), supermemoResults.interval);
const nextDueDate = dateUtils.addDays(baseDate, supermemoResults.interval);

return {
repetitions: supermemoResults.repetition,
Expand All @@ -59,22 +65,41 @@ export const getPracticeResultData = ({ grade, interval, repetitions, eFactor })
};
};

const practice = async ({ interval, repetitions, eFactor, grade, refUid, pluginPageTitle }) => {
interface Practice {
interval: number;
repetitions: number;
eFactor: number;
grade: number;
refUid: string;
pluginPageTitle: string;
dateCreated: null | Date;
}

const practice = async (
{ interval, repetitions, eFactor, grade, refUid, pluginPageTitle, dateCreated = null }: Practice,
isDryRun = false
) => {
// Just don't want to store nextDueDateFromNow
// eslint-disable-next-line no-unused-vars
const { nextDueDateFromNow, ...practiceResultData } = getPracticeResultData({
const { nextDueDateFromNow, ...practiceResultData } = generatePracticeData({
grade,
interval,
repetitions,
eFactor,
baseDate: dateCreated,
});

await savePracticeData({
refUid: refUid,
pluginPageTitle,
grade,
...practiceResultData,
});
if (!isDryRun) {
await savePracticeData({
refUid: refUid,
pluginPageTitle,
grade,
dateCreated,
...practiceResultData,
});
}

return practiceResultData;
};

export default practice;
4 changes: 1 addition & 3 deletions src/queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,6 @@ describe.skip('getPluginPageData', () => {
},
}));

expect(
queries.getPluginPageData({ pluginPageTitle: 'roam/memo', dataBlockName: 'data' })
).resolves.toBe({});
expect(queries.getPluginPageData({ pluginPageTitle: 'roam/memo' })).resolves.toBe({});
});
});
115 changes: 101 additions & 14 deletions src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,21 @@ const mapPluginPageData = (queryResultsData) =>
return acc;
}, {}) || {};

export const getPluginPageData = async ({ pluginPageTitle, dataBlockName }) => {
export const getPluginPageData = async ({ pluginPageTitle }) => {
const q = `[
:find (pull ?pluginPageChildren [
:block/string
:block/children
:block/order
{:block/children ...}])
:in $ ?pageTitle ?dataBlockName
:where
:block/string
:block/children
:block/order
{:block/children ...}])
:in $ ?pageTitle ?dataBlockName
:where
[?page :node/title ?pageTitle]
[?page :block/children ?pluginPageChildren]
[?pluginPageChildren :block/string ?dataBlockName]
]`;

const dataBlockName = 'data';
const queryResultsData = await window.roamAlphaAPI.q(q, pluginPageTitle, dataBlockName);

if (!queryResultsData.length) return {};
Expand All @@ -80,17 +81,16 @@ export const getDueCardUids = (data) => {
return results;
};

const generateNewCardProps = () => ({
dateCreated: new Date(),
export const generateNewCardProps = ({ dateCreated } = {}) => ({
dateCreated: dateCreated || new Date(),
eFactor: 2.5,
interval: 0,
repetitions: 0,
isNew: true,
});

export const getPracticeCardData = async ({ selectedTag, pluginPageTitle }) => {
const dataBlockName = 'data';
const pluginPageData = await getPluginPageData({ pluginPageTitle, dataBlockName });
const pluginPageData = await getPluginPageData({ pluginPageTitle });

const selectedTagReferencesIds = await getPageReferenceIds(selectedTag);
const cardsData = { ...pluginPageData };
Expand Down Expand Up @@ -333,7 +333,7 @@ const getEmojiFromGrade = (grade) => {
}
};

export const savePracticeData = async ({ refUid, pluginPageTitle, ...data }) => {
export const savePracticeData = async ({ refUid, pluginPageTitle, dateCreated, ...data }) => {
await getOrCreatePage(pluginPageTitle);
const dataBlockUid = await getOrCreateBlockOnPage(pluginPageTitle, 'data', -1, {
open: false,
Expand All @@ -345,11 +345,11 @@ export const savePracticeData = async ({ refUid, pluginPageTitle, ...data }) =>
open: false,
});

const todayRoamDateString = stringUtils.dateToRoamDateString(new Date());
const dateCreatedRoamDateString = stringUtils.dateToRoamDateString(dateCreated || new Date());
const emoji = getEmojiFromGrade(data.grade);
const newDataBlockId = await createChildBlock(
cardDataBlockUid,
`[[${todayRoamDateString}]] ${emoji}`,
`[[${dateCreatedRoamDateString}]] ${emoji}`,
0,
{
open: false,
Expand All @@ -367,3 +367,90 @@ export const savePracticeData = async ({ refUid, pluginPageTitle, ...data }) =>
await createChildBlock(newDataBlockId, `${key}:: ${value}`, -1);
}
};

const oldRoamSrGradeMap = {
['r/1']: {
oldDisplayButtonText: 'Again',
memoDisplayButtonText: 'Forgot',
memoGrade: 0,
},
['r/2']: {
oldDisplayButtonText: 'Hard',
memoDisplayButtonText: 'Hard',
memoGrade: 3,
},
['r/3']: {
oldDisplayButtonText: 'Good',
memoDisplayButtonText: 'Good',
memoGrade: 4,
},
['r/4']: {
oldDisplayButtonText: 'Easy',
memoDisplayButtonText: 'Perfect',
memoGrade: 5,
},
};

const mapOldRoamSrPracticeData = (data, pageTitle) => {
return data.reduce((acc, [result]) => {
const dateString = result.title;

result.children
// Filter out unrelated rows (could probably do this in query but ya 🤷🏽‍♂️)
.filter((row) => row.string === `[[${pageTitle}]]`)
// Filter out rows with no records
.filter((row) => !!row.children)
.forEach(({ children: recordList }) => {
for (const { string: record } of recordList) {
const values = record.split(' ');
// Skip all invalid records: [uid, gradeString]
if (values.length !== 2) continue;

const [refUidString, gradeString] = values;

// Skip empty string values 🤷🏽
if (!refUidString || !gradeString) continue;

const refUid = getStringBetween(refUidString, '((', '))');
if (!acc[refUid]) acc[refUid] = [];
acc[refUid].push({
refUid,
grade: oldRoamSrGradeMap[getStringBetween(gradeString, '[[', ']]')].memoGrade,
dateCreated: parseRoamDateString(dateString),
});
}
});

return acc;
}, {});
};

const sortOldRoamSrPracticeData = (mappedData) => {
for (const key in mappedData) {
mappedData[key] = mappedData[key].sort((a, b) => a.dateCreated - b.dateCreated);
}
return mappedData;
};

export const getOldRoamSrPracticeData = async () => {
const reviewTagPageName = 'roam/sr/review';
const reviewPageReferenceData = await window.roamAlphaAPI.q(
`
[:find (pull ?parentPageId [
:node/title
:block/string
:block/children
{:block/children ...}])
:in $ ?reviewTagPageName
:where
[?pageId :node/title ?reviewTagPageName]
[?refIds :block/refs ?pageId]
[?parentPageId :block/children, ?refIds]
]`,
reviewTagPageName
);

return sortOldRoamSrPracticeData(
mapOldRoamSrPracticeData(reviewPageReferenceData, reviewTagPageName)
);
};
14 changes: 13 additions & 1 deletion src/settingsPanelConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as asyncUtils from '~/utils/async';
import { importRoamSrOldData } from '~/utils/migration';

const settingsPanelConfig = ({ setSettings }) => {
const settingsPanelConfig = ({ setSettings, pluginPageTitle }) => {
// importRoamSrOldData({ pluginPageTitle }); // temp
const syncFn = async (e) => {
const tagsListString = e.target.value.trim();
window.roamMemo.extensionAPI.settings.set('tagsListString', tagsListString);
Expand All @@ -24,6 +26,16 @@ const settingsPanelConfig = ({ setSettings }) => {
onChange: processChange,
},
},
{
id: 'import-roam-sr-data',
name: 'Import Roam/Sr Data',
description: 'Import Roam Sr Old data',
action: {
type: 'button',
onClick: () => importRoamSrOldData({ pluginPageTitle }),
content: 'Fetch Data',
},
},
],
};
};
Expand Down
55 changes: 55 additions & 0 deletions src/utils/migration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import practice from '~/practice';
import * as queries from '~/queries';

export const importRoamSrOldData = async ({ pluginPageTitle }) => {
const oldReviewData = await queries.getOldRoamSrPracticeData();
const newReviewData = await convertRoamSrPracticeData(oldReviewData, pluginPageTitle);
console.log('DEBUG:: ~ file: migration.ts ~ line 8 ~ newReviewData', newReviewData);

// Used to Filter blocks that already exist
const pluginPageData = await queries.getPluginPageData({ pluginPageTitle });
// Ignore old data if already started practicing
// if (typeof pluginPageData[refUid] !== 'undefined') continue;
};

const convertRoamSrPracticeData = async (oldReviewRecords, pluginPageTitle) => {
const results = {};
for (const [_, resultsArr] of Object.entries(oldReviewRecords)) {
//@ts-ignore
for (const result of resultsArr) {
const { refUid, dateCreated, grade } = result;

let practiceInputData = {
refUid,
grade,
pluginPageTitle,
dateCreated,
eFactor: undefined,
repetitions: undefined,
interval: undefined,
};

if (results[refUid]) {
const lastPracticeResult = results[refUid][results[refUid].length - 1];
const { eFactor, repetitions, interval } = lastPracticeResult;
practiceInputData = { ...practiceInputData, eFactor, repetitions, interval };
} else {
const newCardData = queries.generateNewCardProps({ dateCreated });
practiceInputData = { ...practiceInputData, ...newCardData };
}

const practiceResult = {
...(await practice(practiceInputData, true)),
grade,
dateCreated,
};
if (results[refUid]) {
results[refUid].push(practiceResult);
} else {
results[refUid] = [practiceResult];
}
}
}

return results;
};

0 comments on commit 43872cc

Please sign in to comment.