-
Notifications
You must be signed in to change notification settings - Fork 2
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
base: develop
Are you sure you want to change the base?
Problem History #39
Conversation
I added cookies to persist the history over sessions. I hope that this is a good way to do it.
|
This reverts commit 59786da.
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
@schweikart I replaced the cookie approach with localstorage and removed the additional dependency |
989a5d2
to
8785d74
Compare
While testing it, clicking on a previous state threw the following error to the console
Can you fix this? (Related conversation: #39 (comment)) |
4e254b8
to
f01f3e7
Compare
Seems like I messed something up while rebasing. I reset the branch to the state before my rebase attempt |
f01f3e7
to
4e254b8
Compare
There was a problem hiding this 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:
- Fetching previous states doesn't work (leading to a preflight CORS error on my machine)
- The
historyStateName
identifier leads to incomplete state restoration for the feature model anomaly solver. - 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. - 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?
src/components/solvers/History.tsx
Outdated
setContent: (t: T) => void; | ||
loadSolution: (id: ProblemTypeSolutionId) => void; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea!
*/ | ||
content: T; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
<History | ||
problemStates={problemStates} | ||
loadSolution={loadSolution} | ||
setContent={props.setProblemInput} | ||
/> |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
} | ||
|
||
export async function fetchSolution(problemType: string, solutionId: number) { | ||
return fetch(`${baseUrl()}/solution/${problemType}?id=${solutionId}`, { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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}`; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// 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); | ||
} | ||
} |
There was a problem hiding this comment.
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
<GoButton clicked={startSolving} /> | ||
<GoButton clicked={getSolution} /> |
There was a problem hiding this comment.
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
export type ProblemTypeSolutionId = { | ||
[problemTypeId: string]: number; | ||
}; |
There was a problem hiding this comment.
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
@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. |
Quality Gate passedThe SonarCloud Quality Gate passed, but some issues were introduced. 1 New issue |
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.