mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 05:07:52 +00:00
Simple text export of Notebook (#6510)
* add simple prototype * tags and metadata now exported * add form for options * revert notebook * add simple e2e test * add test stubs * death to debug
This commit is contained in:
parent
20789601b4
commit
3007b28b0f
@ -377,4 +377,31 @@ test.describe('Notebook entry tests', () => {
|
||||
expect.soft(await sanitizedLink.count()).toBe(1);
|
||||
expect(await unsanitizedLink.count()).toBe(0);
|
||||
});
|
||||
test('can export notebook as text', async ({ page }) => {
|
||||
await nbUtils.enterTextEntry(page, `Foo bar entry`);
|
||||
// Click on 3 Dot Menu
|
||||
await page.locator('button[title="More options"]').click();
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
|
||||
await page.getByRole('menuitem', { name: /Export Notebook as Text/ }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
const download = await downloadPromise;
|
||||
const readStream = await download.createReadStream();
|
||||
const exportedText = await streamToString(readStream);
|
||||
expect(exportedText).toContain('Foo bar entry');
|
||||
});
|
||||
test.fixme('can export multiple notebook entries as text ', async ({ page }) => {});
|
||||
test.fixme('can export all notebook entry metdata', async ({ page }) => {});
|
||||
test.fixme('can export all notebook tags', async ({ page }) => {});
|
||||
test.fixme('can export all notebook snapshots', async ({ page }) => {});
|
||||
|
||||
async function streamToString(readable) {
|
||||
let result = '';
|
||||
for await (const chunk of readable) {
|
||||
result += chunk;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
155
src/plugins/notebook/actions/ExportNotebookAsTextAction.js
Normal file
155
src/plugins/notebook/actions/ExportNotebookAsTextAction.js
Normal file
@ -0,0 +1,155 @@
|
||||
import {saveAs} from 'saveAs';
|
||||
import Moment from 'moment';
|
||||
|
||||
const UNKNOWN_USER = 'Unknown';
|
||||
const UNKNOWN_TIME = 'Unknown';
|
||||
|
||||
export default class ExportNotebookAsTextAction {
|
||||
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-export';
|
||||
this.description = 'Exports notebook contents as a text file';
|
||||
this.group = "action";
|
||||
this.key = 'exportNotebookAsText';
|
||||
this.name = 'Export Notebook as Text';
|
||||
this.priority = 1;
|
||||
}
|
||||
|
||||
invoke(objectPath) {
|
||||
this.showForm(objectPath);
|
||||
}
|
||||
|
||||
getTagName(tagId, availableTags) {
|
||||
const foundTag = availableTags.find(tag => tag.id === tagId);
|
||||
if (foundTag) {
|
||||
return foundTag.label;
|
||||
} else {
|
||||
return tagId;
|
||||
}
|
||||
}
|
||||
|
||||
getTagsForEntry(entry, domainObjectKeyString, annotations) {
|
||||
const foundTags = [];
|
||||
annotations.forEach(annotation => {
|
||||
const target = annotation.targets?.[domainObjectKeyString];
|
||||
if (target?.entryId === entry.id) {
|
||||
annotation.tags.forEach(tag => {
|
||||
if (!foundTags.includes(tag)) {
|
||||
foundTags.push(tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return foundTags;
|
||||
}
|
||||
|
||||
formatTimeStamp(timestamp) {
|
||||
if (timestamp) {
|
||||
return `${Moment.utc(timestamp).format('YYYY-MM-DD HH:mm:ss')} UTC`;
|
||||
} else {
|
||||
return UNKNOWN_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
const domainObject = objectPath[0];
|
||||
const type = this.openmct.types.get(domainObject.type);
|
||||
|
||||
return type?.definition?.name === 'Notebook';
|
||||
}
|
||||
|
||||
async onSave(changes, objectPath) {
|
||||
const availableTags = this.openmct.annotation.getAvailableTags();
|
||||
const identifier = objectPath[0].identifier;
|
||||
const domainObject = await this.openmct.objects.get(identifier);
|
||||
let foundAnnotations = [];
|
||||
// only load annotations if there are tags
|
||||
if (availableTags.length) {
|
||||
foundAnnotations = await this.openmct.annotation.getAnnotations(domainObject.identifier);
|
||||
}
|
||||
|
||||
let notebookAsText = `# ${domainObject.name}\n\n`;
|
||||
|
||||
if (changes.exportMetaData) {
|
||||
const createdTimestamp = domainObject.created;
|
||||
const createdBy = domainObject.createdBy ?? UNKNOWN_USER;
|
||||
const modifiedBy = domainObject.modifiedBy ?? UNKNOWN_USER;
|
||||
const modifiedTimestamp = domainObject.modified ?? domainObject.created;
|
||||
notebookAsText += `Created on ${this.formatTimeStamp(createdTimestamp)} by user ${createdBy}\n\n`;
|
||||
notebookAsText += `Updated on ${this.formatTimeStamp(modifiedTimestamp)} by user ${modifiedBy}\n\n`;
|
||||
}
|
||||
|
||||
const notebookSections = domainObject.configuration.sections;
|
||||
const notebookEntries = domainObject.configuration.entries;
|
||||
|
||||
notebookSections.forEach(section => {
|
||||
notebookAsText += `## ${section.name}\n\n`;
|
||||
|
||||
const notebookPages = section.pages;
|
||||
|
||||
notebookPages.forEach(page => {
|
||||
notebookAsText += `### ${page.name}\n\n`;
|
||||
|
||||
const notebookPageEntries = notebookEntries[section.id]?.[page.id];
|
||||
notebookPageEntries.forEach(entry => {
|
||||
if (changes.exportMetaData) {
|
||||
const createdTimestamp = entry.createdOn;
|
||||
const createdBy = entry.createdBy ?? UNKNOWN_USER;
|
||||
const modifiedBy = entry.modifiedBy ?? UNKNOWN_USER;
|
||||
const modifiedTimestamp = entry.modified ?? entry.created;
|
||||
notebookAsText += `Created on ${this.formatTimeStamp(createdTimestamp)} by user ${createdBy}\n\n`;
|
||||
notebookAsText += `Updated on ${this.formatTimeStamp(modifiedTimestamp)} by user ${modifiedBy}\n\n`;
|
||||
}
|
||||
|
||||
if (changes.exportTags) {
|
||||
const domainObjectKeyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
const tags = this.getTagsForEntry(entry, domainObjectKeyString, foundAnnotations);
|
||||
const tagNames = tags.map(tag => this.getTagName(tag, availableTags));
|
||||
if (tagNames) {
|
||||
notebookAsText += `Tags: ${tagNames.join(', ')}\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
notebookAsText += `${entry.text}\n\n`;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const blob = new Blob([notebookAsText], {type: "text/markdown"});
|
||||
const fileName = domainObject.name + '.md';
|
||||
saveAs(blob, fileName);
|
||||
}
|
||||
|
||||
async showForm(objectPath) {
|
||||
const formStructure = {
|
||||
title: "Export Notebook Text",
|
||||
sections: [
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
key: "exportMetaData",
|
||||
control: "toggleSwitch",
|
||||
name: "Include Metadata (created/modified, etc.)",
|
||||
required: true,
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Include Tags",
|
||||
control: "toggleSwitch",
|
||||
required: true,
|
||||
key: 'exportTags',
|
||||
value: false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const changes = await this.openmct.forms.showForm(formStructure);
|
||||
|
||||
return this.onSave(changes, objectPath);
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import CopyToNotebookAction from './actions/CopyToNotebookAction';
|
||||
import ExportNotebookAsTextAction from './actions/ExportNotebookAsTextAction';
|
||||
import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue';
|
||||
import NotebookViewProvider from './NotebookViewProvider';
|
||||
import NotebookType from './NotebookType';
|
||||
@ -80,6 +81,7 @@ function installBaseNotebookFunctionality(openmct) {
|
||||
};
|
||||
openmct.types.addType('notebookSnapshotImage', notebookSnapshotImageType);
|
||||
openmct.actions.register(new CopyToNotebookAction(openmct));
|
||||
openmct.actions.register(new ExportNotebookAsTextAction(openmct));
|
||||
|
||||
const notebookSnapshotIndicator = new Vue ({
|
||||
components: {
|
||||
|
Loading…
Reference in New Issue
Block a user