aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStanley Yip <stanley_yip@brown.edu>2020-01-20 13:45:31 -0500
committerStanley Yip <stanley_yip@brown.edu>2020-01-20 13:45:31 -0500
commit87bdb7c7eafa1e91e371c9d88b8dd36197f497d3 (patch)
tree65191f06e01e9eab74ecd40c20f3476e532d061c /src
parenta61dfaab422886733a727eebe80b619230d59684 (diff)
parentdf61d40f92a29f948d9f513fb9f50349f16500ee (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into pen
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts48
-rw-r--r--src/client/util/DropConverter.ts10
-rw-r--r--src/client/views/TemplateMenu.tsx4
-rw-r--r--src/client/views/collections/CollectionMulticolumnView.scss23
-rw-r--r--src/client/views/collections/CollectionPivotView.tsx4
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx12
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx31
-rw-r--r--src/client/views/collections/CollectionView.tsx21
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss33
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx (renamed from src/client/views/collections/CollectionMulticolumnView.tsx)238
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx116
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx56
-rw-r--r--src/client/views/nodes/DocumentView.tsx15
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/scraping/buxton/node_scraper.ts57
-rw-r--r--src/scraping/buxton/scraper.py67
-rw-r--r--src/scraping/buxton/source/Bill_Notes_NB75D.docxbin0 -> 27696302 bytes
17 files changed, 527 insertions, 210 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2aa848f5c..7e7b10ae8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -19,7 +19,7 @@ import { AggregateFunction } from "../northstar/model/idea/idea";
import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
import { IconBox } from "../views/nodes/IconBox";
import { OmitKeys, JSONUtils } from "../../Utils";
-import { Field, Doc, Opt, DocListCastAsync } from "../../new_fields/Doc";
+import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../new_fields/Doc";
import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField";
import { HtmlField } from "../../new_fields/HtmlField";
import { List } from "../../new_fields/List";
@@ -51,6 +51,7 @@ import { DocuLinkBox } from "../views/nodes/DocuLinkBox";
import { DocumentBox } from "../views/nodes/DocumentBox";
import { InkingStroke } from "../views/InkingStroke";
import { InkField } from "../../new_fields/InkField";
+import { InkingControl } from "../views/InkingControl";
const requestImageSize = require('../util/request-image-size');
const path = require('path');
@@ -522,6 +523,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Stacking });
}
+ export function MulticolumnDocument(documents: Array<Doc>, options: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Multicolumn });
+ }
+
export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Masonry });
}
@@ -660,6 +665,47 @@ export namespace Docs {
throw new Error(`How did ${data} of type ${typeof data} end up in JSON?`);
};
+ export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined {
+ let created: Doc | undefined;
+ let layout: ((fieldKey: string) => string) | undefined;
+ const field = target[fieldKey];
+ const resolved = options || {};
+ if (field instanceof ImageField) {
+ created = Docs.Create.ImageDocument((field as ImageField).url.href, resolved);
+ layout = ImageBox.LayoutString;
+ } else if (field instanceof Doc) {
+ created = field;
+ } else if (field instanceof VideoField) {
+ created = Docs.Create.VideoDocument((field as VideoField).url.href, resolved);
+ layout = VideoBox.LayoutString;
+ } else if (field instanceof PdfField) {
+ created = Docs.Create.PdfDocument((field as PdfField).url.href, resolved);
+ layout = PDFBox.LayoutString;
+ } else if (field instanceof IconField) {
+ created = Docs.Create.IconDocument((field as IconField).icon, resolved);
+ layout = IconBox.LayoutString;
+ } else if (field instanceof AudioField) {
+ created = Docs.Create.AudioDocument((field as AudioField).url.href, resolved);
+ layout = AudioBox.LayoutString;
+ } else if (field instanceof HistogramField) {
+ created = Docs.Create.HistogramDocument((field as HistogramField).HistoOp, resolved);
+ layout = HistogramBox.LayoutString;
+ } else if (field instanceof InkField) {
+ const { selectedColor, selectedWidth, selectedTool } = InkingControl.Instance;
+ created = Docs.Create.InkDocument(selectedColor, selectedTool, Number(selectedWidth), (field as InkField).inkData, resolved);
+ layout = InkingStroke.LayoutString;
+ } else if (field instanceof List && field[0] instanceof Doc) {
+ created = Docs.Create.StackingDocument(DocListCast(field), resolved);
+ layout = CollectionView.LayoutString;
+ } else {
+ created = Docs.Create.TextDocument({ ...{ width: 200, height: 25, autoHeight: true }, ...resolved });
+ layout = FormattedTextBox.LayoutString;
+ }
+ created.layout = layout?.(fieldKey);
+ proto && (created.proto = Doc.GetProto(proto));
+ return created;
+ }
+
export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> {
let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
if (type.indexOf("image") !== -1) {
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index da0ad7efe..ff0e19347 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -7,8 +7,7 @@ import { StrCast } from "../../new_fields/Types";
import { Docs } from "../documents/Documents";
import { ScriptField } from "../../new_fields/ScriptField";
-
-function makeTemplate(doc: Doc): boolean {
+export function makeTemplate(doc: Doc, suppressTitle = false): boolean {
const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0];
const fieldKey = layout.replace("fieldKey={'", "").replace(/'}$/, "");
@@ -17,7 +16,7 @@ function makeTemplate(doc: Doc): boolean {
docs.forEach(d => {
if (!StrCast(d.title).startsWith("-")) {
any = true;
- Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc));
+ Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc), suppressTitle);
} else if (d.type === DocumentType.COL) {
any = makeTemplate(d) || any;
}
@@ -27,7 +26,8 @@ function makeTemplate(doc: Doc): boolean {
export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
data && data.draggedDocuments.map((doc, i) => {
let dbox = doc;
- if (!doc.onDragStart && !doc.onClick && doc.viewType !== CollectionViewType.Linear) {
+ // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
+ if (!doc.onDragStart && !doc.onClick && !doc.isButtonBar) {
const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
if (layoutDoc.type === DocumentType.COL) {
layoutDoc.isTemplateDoc = makeTemplate(layoutDoc);
@@ -38,7 +38,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
dbox.dragFactory = layoutDoc;
dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
- } else if (doc.viewType === CollectionViewType.Linear) {
+ } else if (doc.isButtonBar) {
dbox.ignoreClick = true;
}
data.droppedDocuments[i] = dbox;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 8f2ec4bef..d953d3bab 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -58,6 +58,9 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
toggleCustom = (e: React.ChangeEvent<HTMLInputElement>): void => {
this.props.docs.map(dv => dv.setCustomView(e.target.checked));
}
+ toggleNarrative = (e: React.ChangeEvent<HTMLInputElement>): void => {
+ this.props.docs.map(dv => dv.setNarrativeView(e.target.checked));
+ }
toggleFloat = (e: React.ChangeEvent<HTMLInputElement>): void => {
SelectionManager.DeselectAll();
@@ -146,6 +149,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
templateMenu.push(<OtherToggle key={"float"} name={"Float"} checked={this.props.docs[0].Document.z ? true : false} toggle={this.toggleFloat} />);
templateMenu.push(<OtherToggle key={"custom"} name={"Custom"} checked={StrCast(this.props.docs[0].Document.layoutKey, "layout") !== "layout"} toggle={this.toggleCustom} />);
+ templateMenu.push(<OtherToggle key={"narrative"} name={"Narrative"} checked={StrCast(this.props.docs[0].Document.layoutKey, "layout") === "layout_narrative"} toggle={this.toggleNarrative} />);
templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout.chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
return (
<Flyout anchorPoint={anchorPoints.LEFT_TOP}
diff --git a/src/client/views/collections/CollectionMulticolumnView.scss b/src/client/views/collections/CollectionMulticolumnView.scss
deleted file mode 100644
index a54af748b..000000000
--- a/src/client/views/collections/CollectionMulticolumnView.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-.collectionMulticolumnView_contents {
- display: flex;
- width: 100%;
- height: 100%;
- overflow: hidden;
-
- .document-wrapper {
- display: flex;
- flex-direction: column;
-
- .display {
- text-align: center;
- height: 20px;
- }
-
- }
-
- .resizer {
- background: black;
- cursor: ew-resize;
- }
-
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 6af7cce70..53ad433b3 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -20,7 +20,7 @@ import { Set } from "typescript-collections";
export class CollectionPivotView extends CollectionSubView(doc => doc) {
componentDidMount = () => {
this.props.Document.freeformLayoutEngine = "pivot";
- if (!this.props.Document.facetCollection) {
+ if (true || !this.props.Document.facetCollection) {
const facetCollection = Docs.Create.FreeformDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true });
facetCollection.target = this.props.Document;
@@ -34,7 +34,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) {
facetCollection.onCheckedClick = new ScriptField(script);
}
- const openDocText = "const alias = getAlias(this); alias.layoutKey = 'layoutCustom'; useRightSplit(alias); ";
+ const openDocText = "const alias = getAlias(this); alias.layoutKey = 'layout_detailed'; useRightSplit(alias); ";
const openDocScript = CompileScript(openDocText, {
params: { this: Doc.name, heading: "boolean", checked: "boolean", context: Doc.name },
typecheck: false,
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 39b4e4e1d..23a664359 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -18,6 +18,9 @@ import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import "./CollectionStackingView.scss";
import { TraceMobx } from "../../../new_fields/util";
+import { FormattedTextBox } from "../nodes/FormattedTextBox";
+import { ImageField } from "../../../new_fields/URLField";
+import { ImageBox } from "../nodes/ImageBox";
library.add(faPalette);
@@ -132,6 +135,15 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
addDocument = (value: string, shiftDown?: boolean) => {
+ if (value === ":freeForm") {
+ return this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { width: 200, height: 200 }));
+ } else if (value.startsWith(":")) {
+ const { Document, addDocument } = this.props.parent.props;
+ const fieldKey = value.substring(1);
+ const proto = Doc.GetProto(Document);
+ const created = Docs.Get.DocumentFromField(Document, fieldKey, proto);
+ return created ? addDocument(created) : false;
+ }
this._createAliasSelected = false;
const key = StrCast(this.props.parent.props.Document.sectionFilter);
const newDoc = Docs.Create.TextDocument({ height: 18, width: 200, documentText: "@@@" + value, title: value, autoHeight: true });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 70860b6bd..a48208bd9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -29,6 +29,9 @@ import "./CollectionTreeView.scss";
import React = require("react");
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { ScriptBox } from '../ScriptBox';
+import { ImageBox } from '../nodes/ImageBox';
+import { makeTemplate } from '../../util/DropConverter';
+import { CollectionDockingView } from './CollectionDockingView';
export interface TreeViewProps {
@@ -628,9 +631,35 @@ export class CollectionTreeView extends CollectionSubView(Document) {
layoutItems.push({ description: (this.props.Document.hideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.hideHeaderFields = !this.props.Document.hideHeaderFields, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
}
+ ContextMenu.Instance.addItem({
+ description: "Buxton Layout", icon: "eye", event: () => {
+ const { TextDocument, ImageDocument } = Docs.Create;
+ const wrapper = Docs.Create.StackingDocument([
+ ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "hero" }),
+ TextDocument({ title: "year" }),
+ TextDocument({ title: "degrees_of_freedom" }),
+ TextDocument({ title: "company" }),
+ TextDocument({ title: "short_description" }),
+ ], { autoHeight: true, chromeStatus: "disabled" });
+ wrapper.disableLOD = true;
+ makeTemplate(wrapper, true);
+ const detailedLayout = Doc.MakeAlias(wrapper);
+ const cardLayout = ImageBox.LayoutString("hero");
+ this.childLayoutPairs.forEach(({ layout }) => {
+ const proto = Doc.GetProto(layout);
+ proto.layout = cardLayout;
+ proto.layout_detailed = detailedLayout;
+ layout.showTitle = "title";
+ layout.showTitleHover = "titlehover";
+ });
+ }
+ });
const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
- onClicks.push({ description: "Edit onChecked Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Checked Changed ...", this.props.Document, "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean" }) });
+ onClicks.push({
+ description: "Edit onChecked Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Checked Changed ...", this.props.Document,
+ "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean", context: Doc.name })
+ });
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
}
outerXf = () => Utils.GetScreenTransform(this._mainEle!);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4bd456233..a665b678b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,7 +1,7 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEye } from '@fortawesome/free-regular-svg-icons';
import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, IReactionDisposer, observable, reaction, runInAction, computed } from 'mobx';
import { observer } from "mobx-react";
import * as React from 'react';
import Lightbox from 'react-image-lightbox-with-rotate';
@@ -10,7 +10,7 @@ import { DateField } from '../../../new_fields/DateField';
import { Doc, DocListCast } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { listSpec } from '../../../new_fields/Schema';
-import { BoolCast, Cast, StrCast } from '../../../new_fields/Types';
+import { BoolCast, Cast, StrCast, NumCast } from '../../../new_fields/Types';
import { ImageField } from '../../../new_fields/URLField';
import { TraceMobx } from '../../../new_fields/util';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
@@ -27,7 +27,7 @@ import { CollectionDockingView } from "./CollectionDockingView";
import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionLinearView } from './CollectionLinearView';
-import { CollectionMulticolumnView } from './CollectionMulticolumnView';
+import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionPivotView } from './CollectionPivotView';
import { CollectionSchemaView } from "./CollectionSchemaView";
import { CollectionStackingView } from './CollectionStackingView';
@@ -50,7 +50,8 @@ export enum CollectionViewType {
Pivot,
Linear,
Staff,
- Multicolumn
+ Multicolumn,
+ Timeline
}
export namespace CollectionViewType {
@@ -90,8 +91,18 @@ export class CollectionView extends Touchable<FieldViewProps> {
@observable private static _safeMode = false;
public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
+ @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); }
+ @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+
get collectionViewType(): CollectionViewType | undefined {
- const viewField = Cast(this.props.Document.viewType, "number");
+ if (!this.extensionDoc) return CollectionViewType.Invalid;
+ NumCast(this.props.Document.viewType) && setTimeout(() => {
+ if (this.props.Document.viewType) {
+ this.extensionDoc!.viewType = NumCast(this.props.Document.viewType);
+ }
+ Doc.GetProto(this.props.Document).viewType = this.props.Document.viewType = undefined;
+ });
+ const viewField = NumCast(this.extensionDoc.viewType, Cast(this.props.Document.viewType, "number"));
if (CollectionView._safeMode) {
if (viewField === CollectionViewType.Freeform) {
return CollectionViewType.Tree;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
new file mode 100644
index 000000000..f57ba438a
--- /dev/null
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -0,0 +1,33 @@
+.collectionMulticolumnView_contents {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+
+ .document-wrapper {
+ display: flex;
+ flex-direction: column;
+
+ .label-wrapper {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ height: 20px;
+ }
+
+ }
+
+ .resizer {
+ cursor: ew-resize;
+ transition: 0.5s opacity ease;
+ display: flex;
+ flex-direction: column;
+
+ .internal {
+ width: 100%;
+ height: 100%;
+ transition: 0.5s background-color ease;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 955e87f13..70e56183c 100644
--- a/src/client/views/collections/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,76 +1,93 @@
import { observer } from 'mobx-react';
-import { makeInterface } from '../../../new_fields/Schema';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { CollectionSubView } from './CollectionSubView';
+import { makeInterface } from '../../../../new_fields/Schema';
+import { documentSchema } from '../../../../new_fields/documentSchemas';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import * as React from "react";
-import { Doc } from '../../../new_fields/Doc';
-import { NumCast, StrCast, BoolCast } from '../../../new_fields/Types';
-import { ContentFittingDocumentView } from './../nodes/ContentFittingDocumentView';
-import { Utils } from '../../../Utils';
+import { Doc } from '../../../../new_fields/Doc';
+import { NumCast, StrCast, BoolCast } from '../../../../new_fields/Types';
+import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
+import { Utils } from '../../../../Utils';
import "./collectionMulticolumnView.scss";
-import { computed, trace } from 'mobx';
-import { Transform } from '../../util/Transform';
+import { computed, trace, observable, action } from 'mobx';
+import { Transform } from '../../../util/Transform';
+import WidthLabel from './MulticolumnWidthLabel';
+import ResizeBar from './MulticolumnResizer';
type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
const MulticolumnDocument = makeInterface(documentSchema);
-interface Unresolved {
- target: Doc;
+interface WidthSpecifier {
magnitude: number;
unit: string;
}
-interface Resolved {
- target: Doc;
- pixels: number;
-}
-
interface LayoutData {
- unresolved: Unresolved[];
- numFixed: number;
- numRatio: number;
+ widthSpecifiers: WidthSpecifier[];
starSum: number;
}
-const resolvedUnits = ["*", "px"];
-const resizerWidth = 2;
-const resizerOpacity = 0.4;
+export const WidthUnit = {
+ Pixel: "px",
+ Ratio: "*"
+};
+
+const resolvedUnits = Object.values(WidthUnit);
+const resizerWidth = 4;
@observer
export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) {
+ /**
+ * @returns the list of layout documents whose width unit is
+ * *, denoting that it will be displayed with a ratio, not fixed pixel, value
+ */
@computed
private get ratioDefinedDocs() {
- return this.childLayoutPairs.map(({ layout }) => layout).filter(({ widthUnit }) => StrCast(widthUnit) === "*");
+ return this.childLayoutPairs.map(({ layout }) => layout).filter(({ widthUnit }) => StrCast(widthUnit) === WidthUnit.Ratio);
}
+ /**
+ * This loops through all childLayoutPairs and extracts the values for widthUnit
+ * and widthMagnitude, ignoring any that are malformed. Additionally, it then
+ * normalizes the ratio values so that one * value is always 1, with the remaining
+ * values proportionate to that easily readable metric.
+ * @returns the list of the resolved width specifiers (unit and magnitude pairs)
+ * as well as the sum of the * coefficients, i.e. the ratio magnitudes
+ */
@computed
private get resolvedLayoutInformation(): LayoutData {
- const unresolved: Unresolved[] = [];
- let starSum = 0, numFixed = 0, numRatio = 0;
-
- for (const { layout } of this.childLayoutPairs) {
- const unit = StrCast(layout.widthUnit);
- const magnitude = NumCast(layout.widthMagnitude);
+ let starSum = 0;
+ const widthSpecifiers: WidthSpecifier[] = [];
+ this.childLayoutPairs.map(({ layout: { widthUnit, widthMagnitude } }) => {
+ const unit = StrCast(widthUnit);
+ const magnitude = NumCast(widthMagnitude);
if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
- if (unit === "*") {
- starSum += magnitude;
- numRatio++;
- } else {
- numFixed++;
- }
- unresolved.push({ target: layout, magnitude, unit });
+ (unit === WidthUnit.Ratio) && (starSum += magnitude);
+ widthSpecifiers.push({ magnitude, unit });
}
- // otherwise, the particular configuration entry is ignored and the remaining
- // space is allocated as if the document were absent from the configuration list
- }
+ /**
+ * Otherwise, the child document is ignored and the remaining
+ * space is allocated as if the document were absent from the child list
+ */
+ });
+ /**
+ * Here, since these values are all relative, adjustments during resizing or
+ * manual updating can, though their ratios remain the same, cause the values
+ * themselves to drift toward zero. Thus, whenever we change any of the values,
+ * we normalize everything (dividing by the smallest magnitude).
+ */
setTimeout(() => {
- const minimum = Math.min(...this.ratioDefinedDocs.map(({ widthMagnitude }) => NumCast(widthMagnitude)));
- this.ratioDefinedDocs.forEach(layout => layout.widthMagnitude = NumCast(layout.widthMagnitude) / minimum);
+ const { ratioDefinedDocs } = this;
+ if (this.childLayoutPairs.length) {
+ const minimum = Math.min(...ratioDefinedDocs.map(({ widthMagnitude }) => NumCast(widthMagnitude)));
+ if (minimum !== 0) {
+ ratioDefinedDocs.forEach(layout => layout.widthMagnitude = NumCast(layout.widthMagnitude) / minimum);
+ }
+ }
});
- return { unresolved, numRatio, numFixed, starSum };
+ return { widthSpecifiers, starSum };
}
/**
@@ -83,12 +100,12 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
*/
@computed
private get totalFixedAllocation(): number | undefined {
- return this.resolvedLayoutInformation?.unresolved.reduce(
- (sum, { magnitude, unit }) => sum + (unit === "px" ? magnitude : 0), 0);
+ return this.resolvedLayoutInformation?.widthSpecifiers.reduce(
+ (sum, { magnitude, unit }) => sum + (unit === WidthUnit.Pixel ? magnitude : 0), 0);
}
/**
- * This returns the total quantity, in pixels, that this
+ * @returns the total quantity, in pixels, that this
* view needs to reserve for child documents that have
* (with lower priority) requested a certain relative proportion of the
* remaining pixel width not allocated for fixed widths.
@@ -98,14 +115,14 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
*/
@computed
private get totalRatioAllocation(): number | undefined {
- const layoutInfoLen = this.resolvedLayoutInformation?.unresolved.length;
+ const layoutInfoLen = this.resolvedLayoutInformation.widthSpecifiers.length;
if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
return this.props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1));
}
}
/**
- * This returns the total quantity, in pixels, that
+ * @returns the total quantity, in pixels, that
* 1* (relative / star unit) is worth. For example,
* if the configuration has three documents, with, respectively,
* widths of 2*, 2* and 1*, and the panel width returns 1000px,
@@ -123,20 +140,37 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
}
}
+ /**
+ * This wrapper function exists to prevent mobx from
+ * needlessly rerendering the internal ContentFittingDocumentViews
+ */
private getColumnUnitLength = () => this.columnUnitLength;
+ /**
+ * @param layout the document whose transform we'd like to compute
+ * Given a layout document, this function
+ * returns the resolved width it has requested, in pixels.
+ * @returns the stored column width if already in pixels,
+ * or the ratio width evaluated to a pixel value
+ */
private lookupPixels = (layout: Doc): number => {
const columnUnitLength = this.columnUnitLength;
if (columnUnitLength === undefined) {
return 0; // we're still waiting on promises to resolve
}
let width = NumCast(layout.widthMagnitude);
- if (StrCast(layout.widthUnit) === "*") {
+ if (StrCast(layout.widthUnit) === WidthUnit.Ratio) {
width *= columnUnitLength;
}
return width;
}
+ /**
+ * @returns the transform that will correctly place
+ * the document decorations box, shifted to the right by
+ * the sum of all the resolved column widths of the
+ * documents before the target.
+ */
private lookupIndividualTransform = (layout: Doc) => {
const columnUnitLength = this.columnUnitLength;
if (columnUnitLength === undefined) {
@@ -145,27 +179,31 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
let offset = 0;
for (const { layout: candidate } of this.childLayoutPairs) {
if (candidate === layout) {
- const shift = offset;
- return this.props.ScreenToLocalTransform().translate(-shift, 0);
+ return this.props.ScreenToLocalTransform().translate(-offset, 0);
}
offset += this.lookupPixels(candidate) + resizerWidth;
}
return Transform.Identity(); // type coersion, this case should never be hit
}
+ /**
+ * @returns the resolved list of rendered child documents, displayed
+ * at their resolved pixel widths, each separated by a resizer.
+ */
@computed
private get contents(): JSX.Element[] | null {
- trace();
const { childLayoutPairs } = this;
const { Document, PanelHeight } = this.props;
const collector: JSX.Element[] = [];
for (let i = 0; i < childLayoutPairs.length; i++) {
const { layout } = childLayoutPairs[i];
collector.push(
- <div className={"document-wrapper"}>
+ <div
+ className={"document-wrapper"}
+ key={Utils.GenerateGuid()}
+ >
<ContentFittingDocumentView
{...this.props}
- key={Utils.GenerateGuid()}
Document={layout}
DataDocument={layout.resolvedDataDoc as Doc}
PanelWidth={() => this.lookupPixels(layout)}
@@ -201,98 +239,4 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
);
}
-}
-
-interface SpacerProps {
- width: number;
- columnUnitLength(): number | undefined;
- toLeft?: Doc;
- toRight?: Doc;
-}
-
-interface WidthLabelProps {
- layout: Doc;
- collectionDoc: Doc;
- decimals?: number;
-}
-
-@observer
-class WidthLabel extends React.Component<WidthLabelProps> {
-
- @computed
- private get contents() {
- const { layout, decimals } = this.props;
- const magnitude = NumCast(layout.widthMagnitude).toFixed(decimals ?? 3);
- const unit = StrCast(layout.widthUnit);
- return <span className={"display"}>{magnitude} {unit}</span>;
- }
-
- render() {
- return BoolCast(this.props.collectionDoc.showWidthLabels) ? this.contents : (null);
- }
-
-}
-
-@observer
-class ResizeBar extends React.Component<SpacerProps> {
-
- private registerResizing = (e: React.PointerEvent<HTMLDivElement>) => {
- e.stopPropagation();
- e.preventDefault();
- window.removeEventListener("pointermove", this.onPointerMove);
- window.removeEventListener("pointerup", this.onPointerUp);
- window.addEventListener("pointermove", this.onPointerMove);
- window.addEventListener("pointerup", this.onPointerUp);
- }
-
- private onPointerMove = ({ movementX }: PointerEvent) => {
- const { toLeft, toRight, columnUnitLength } = this.props;
- const target = movementX > 0 ? toRight : toLeft;
- let scale = columnUnitLength();
- if (target && scale) {
- const { widthUnit, widthMagnitude } = target;
- scale = widthUnit === "*" ? scale : 1;
- target.widthMagnitude = NumCast(widthMagnitude) - Math.abs(movementX) / scale;
- }
- }
-
- private get isActivated() {
- const { toLeft, toRight } = this.props;
- if (toLeft && toRight) {
- if (StrCast(toLeft.widthUnit) === "px" && StrCast(toRight.widthUnit) === "px") {
- return false;
- }
- return true;
- } else if (toLeft) {
- if (StrCast(toLeft.widthUnit) === "px") {
- return false;
- }
- return true;
- } else if (toRight) {
- if (StrCast(toRight.widthUnit) === "px") {
- return false;
- }
- return true;
- }
- return false;
- }
-
- private onPointerUp = () => {
- window.removeEventListener("pointermove", this.onPointerMove);
- window.removeEventListener("pointerup", this.onPointerUp);
- }
-
- render() {
- return (
- <div
- className={"resizer"}
- style={{
- width: this.props.width,
- opacity: this.isActivated ? resizerOpacity : 0
- }}
- onPointerDown={this.registerResizing}
- />
- );
- }
-
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
new file mode 100644
index 000000000..11e210958
--- /dev/null
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -0,0 +1,116 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+import { Doc } from "../../../../new_fields/Doc";
+import { NumCast, StrCast } from "../../../../new_fields/Types";
+import { WidthUnit } from "./CollectionMulticolumnView";
+
+interface ResizerProps {
+ width: number;
+ columnUnitLength(): number | undefined;
+ toLeft?: Doc;
+ toRight?: Doc;
+}
+
+enum ResizeMode {
+ Global = "blue",
+ Pinned = "red",
+ Undefined = "black"
+}
+
+const resizerOpacity = 1;
+
+@observer
+export default class ResizeBar extends React.Component<ResizerProps> {
+ @observable private isHoverActive = false;
+ @observable private isResizingActive = false;
+ @observable private resizeMode = ResizeMode.Undefined;
+
+ @action
+ private registerResizing = (e: React.PointerEvent<HTMLDivElement>, mode: ResizeMode) => {
+ e.stopPropagation();
+ e.preventDefault();
+ this.resizeMode = mode;
+ window.removeEventListener("pointermove", this.onPointerMove);
+ window.removeEventListener("pointerup", this.onPointerUp);
+ window.addEventListener("pointermove", this.onPointerMove);
+ window.addEventListener("pointerup", this.onPointerUp);
+ this.isResizingActive = true;
+ }
+
+ private onPointerMove = ({ movementX }: PointerEvent) => {
+ const { toLeft, toRight, columnUnitLength } = this.props;
+ const movingRight = movementX > 0;
+ const toNarrow = movingRight ? toRight : toLeft;
+ const toWiden = movingRight ? toLeft : toRight;
+ const unitLength = columnUnitLength();
+ if (unitLength) {
+ if (toNarrow) {
+ const { widthUnit, widthMagnitude } = toNarrow;
+ const scale = widthUnit === WidthUnit.Ratio ? unitLength : 1;
+ toNarrow.widthMagnitude = NumCast(widthMagnitude) - Math.abs(movementX) / scale;
+ }
+ if (this.resizeMode === ResizeMode.Pinned && toWiden) {
+ const { widthUnit, widthMagnitude } = toWiden;
+ const scale = widthUnit === WidthUnit.Ratio ? unitLength : 1;
+ toWiden.widthMagnitude = NumCast(widthMagnitude) + Math.abs(movementX) / scale;
+ }
+ }
+ }
+
+ private get isActivated() {
+ const { toLeft, toRight } = this.props;
+ if (toLeft && toRight) {
+ if (StrCast(toLeft.widthUnit) === WidthUnit.Pixel && StrCast(toRight.widthUnit) === WidthUnit.Pixel) {
+ return false;
+ }
+ return true;
+ } else if (toLeft) {
+ if (StrCast(toLeft.widthUnit) === WidthUnit.Pixel) {
+ return false;
+ }
+ return true;
+ } else if (toRight) {
+ if (StrCast(toRight.widthUnit) === WidthUnit.Pixel) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @action
+ private onPointerUp = () => {
+ this.resizeMode = ResizeMode.Undefined;
+ this.isResizingActive = false;
+ this.isHoverActive = false;
+ window.removeEventListener("pointermove", this.onPointerMove);
+ window.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ render() {
+ return (
+ <div
+ className={"resizer"}
+ style={{
+ width: this.props.width,
+ opacity: this.isActivated && this.isHoverActive ? resizerOpacity : 0
+ }}
+ onPointerEnter={action(() => this.isHoverActive = true)}
+ onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
+ >
+ <div
+ className={"internal"}
+ onPointerDown={e => this.registerResizing(e, ResizeMode.Pinned)}
+ style={{ backgroundColor: this.resizeMode }}
+ />
+ <div
+ className={"internal"}
+ onPointerDown={e => this.registerResizing(e, ResizeMode.Global)}
+ style={{ backgroundColor: this.resizeMode }}
+ />
+ </div>
+ );
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
new file mode 100644
index 000000000..b394fed62
--- /dev/null
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
@@ -0,0 +1,56 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { computed } from "mobx";
+import { Doc } from "../../../../new_fields/Doc";
+import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
+import { EditableView } from "../../EditableView";
+import { WidthUnit } from "./CollectionMulticolumnView";
+
+interface WidthLabelProps {
+ layout: Doc;
+ collectionDoc: Doc;
+ decimals?: number;
+}
+
+@observer
+export default class WidthLabel extends React.Component<WidthLabelProps> {
+
+ @computed
+ private get contents() {
+ const { layout, decimals } = this.props;
+ const getUnit = () => StrCast(layout.widthUnit);
+ const getMagnitude = () => String(+NumCast(layout.widthMagnitude).toFixed(decimals ?? 3));
+ return (
+ <div className={"label-wrapper"}>
+ <EditableView
+ GetValue={getMagnitude}
+ SetValue={value => {
+ const converted = Number(value);
+ if (!isNaN(converted) && converted > 0) {
+ layout.widthMagnitude = converted;
+ return true;
+ }
+ return false;
+ }}
+ contents={getMagnitude()}
+ />
+ <EditableView
+ GetValue={getUnit}
+ SetValue={value => {
+ if (Object.values(WidthUnit).includes(value)) {
+ layout.widthUnit = value;
+ return true;
+ }
+ return false;
+ }}
+ contents={getUnit()}
+ />
+ </div>
+ );
+ }
+
+ render() {
+ return BoolCast(this.props.collectionDoc.showWidthLabels) ? this.contents : (null);
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a833afe4e..c35a44860 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,7 @@ import { GestureUtils } from '../../../pen-gestures/GestureUtils';
import { RadialMenu } from './RadialMenu';
import { RadialMenuProps } from './RadialMenuItem';
+import { CollectionStackingView } from '../collections/CollectionStackingView';
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -644,6 +645,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
+ setNarrativeView = (custom: boolean): void => {
+ if (custom) {
+ this.props.Document.layout_narrative = CollectionView.LayoutString("narrative");
+ this.props.Document.nativeWidth = this.props.Document.nativeHeight = undefined;
+ !this.props.Document.narrative && (Doc.GetProto(this.props.Document).narrative = new List<Doc>([]));
+ this.props.Document.viewType = CollectionViewType.Stacking;
+ this.props.Document.layoutKey = "layout_narrative";
+ } else {
+ DocumentView.makeNativeViewClicked(this.props.Document);
+ }
+ }
+
+ @undoBatch
+ @action
setCustomView = (custom: boolean): void => {
if (this.props.ContainingCollectionView?.props.DataDoc || this.props.ContainingCollectionView?.props.Document.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.Document);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c56fde186..6e6ee1712 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -54,7 +54,7 @@ export interface FieldViewProps {
@observer
export class FieldView extends React.Component<FieldViewProps> {
public static LayoutString(fieldType: { name: string }, fieldStr: string) {
- return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"dada} />"
+ return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
}
@computed
diff --git a/src/scraping/buxton/node_scraper.ts b/src/scraping/buxton/node_scraper.ts
new file mode 100644
index 000000000..ef1d989d4
--- /dev/null
+++ b/src/scraping/buxton/node_scraper.ts
@@ -0,0 +1,57 @@
+import { readdirSync } from "fs";
+import { resolve } from "path";
+
+const StreamZip = require('node-stream-zip');
+
+export async function open(path: string) {
+ const zip = new StreamZip({
+ file: path,
+ storeEntries: true
+ });
+ return new Promise<string>((resolve, reject) => {
+ zip.on('ready', () => {
+ console.log("READY!", zip.entriesCount);
+ for (const entry of Object.values(zip.entries()) as any[]) {
+ const desc = entry.isDirectory ? 'directory' : `${entry.size} bytes`;
+ console.log(`Entry ${entry.name}: ${desc}`);
+ }
+ let body = "";
+ zip.stream("word/document.xml", (error: any, stream: any) => {
+ if (error) {
+ reject(error);
+ }
+ stream.on('data', (chunk: any) => body += chunk.toString());
+ stream.on('end', () => {
+ resolve(body);
+ zip.close();
+ });
+ });
+ });
+ });
+}
+
+export async function extract(path: string) {
+ const contents = await open(path);
+ let body = "";
+ const components = contents.toString().split('<w:t');
+ for (const component of components) {
+ const tags = component.split('>');
+ console.log(tags[1]);
+ const content = tags[1].replace(/<.*$/, "");
+ body += content;
+ }
+ return body;
+}
+
+async function parse(): Promise<string[]> {
+ const sourceDirectory = resolve(`${__dirname}/source`);
+ const candidates = readdirSync(sourceDirectory).filter(file => file.endsWith(".doc") || file.endsWith(".docx")).map(file => `${sourceDirectory}/${file}`);
+ await extract(candidates[0]);
+ try {
+ return Promise.all(candidates.map(extract));
+ } catch {
+ return [];
+ }
+}
+
+parse(); \ No newline at end of file
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index a9256073b..2d1a5ca32 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -13,11 +13,12 @@ import math
import sys
source = "./source"
-dist = "../../server/public/files"
+filesPath = "../../server/public/files"
+image_dist = filesPath + "/images/buxton"
db = MongoClient("localhost", 27017)["Dash"]
target_collection = db.newDocuments
-target_doc_title = "Workspace 1"
+target_doc_title = "Collection 1"
schema_guids = []
common_proto_id = ""
@@ -70,7 +71,7 @@ def text_doc_map(string_list):
return listify(proxify_guids(list(map(guid_map, string_list))))
-def write_collection(parse_results, display_fields, storage_key, viewType=2):
+def write_collection(parse_results, display_fields, storage_key, viewType):
view_guids = parse_results["child_guids"]
data_doc = parse_results["schema"]
@@ -99,13 +100,18 @@ def write_collection(parse_results, display_fields, storage_key, viewType=2):
fields[storage_key] = listify(proxify_guids(view_guids))
fields["schemaColumns"] = listify(display_fields)
fields["backgroundColor"] = "white"
- fields["scale"] = 0.5
fields["viewType"] = 2
fields["author"] = "Bill Buxton"
+ fields["disableLOD"] = True
fields["creationDate"] = {
"date": datetime.datetime.utcnow().microsecond,
"__type": "date"
}
+ if "image_urls" in parse_results:
+ fields["hero"] = {
+ "url": parse_results["image_urls"][0],
+ "__type": "image"
+ }
fields["isPrototype"] = True
fields["page"] = -1
@@ -167,14 +173,17 @@ def write_text_doc(content):
def write_image(folder, name):
- path = f"http://localhost:1050/files/{folder}/{name}"
+ path = f"http://localhost:1050/files/images/buxton/{folder}/{name}"
data_doc_guid = guid()
view_doc_guid = guid()
- image = Image.open(f"{dist}/{folder}/{name}")
+ image = Image.open(f"{image_dist}/{folder}/{name}")
native_width, native_height = image.size
+ if abs(native_width - native_height) < 10:
+ return None
+
view_doc = {
"_id": view_doc_guid,
"fields": {
@@ -213,7 +222,10 @@ def write_image(folder, name):
target_collection.insert_one(view_doc)
target_collection.insert_one(data_doc)
- return view_doc_guid
+ return {
+ "layout_id": view_doc_guid,
+ "url": path
+ }
def parse_document(file_name: str):
@@ -222,20 +234,26 @@ def parse_document(file_name: str):
result = {}
- dir_path = dist + "/" + pure_name
+ dir_path = image_dist + "/" + pure_name
+ print(dir_path)
mkdir_if_absent(dir_path)
raw = str(docx2txt.process(source + "/" + file_name, dir_path))
+ urls = []
view_guids = []
count = 0
for image in os.listdir(dir_path):
- count += 1
- view_guids.append(write_image(pure_name, image))
- copyfile(dir_path + "/" + image, dir_path +
- "/" + image.replace(".", "_o.", 1))
- copyfile(dir_path + "/" + image, dir_path +
- "/" + image.replace(".", "_m.", 1))
+ created = write_image(pure_name, image)
+ if created != None:
+ urls.append(created["url"])
+ view_guids.append(created["layout_id"])
+ count += 1
+ resolved = dir_path + "/" + image
+ original = dir_path + "/" + image.replace(".", "_o.", 1)
+ medium = dir_path + "/" + image.replace(".", "_m.", 1)
+ copyfile(resolved, original)
+ copyfile(resolved, medium)
print(f"extracted {count} images...")
def sanitize(line): return re.sub("[\n\t]+", "", line).replace(u"\u00A0", " ").replace(
@@ -342,7 +360,8 @@ def parse_document(file_name: str):
"fields": result,
"__type": "Doc"
},
- "child_guids": view_guids
+ "child_guids": view_guids,
+ "image_urls": urls
}
@@ -356,21 +375,19 @@ def write_common_proto():
"_id": id,
"fields": {
"proto": protofy("collectionProto"),
- "title": "Common Import Proto",
+ "title": "The Buxton Collection",
},
"__type": "Doc"
}
-
target_collection.insert_one(common_proto)
-
return id
-if os.path.exists(dist):
- shutil.rmtree(dist)
-while os.path.exists(dist):
+if os.path.exists(image_dist):
+ shutil.rmtree(image_dist)
+while os.path.exists(image_dist):
pass
-os.mkdir(dist)
+os.mkdir(image_dist)
mkdir_if_absent(source)
common_proto_id = write_common_proto()
@@ -380,7 +397,7 @@ for file_name in os.listdir(source):
if file_name.endswith('.docx'):
candidates += 1
schema_guids.append(write_collection(
- parse_document(file_name), ["title", "data"], "image_data"))
+ parse_document(file_name), ["title", "data"], "data", 5))
print("writing parent schema...")
parent_guid = write_collection({
@@ -390,7 +407,7 @@ parent_guid = write_collection({
"__type": "Doc"
},
"child_guids": schema_guids
-}, ["title", "short_description", "original_price"], "data", 1)
+}, ["title", "short_description", "original_price"], "data", 4)
print("appending parent schema to main workspace...\n")
target_collection.update_one(
@@ -400,7 +417,7 @@ target_collection.update_one(
print("rewriting .gitignore...\n")
lines = ['*', '!.gitignore']
-with open(dist + "/.gitignore", 'w') as f:
+with open(filesPath + "/.gitignore", 'w') as f:
f.write('\n'.join(lines))
suffix = "" if candidates == 1 else "s"
diff --git a/src/scraping/buxton/source/Bill_Notes_NB75D.docx b/src/scraping/buxton/source/Bill_Notes_NB75D.docx
new file mode 100644
index 000000000..a5a5e3d90
--- /dev/null
+++ b/src/scraping/buxton/source/Bill_Notes_NB75D.docx
Binary files differ