Skip to content

Commit

Permalink
Label case where data is from a prior version of generator script. (#237
Browse files Browse the repository at this point in the history
)

* Label stale data from prior generator-script versions.

* Ensure exhaustive check for describing data source type. Add color.

* Remove central list of warning-sources; include color and msg logic in same switch.
  • Loading branch information
jsoules authored Nov 4, 2024
1 parent f67c9a1 commit fa1c0e9
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 14 deletions.
4 changes: 3 additions & 1 deletion gui/src/app/Project/ProjectDataModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ const isProjectBase = (x: any): x is ProjectBase => {

export enum DataSource {
GENERATED_BY_R = "generated_by_r",
GENERATED_BY_STALE_R = "generated_by_stale_r",
GENERATED_BY_PYTHON = "generated_by_python",
GENERATED_BY_STALE_PYTHON = "generated_by_stale_python",
}

type ProjectMetadata = {
Expand All @@ -104,7 +106,7 @@ export const isProjectMetaData = (x: any): x is ProjectMetadata => {
if (!baseObjectCheck(x)) return false;
if (typeof x.title !== "string") return false;
if (
x.dataSource !== undefined && // allow undefined for backwards compatibility
x.dataSource !== undefined && // undefined = manually edited or unknown provenance
!Object.values(DataSource).includes(x.dataSource)
)
return false;
Expand Down
29 changes: 27 additions & 2 deletions gui/src/app/Project/ProjectReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,12 @@ const ProjectReducer = (s: ProjectDataModel, a: ProjectReducerAction) => {
}
case "commitFile": {
const newState = { ...s };
if (a.filename === ProjectKnownFiles.DATAFILE) {
newState.meta = { ...s.meta, dataSource: undefined };
const newDataSource = confirmDataSourceForCommit(
s.meta.dataSource,
a.filename,
);
if (newDataSource !== s.meta.dataSource) {
newState.meta = { ...s.meta, dataSource: newDataSource };
}
newState[a.filename] = s.ephemera[a.filename];
return newState;
Expand Down Expand Up @@ -102,4 +106,25 @@ const ProjectReducer = (s: ProjectDataModel, a: ProjectReducerAction) => {
}
};

const confirmDataSourceForCommit = (
currentSource: DataSource | undefined,
editedFile: ProjectKnownFiles,
): DataSource | undefined => {
if (editedFile === ProjectKnownFiles.DATAFILE) return undefined;
if (
editedFile === ProjectKnownFiles.DATAPYFILE &&
currentSource === DataSource.GENERATED_BY_PYTHON
) {
return DataSource.GENERATED_BY_STALE_PYTHON;
}
if (
editedFile === ProjectKnownFiles.DATARFILE &&
currentSource === DataSource.GENERATED_BY_R
) {
return DataSource.GENERATED_BY_STALE_R;
}

return currentSource;
};

export default ProjectReducer;
2 changes: 1 addition & 1 deletion gui/src/app/Project/ProjectSerialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const loadMetaFromString = (
json: string,
clearExisting: boolean = false,
): ProjectDataModel => {
let newMeta = JSON.parse(json);
const newMeta = JSON.parse(json);
if (!isProjectMetaData(newMeta)) {
throw Error("Deserialized meta is not valid");
}
Expand Down
42 changes: 32 additions & 10 deletions gui/src/app/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useMediaQuery from "@mui/material/useMediaQuery";
import StanFileEditor from "@SpComponents/StanFileEditor";
import TabWidget from "@SpComponents/TabWidget";
import TextEditor from "@SpComponents/TextEditor";
import { ColorOptions, ToolbarItem } from "@SpComponents/ToolBar";
import { FileNames } from "@SpCore/FileMapping";
import { ProjectContext } from "@SpCore/ProjectContextProvider";
import {
Expand All @@ -16,6 +17,7 @@ import Sidebar, { drawerWidth } from "@SpPages/Sidebar";
import TopBar from "@SpPages/TopBar";
import DataPyWindow from "@SpScripting/DataGeneration/DataPyWindow";
import DataRWindow from "@SpScripting/DataGeneration/DataRWindow";
import { unreachable } from "@SpUtil/unreachable";
import {
FunctionComponent,
useCallback,
Expand All @@ -26,7 +28,6 @@ import {
useState,
} from "react";
import SamplingWindow from "./SamplingWindow/SamplingWindow";
import { ToolbarItem } from "@SpComponents/ToolBar";

type Props = {
//
Expand Down Expand Up @@ -124,30 +125,51 @@ const LeftView: FunctionComponent<LeftViewProps> = () => {
);
};

const DataEditor: FunctionComponent<{}> = () => {
const DataEditor: FunctionComponent<unknown> = () => {
const { data, update } = useContext(ProjectContext);

const dataIsEdited = useMemo(() => {
return data.dataFileContent !== data.ephemera.dataFileContent;
}, [data.dataFileContent, data.ephemera.dataFileContent]);

const dataSourceDesc: undefined | { msg: string; color: ColorOptions } =
useMemo(() => {
if (dataIsEdited) return undefined;

switch (data.meta.dataSource) {
case undefined: {
return undefined;
}
case DataSource.GENERATED_BY_PYTHON: {
return { msg: "data.py", color: "info.main" };
}
case DataSource.GENERATED_BY_R: {
return { msg: "data.R", color: "info.main" };
}
case DataSource.GENERATED_BY_STALE_PYTHON: {
return { msg: "a prior version of data.py.", color: "warning.main" };
}
case DataSource.GENERATED_BY_STALE_R: {
return { msg: "a prior version of data.R.", color: "warning.main" };
}
default:
return unreachable(data.meta.dataSource);
}
}, [dataIsEdited, data.meta.dataSource]);

const dataMessage: ToolbarItem[] = useMemo(() => {
if (data.meta.dataSource === undefined || dataIsEdited) {
if (dataSourceDesc === undefined) {
return [];
} else {
return [
{
type: "text",
label:
"Data is generated by data." +
(data.meta.dataSource === DataSource.GENERATED_BY_PYTHON
? "py"
: "R"),
color: "info.main",
label: `Data generated by ${dataSourceDesc.msg}`,
color: dataSourceDesc.color,
},
];
}
}, [data.meta.dataSource, dataIsEdited]);
}, [dataSourceDesc]);

const onSetEditedText = useCallback(
(content: string) => {
Expand Down
55 changes: 55 additions & 0 deletions gui/test/app/Project/ProjectReducer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ describe("Project reducer", () => {
expect(result[ProjectKnownFiles.DATAFILE]).toEqual(
result.ephemera[ProjectKnownFiles.DATAFILE],
);
});
test("Saving data.json clears data source", () => {
expect(initialState[ProjectKnownFiles.DATAFILE]).not.toEqual(
initialState.ephemera[ProjectKnownFiles.DATAFILE],
);
const result = ProjectReducer(initialState, commitAction);
expect(result.meta.dataSource).toBeUndefined();
});
test("Save action does not save non-chosen files", () => {
Expand All @@ -157,6 +163,55 @@ describe("Project reducer", () => {
const result = ProjectReducer(initialState, commitAction);
expect(result.ephemera).toEqual(initialState.ephemera);
});
test("Saving data generation script updates status on data it generated", () => {
const pairs = [
{
source: DataSource.GENERATED_BY_PYTHON,
newSource: DataSource.GENERATED_BY_STALE_PYTHON,
file: ProjectKnownFiles.DATAPYFILE,
},
{
source: DataSource.GENERATED_BY_R,
newSource: DataSource.GENERATED_BY_STALE_R,
file: ProjectKnownFiles.DATARFILE,
},
];
pairs.forEach((p) => {
const initial = {
...initialState,
meta: { dataSource: p.source },
} as any as ProjectDataModel;
const commit = { ...commitAction, filename: p.file };
const result = ProjectReducer(initial, commit);
expect(result.meta.dataSource).toEqual(p.newSource);
});
});
test("Saving data generation script does not change status for data.json it didn't generate", () => {
const pairs = [
{
source: DataSource.GENERATED_BY_PYTHON,
file: ProjectKnownFiles.DATAPYFILE,
},
{
source: DataSource.GENERATED_BY_R,
file: ProjectKnownFiles.DATARFILE,
},
];
const sources = Object.entries(DataSource);
pairs.forEach((p) => {
sources
.filter(([, value]) => value !== p.source)
.forEach((s) => {
const initial = {
...initialState,
meta: { dataSource: s },
} as any as ProjectDataModel;
const commit = { ...commitAction, filename: p.file };
const result = ProjectReducer(initial, commit);
expect(result.meta.dataSource).toEqual(s);
});
});
});
});

describe("Updating sampling options", () => {
Expand Down

0 comments on commit fa1c0e9

Please sign in to comment.