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

Problem History #39

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open

Problem History #39

wants to merge 24 commits into from

Conversation

Elscrux
Copy link
Member

@Elscrux Elscrux commented Nov 2, 2023

First draft of the Problem History which is integrated into the ProgressHandler.
@schweikart If you have ideas on how this feature should look on the website, let me know.

@Elscrux Elscrux requested a review from schweikart November 2, 2023 18:35
@Elscrux
Copy link
Member Author

Elscrux commented Nov 9, 2023

I added cookies to persist the history over sessions. I hope that this is a good way to do it.
Two more things I thought about:

  1. There could be a delete button for every history entry and a clear all button for the whole history.
  2. I added a way to load solutions instead of solving them another time when the same problem is inside the history. This should probably be changed to be server-side instead. Distinguishing between GET /solution and POST /solve shouldn't be a thing we do here. The server can check if something was already computed once much better than we do here. POST /solve could get a shortcut to return an existing solution to the problem you're trying to solve.

Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@Elscrux
Copy link
Member Author

Elscrux commented Nov 14, 2023

@schweikart I replaced the cookie approach with localstorage and removed the additional dependency

@schweikart schweikart force-pushed the refactor/remove-logical-expression-parsing branch from 989a5d2 to 8785d74 Compare January 22, 2024 15:23
@schweikart
Copy link
Member

schweikart commented Jan 22, 2024

While testing it, clicking on a previous state threw the following error to the console

Access to fetch at 'http://localhost:8080/solution/sat?id=22' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Can you fix this?

(Related conversation: #39 (comment))

@schweikart schweikart changed the base branch from refactor/remove-logical-expression-parsing to develop January 22, 2024 16:20
@schweikart
Copy link
Member

schweikart commented Jan 22, 2024

Rebased onto develop, as I have caused the conflicts with my formatting PR and merged the dependency PR

Seems like I messed something up while rebasing. I reset the branch to the state before my rebase attempt

Copy link
Member

@schweikart schweikart left a comment

Choose a reason for hiding this comment

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

Sorry to keep you waiting for so long...

There are a few things I'd like to draw your attention to:

  1. Fetching previous states doesn't work (leading to a preflight CORS error on my machine)
  2. The historyStateName identifier leads to incomplete state restoration for the feature model anomaly solver.
  3. Can you implement a Clear history button? I think this wouldn't be too difficult and help a lot with using and debugging the toolbox.
  4. Also, I've annotated some minor code quality issues that shouldn't be too hard to fix.

I haven't reviewed the code changes from the Dimacs parser removal, as we've already merged (another version of) them in #38. Can you remove them from this branch?

Comment on lines 15 to 16
setContent: (t: T) => void;
loadSolution: (id: ProblemTypeSolutionId) => void;
Copy link
Member

Choose a reason for hiding this comment

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

This naming is unconventional for React. I suggest using onRequestSolution for loadSolution to express the event-like character of passing data upwards. I'm unsure what to do with setContent, maybe onRequestContent or onContentChange?

Copy link
Member

Choose a reason for hiding this comment

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

maybe rather a joint event onRequestRollback(problemState) that can be handled by the containing element? Then, this element wouldn't have to deal with the data managed by another element.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea!

Comment on lines 15 to 16
*/
content: T;
Copy link
Member

Choose a reason for hiding this comment

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

To me, this content name seems a bit unintuitive. Maybe use input?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's inspired by SolveRequest's naming which has a field called requestContent - probably not the best naming either so we might end up changing that too.
In any case, I've changed this to problemInput. What do you think?

/**
* Ids of the solutions of the problem input per problem type
*/
solutionIds: ProblemTypeSolutionId;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe just use a Map here?

(or was there a problem with using maps in serialization? I can't quite remember)

Copy link
Member Author

Choose a reason for hiding this comment

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

There is a serialization issue here but not the one we had when passing a map to the server.
When storing something in the local storage, we just store a string. When deserializing the string, the type information of the map won't persist because of TypeScript limitations which causes that you can't call any functions on the object, as only the primitive object data is loaded but not the type information which are lost.

Comment on lines 155 to 159
<History
problemStates={problemStates}
loadSolution={loadSolution}
setContent={props.setProblemInput}
/>
Copy link
Member

Choose a reason for hiding this comment

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

Why is the history part of the progress handler? Shouldn't this be independent from each other? Similarly, I don't think problemState data should be managed by the progress handler

Copy link
Member Author

Choose a reason for hiding this comment

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

Iirc this was a compromise as I didn't find a good structure to be able to load solutions (which is handled by the ProgressHandler) from the History without requiring a lot of boilerplate code in all the problem masks.
I'm not sure what the best way to resolve these kinds of communication problems is in React. Do you know any patterns or techniques to do that properly there? Some event-driven thing or so?

src/components/solvers/SAT/TextArea.tsx Show resolved Hide resolved
}

export async function fetchSolution(problemType: string, solutionId: number) {
return fetch(`${baseUrl()}/solution/${problemType}?id=${solutionId}`, {
Copy link
Member

Choose a reason for hiding this comment

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

This code seems to throw a preflight CORS error when a solution is clicked

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed by this toolbox PR that I forgot to make
ProvideQ/toolbox-server#67

}

export const ProgressHandler = <T extends {}>(
props: ProgressHandlerProps<T>
) => {
const historyItemName: string = `problemStates-${props.problemTypes}`;
Copy link
Member

Choose a reason for hiding this comment

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

This identification scheme breaks for our Feature Model Anomaly solver. If I run a solve request only for the DEAD anomaly type, this state is gone after refreshing the page.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe store results separately for separate problem types?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch! I think storying them separately would be good but it also creates some new questions. One in particular, that I don't know the answer to is should clicking on a history element should change the currently selected solvers as well. As an example: You have only the dead anomaly type selected and solve problem 1. Then you select the void anomaly type as well and click on the history. Will this deselect void anomaly type again or will it just show no solution for it?

Copy link
Member Author

@Elscrux Elscrux Jan 23, 2024

Choose a reason for hiding this comment

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

image
Also would the history be displayed like this or how would it all work? (both would be a problem that both had, void only void, dead only dead)

Comment on lines 51 to 60
// Handle problem states from local storage
if (problemStates.length == 0) {
let historyItem = localStorage.getItem(historyItemName);
if (historyItem == null) return;

let storageStates = JSON.parse(historyItem);
if (storageStates.length > 0) {
setProblemStates(storageStates);
}
}
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 extract the store / restore code to an external file? useEffect should remain within a component but the code called within may go somewhere else

Comment on lines 69 to 134
<GoButton clicked={startSolving} />
<GoButton clicked={getSolution} />
Copy link
Member

Choose a reason for hiding this comment

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

Why did you rename this function? I think the getSolution name hides the fact that this function might invoke an expensive computation of a solution. Also, this getter doesn't return anything

Comment on lines 23 to 25
export type ProblemTypeSolutionId = {
[problemTypeId: string]: number;
};
Copy link
Member

Choose a reason for hiding this comment

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

The name is a bit misleading. It seems like an object of this type is an ID. Please give this a clearer name (i.e., SolutionIds) or just use a Map

@Elscrux
Copy link
Member Author

Elscrux commented Jan 23, 2024

@schweikart I've resolved most things you've brought up. The bug in feature model anomaly will need some more decisions before I can go ahead with that. Before we solve this, I won't add a clear history button in case there is a lot of refactoring needed to resolve the bug mentioned before.

Copy link

Quality Gate Passed Quality Gate passed

The SonarCloud Quality Gate passed, but some issues were introduced.

1 New issue
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants