Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DYN-4034 : open file from json content #12671

Merged
merged 5 commits into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions src/DynamoCore/Models/DynamoModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1647,6 +1647,18 @@ public void ForceRun()

#region save/load

/// <summary>
/// Opens a Dynamo workspace from a Json string.
/// </summary>
/// <param name="fileContents">Json file content</param>
/// <param name="forceManualExecutionMode">Set this to true to discard
/// execution mode specified in the file and set manual mode</param>
public void OpenFileFromJson(string fileContents, bool forceManualExecutionMode = false)
{
OpenJsonFileFromPath(fileContents, "", forceManualExecutionMode);
return;
}

/// <summary>
/// Opens a Dynamo workspace from a path to a file on disk.
/// </summary>
Expand Down Expand Up @@ -1852,7 +1864,10 @@ private bool OpenJsonFile(
bool forceManualExecutionMode,
out WorkspaceModel workspace)
{
CustomNodeManager.AddUninitializedCustomNodesInPath(Path.GetDirectoryName(filePath), IsTestMode);
if (!string.IsNullOrEmpty(filePath))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this check back because Path.GetDirectoryName throws when filePath is null.

{
CustomNodeManager.AddUninitializedCustomNodesInPath(Path.GetDirectoryName(filePath), IsTestMode);
}

var currentHomeSpace = Workspaces.OfType<HomeWorkspaceModel>().FirstOrDefault();
currentHomeSpace.UndefineCBNFunctionDefinitions();
Expand All @@ -1870,7 +1885,7 @@ private bool OpenJsonFile(
CustomNodeManager,
this.LinterManager);

workspace.FileName = filePath;
workspace.FileName = string.IsNullOrEmpty(filePath) ? "" : filePath;
workspace.ScaleFactor = dynamoPreferences.ScaleFactor;

// NOTE: This is to handle the case of opening a JSON file that does not have a version string
Expand All @@ -1887,6 +1902,11 @@ private bool OpenJsonFile(

homeWorkspace.ReCompileCodeBlockNodesForFunctionDefinitions();

if (string.IsNullOrEmpty(workspace.FileName))
{
workspace.HasUnsavedChanges = true;
}

RunType runType;
if (!homeWorkspace.HasRunWithoutCrash || !Enum.TryParse(dynamoPreferences.RunType, false, out runType) || forceManualExecutionMode)
runType = RunType.Manual;
Expand Down
7 changes: 7 additions & 0 deletions src/DynamoCore/Models/DynamoModelCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ protected virtual void OpenFileImpl(OpenFileCommand command)
//ClipBoard.Clear();
}

protected virtual void OpenFileFromJsonImpl(OpenFileFromJsonCommand command)
{
string fileContents = command.FileContents;
bool forceManualMode = command.ForceManualExecutionMode;
OpenFileFromJson(fileContents, forceManualMode);
}

private void RunCancelImpl(RunCancelCommand command)
{
var model = CurrentWorkspace as HomeWorkspaceModel;
Expand Down
80 changes: 80 additions & 0 deletions src/DynamoCore/Models/RecordableCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ internal static RecordableCommand Deserialize(XmlElement element)
case "OpenFileCommand":
command = OpenFileCommand.DeserializeCore(element);
break;
case "OpenFileFromJsonCommand":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use nameof?

command = OpenFileFromJsonCommand.DeserializeCore(element);
break;
case "PausePlaybackCommand":
command = PausePlaybackCommand.DeserializeCore(element);
break;
Expand Down Expand Up @@ -546,6 +549,83 @@ internal override void TrackAnalytics()
#endregion
}

/// <summary>
/// A command to open a file from Json content.
/// </summary>
[DataContract]
public class OpenFileFromJsonCommand : RecordableCommand
{
#region Public Class Methods

/// <summary>
///
/// </summary>
/// <param name="fileContents">The Json content of a file.</param>
/// <param name="forceManualExecutionMode">Should the file be opened in manual execution mode?</param>
public OpenFileFromJsonCommand(string fileContents, bool forceManualExecutionMode = false)
{
FileContents = fileContents;
ForceManualExecutionMode = forceManualExecutionMode;
}

internal static OpenFileFromJsonCommand DeserializeCore(XmlElement element)
{
XmlElementHelper helper = new XmlElementHelper(element);
string xmlFileContents = helper.ReadString("XmlFileContents");
return new OpenFileFromJsonCommand(xmlFileContents);
}

#endregion

#region Public Command Properties

[DataMember]
internal string FileContents { get; private set; }
internal bool ForceManualExecutionMode { get; private set; }
private DynamoModel dynamoModel;

#endregion

#region Protected Overridable Methods

protected override void ExecuteCore(DynamoModel dynamoModel)
{
this.dynamoModel = dynamoModel;
dynamoModel.OpenFileFromJsonImpl(this);
}


protected override void SerializeCore(XmlElement element)
{
var helper = new XmlElementHelper(element);
helper.SetAttribute("XmlFileContents", FileContents);
}


internal override void TrackAnalytics()
{
// Log file open action and the number of nodes in the opened workspace
Dynamo.Logging.Analytics.TrackFileOperationEvent(
dynamoModel.CurrentWorkspace.Name,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use something else here since Name will be an empty string ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this match the implementation/result for disk based file open?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I see, the other uses filepath - maybe something that notes this is in memory open.

Logging.Actions.Open,
dynamoModel.CurrentWorkspace.Nodes.Count());

// If there are unresolved nodes in the opened workspace, log the node names and count
var unresolvedNodes = dynamoModel.CurrentWorkspace.Nodes.OfType<DummyNode>();
if (unresolvedNodes != null && unresolvedNodes.Any())
{
Dynamo.Logging.Analytics.TrackEvent(
Logging.Actions.Unresolved,
Logging.Categories.NodeOperations,
unresolvedNodes.Select(n => string.Format("{0}:{1}", n.LegacyAssembly, n.LegacyFullName))
.Aggregate((x, y) => string.Format("{0}, {1}", x, y)),
unresolvedNodes.Count());
}
}

#endregion
}

/// <summary>
/// A command used to execute or cancel execution.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/DynamoCore/Scheduler/DynamoSchedulerInternals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public bool HasPendingTasks
}
}
}

#endregion

#region Private Class Helper Methods
Expand Down
2 changes: 2 additions & 0 deletions src/DynamoCoreWpf/Commands/DynamoCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ void OnModelCommandCompleted(DynamoModel.RecordableCommand command)
case "CreateCustomNodeCommand":
case "AddPresetCommand":
case "ApplyPresetCommand":
case "OpenFileFromJsonCommand":
// for this commands there is no need
// to do anything after execution
break;
Expand Down Expand Up @@ -167,6 +168,7 @@ void OnModelCommandStarting(DynamoModel.RecordableCommand command)
break;

case "OpenFileCommand":
case "OpenFileFromJsonCommand":
case "RunCancelCommand":
case "ForceRunCancelCommand":
case "CreateNodeCommand":
Expand Down
135 changes: 126 additions & 9 deletions src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ public void WorkspaceActualSize(double width, double height)

private WorkspaceViewModel currentWorkspaceViewModel;
private string filePath;
private string fileContents;
/// <summary>
/// The index in the collection of workspaces of the current workspace.
/// This property is bound to the SelectedIndex property in the workspaces tab control
Expand Down Expand Up @@ -1447,13 +1448,48 @@ public FileDialog GetSaveDialog(WorkspaceModel workspace)
return fileDialog;
}

/// <summary>
/// Attempts to open a file using the Json content passed to OpenFromJsonCommand, but wraps
/// the call with a check to make sure no unsaved changes to the HomeWorkspace are lost.
/// </summary>
/// <param name="openFromJsonCommand"> <see cref="DynamoModel.OpenFileFromJsonCommand"/> </param>
private void OpenFromJsonIfSaved(object openCommand)
{
filePath = string.Empty;
fileContents = string.Empty;

var command = openCommand as DynamoModel.OpenFileFromJsonCommand;
if (command == null)
{
return;
}

if (HomeSpace != null && HomeSpace.HasUnsavedChanges)
{
if (AskUserToSaveWorkspaceOrCancel(HomeSpace))
{
fileContents = command.FileContents;
ExecuteCommand(command);
ShowStartPage = false;
}
}
else
{
OpenFromJsonCommand.Execute(new Tuple<string, bool>(command.FileContents, command.ForceManualExecutionMode));
ShowStartPage = false;
}
}

/// <summary>
/// Attempts to open a file using the passed open command, but wraps the call
/// with a check to make sure no unsaved changes to the HomeWorkspace are lost.
/// </summary>
/// <param name="openCommand"> <see cref="DynamoModel.OpenFileCommand"/> </param>
private void OpenIfSaved(object openCommand)
{
fileContents = string.Empty;
filePath = string.Empty;

var command = openCommand as DynamoModel.OpenFileCommand;
if (command == null)
{
Expand All @@ -1464,16 +1500,71 @@ private void OpenIfSaved(object openCommand)
{
if (AskUserToSaveWorkspaceOrCancel(HomeSpace))
{
this.filePath = command.FilePath;
this.ExecuteCommand(command);
this.ShowStartPage = false;
filePath = command.FilePath;
ExecuteCommand(command);
ShowStartPage = false;
}
}
else
{
this.OpenCommand.Execute(new Tuple<string,bool>(command.FilePath, command.ForceManualExecutionMode));
this.ShowStartPage = false;
OpenCommand.Execute(new Tuple<string,bool>(command.FilePath, command.ForceManualExecutionMode));
ShowStartPage = false;
}
}

/// <summary>
/// Open a definition or workspace.
/// </summary>
/// <param name="parameters"></param>
/// For most cases, parameters variable refers to the Json content file to open
/// However, when this command is used in OpenFileDialog, the variable is
/// a Tuple<string, bool> instead. The boolean flag is used to override the
/// RunSetting of the workspace.
private void OpenFromJson(object parameters)
{
// try catch for exceptions thrown while opening files, say from a future version,
// that can't be handled reliably
filePath = string.Empty;
fileContents = string.Empty;
bool forceManualMode = false;
try
{
var packedParams = parameters as Tuple<string, bool>;
if (packedParams != null)
{
fileContents = packedParams.Item1;
forceManualMode = packedParams.Item2;
}
else
{
fileContents = parameters as string;
}
ExecuteCommand(new DynamoModel.OpenFileFromJsonCommand(fileContents, forceManualMode));
}
catch (Exception e)
{
if (!DynamoModel.IsTestMode)
{
string commandString = String.Format(Resources.MessageErrorOpeningFileGeneral);
string errorMsgString;
if (e is Newtonsoft.Json.JsonReaderException)
{
errorMsgString = String.Format(Resources.MessageFailedToOpenCorruptedFile, "Json file content");
}
else
{
errorMsgString = String.Format(Resources.MessageUnkownErrorOpeningFile, "Json file content");
}
model.Logger.LogNotification("Dynamo", commandString, errorMsgString, e.ToString());
System.Windows.MessageBox.Show(errorMsgString);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to use the new themed dialog box where possible - @QilongTang @RobertGlobant20 can you point to an example of this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
else
{
throw (e);
}
return;
}
this.ShowStartPage = false; // Hide start page if there's one.
}

/// <summary>
Expand All @@ -1489,6 +1580,7 @@ private void Open(object parameters)
// try catch for exceptions thrown while opening files, say from a future version,
// that can't be handled reliably
filePath = string.Empty;
fileContents = string.Empty;
bool forceManualMode = false;
try
{
Expand Down Expand Up @@ -1541,17 +1633,42 @@ private bool CanOpen(object parameters)
return PathHelper.IsValidPath(filePath);
}

private bool CanOpenFromJson(object parameters)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about reusing this helper?

public static bool isValidJson(string path, out string fileContents, out Exception ex)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reuse the helper, I put a lot of thoughts into it.. Feel free to update as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
string fileContent = parameters as string;
if (string.IsNullOrEmpty(fileContent))
{
return false;
}

try
{
var obj = Newtonsoft.Json.Linq.JToken.Parse(fileContent);
return true;
}
catch (Exception e)
{
Dynamo.Logging.LogMessage.Error(e);
}

return false;
}

/// <summary>
/// Read the contents of the file and set the view parameters for that current workspace
/// </summary>
private void model_ComputeModelDeserialized()
{
if (filePath == String.Empty) return;
string fileContents = File.ReadAllText(filePath);
{
try
{
string fileContentsInUse = String.IsNullOrEmpty(filePath) ? fileContents : File.ReadAllText(filePath);
if (string.IsNullOrEmpty(fileContentsInUse))
{
return;
}

// This call will fail in the case of an XML file
ExtraWorkspaceViewInfo viewInfo = WorkspaceViewModel.ExtraWorkspaceViewInfoFromJson(fileContents);
ExtraWorkspaceViewInfo viewInfo = WorkspaceViewModel.ExtraWorkspaceViewInfoFromJson(fileContentsInUse);

Model.CurrentWorkspace.UpdateWithExtraWorkspaceViewInfo(viewInfo);
Model.OnWorkspaceOpening(viewInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ private void InitializeDelegateCommands()
{
OpenCommand = new DelegateCommand(Open, CanOpen);
OpenIfSavedCommand = new DelegateCommand(OpenIfSaved, CanOpen);
OpenFromJsonCommand = new DelegateCommand(OpenFromJson, CanOpenFromJson);
OpenFromJsonIfSavedCommand = new DelegateCommand(OpenFromJsonIfSaved, CanOpenFromJson);
OpenRecentCommand = new DelegateCommand(OpenRecent, CanOpenRecent);
SaveCommand = new DelegateCommand(Save, CanSave);
SaveAsCommand = new DelegateCommand(SaveAs, CanSaveAs);
Expand Down Expand Up @@ -84,6 +86,8 @@ private void InitializeDelegateCommands()
NodeFromSelectionCommand = new DelegateCommand(CreateNodeFromSelection, CanCreateNodeFromSelection);
OpenDocumentationLinkCommand = new DelegateCommand(OpenDocumentationLink);
}
public DelegateCommand OpenFromJsonIfSavedCommand { get; set; }
public DelegateCommand OpenFromJsonCommand { get; set; }
public DelegateCommand OpenIfSavedCommand { get; set; }
public DelegateCommand OpenCommand { get; set; }
public DelegateCommand ShowOpenDialogAndOpenResultCommand { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions src/DynamoUtilities/DebugModes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ private static void RegisterDebugModes()

internal static void LoadDebugModesStatusFromConfig(string configPath)
{
if (!File.Exists(configPath)) return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


try
{
XmlDocument xmlDoc;
Expand Down
Loading