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

Base stepper PR #689

Merged
merged 54 commits into from
Sep 9, 2024
Merged

Base stepper PR #689

merged 54 commits into from
Sep 9, 2024

Conversation

miggol
Copy link
Contributor

@miggol miggol commented Aug 28, 2024

Alright, this is ready for review. I don't consider everything done but I want to move on to other things and allow the rest of the team to continue development with a working stepper.

It's a bit of a beast with some rough edges. I encourage asking for docstrings, comments, and clarification during review.

Things that still need doing:

  • Disabled mode for stepper items, with correct classes and no link
  • Error collection, which should be an easy addition as all stepper items that point to a ModelForm view have the instantiated form available
  • Stepper bubble colour depending on errors and completeness
  • Collecting all errors in the Stepper class for presentation in ProposalSubmit
  • The Documents page, which will come with the Attachments update
  • Collapsing of non-active stepper subfolders
  • Stepper flow for non-standard Proposal types.

Thankfully, these are all smaller issues that are very doable by others now that the framework is there.

miggol added 30 commits May 6, 2024 11:22
Rather than the stepper template
Wow! Somehow this just works out and results in a clean merge
@miggol miggol requested a review from EdoStorm96 August 28, 2024 13:25
Copy link
Contributor

@EdoStorm96 EdoStorm96 left a comment

Choose a reason for hiding this comment

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

Ok, Nice job! Whew, what a beast! ;p I am starting to kindoff get it. It seems like it will be quite powerful for different apps in the future!

For my review, I had a look through everything you altered and did some minor testing. It looks like everything is working as intended, therefore, I did not really go into the minutia of how stuff works (Like SessionCheck.make_stepper_item() ...) but had a pretty good look and tried to understand the design as best I could. In general I do find it a bit complicated, but I think going over these points would improve the whole thing quite a bit!

  • TaskMixin (or somewhere) still needs a StepperContextMixin. Otherwise TaskCreate and stuff cause an error. (minor)
  • A clearer separation of classes that are meant to be inhereted and classes that are meant to be instantiated. Maybe these can live in different files? Because I find it a bit messy if they are all in checkers.py. Maybe just move stuff like "Base" checkers and stepper items to the helpers file. Maybe have comments throughout this file to seperate different types of Base classes ... And group different types together.
  • Also Docstrings for anything intended as a baseclass would be very nice!
  • Lastly, I have a bit of an issue with the Layout class. I find it a bit unclear when the Stepper does things and when the Layout does things and I feel like these could be one big Stepper class? Maybe it would be to much, but now they are also very intertwined, which is indeed a bit spaghetti-ish.

See my other comments for minor things I found.

</a>
{% if url.children %}
{% counter depth create 1 %}
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this is not doing anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It prevents a bit of ugliness caused by the empty <ul />.

image

Note the inconsistent item spacing and "tail" at the end

classes.append(
"disabled",
)
return self._concat_css_classes(
Copy link
Contributor

Choose a reason for hiding this comment

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

Might as well do the join here imho


class ProposalTypeChecker(
Checker,
BaseStepperComponent,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can´t this just inherit from Checker?

@@ -53,8 +54,12 @@ def get_back_url(self):
return reverse(next_url, args=(pk,))

def get_study(self):
# Um.... what?
Copy link
Contributor

Choose a reason for hiding this comment

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

Haha Dunno. Let's get rid and have this mixin get the StudyURLMixin as well.



#######################
# CRUD actions on Study
#######################
class StudyUpdate(AllowErrorsOnBackbuttonMixin, UpdateView):
class StudyUpdate(
StudyMixin,
Copy link
Contributor

Choose a reason for hiding this comment

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

So, thanks to this handy mixin, we can get rid of the context["proposal"] = self.object.proposal's in these views

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean doing context["proposal"] = self.get_proposal() or moving the setting of the proposal context variable into StudyMixin??

Or something else?

@@ -23,12 +23,17 @@
)
from ..models import Study, Documents
from ..utils import check_has_adults, check_necessity_required, get_study_progress
from ..mixins import StudyMixin
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is there no StepperContextMixin needed here? I'm confused ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because StudyMixin inherits from StepperContextMixin, which in turn gets imported in studies/mixins.py

@@ -90,8 +100,17 @@ def get_back_url(self):
def get_study(self):
return self.object

def get_proposal(
Copy link
Contributor

Choose a reason for hiding this comment

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

Get rid and mix in StudyMixin :)


class BaseStepperComponent:

def __init__(self, stepper, parent=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe just make this stuff part of the Checker and get rid of BaseStepperComponent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm... I like BaseStepperComponent to keep track of what is shared between items and checkers for now.



class TrajectoriesChecker(
Checker,
Copy link
Contributor

Choose a reason for hiding this comment

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

Couldn´t you just use a ModelFormChecker? And get rid of TrajectoriesItem?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, that's better. Would be needed for checking errors in the future, too. Don't know why I chose not to

)
return []

def make_stepper_item(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we bury this somewhere deep? xp

@miggol
Copy link
Contributor Author

miggol commented Sep 2, 2024

Alright thanks for the valuable feedback!

I'm going to go through the smaller stuff first, then consider the Layout class and some of the other class hierarchies, and then document everything after.

@miggol
Copy link
Contributor Author

miggol commented Sep 3, 2024

Okay, I think I've addressed everything. Ready for another review!

  • Moved base classes into stepper_helpers.py with new docstrings and comments
  • Merged the Layout and Stepper classes. Layout came into existence when I didn't yet know what direction the classes would take, but in the end it is kind of superfluous
  • renderable Mixin now lives in main/utils.py
  • StepperItems are now also renderables
  • The Stepper has a more functional and less stateful build_stepper() method
  • All the minor comments

Copy link
Contributor

@EdoStorm96 EdoStorm96 left a comment

Choose a reason for hiding this comment

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

Alright, some really good improvement! Nice ideas and good catches. I do have some more comments.

  • I am having a strange problem where clicking around in the stepper, in a new proposal or older proposal, keeps bringing me to an old proposal ... A few pk's back. Somewhere along the route, the stepper is receiving the wrong proposal object, and redirecting to it. I think this has something to do with ProposalMixin. When I get to the WMOForm and further in the form, the problem doesn´t occur anymore for me.
  • I am not a huge fan of proposal type hint and suggest we get rid of it. It seems like one more place to keep track of this. I would suggest the following:
    • During construction on major/4, the stepper would not appear until after you filled in the first form. I did not mind this design as it gives you kind off a zen start. Maybe we can bring this back? This would then, maybe also eliminate the need for the proposal_type_hint, as we could then just examine the proposal, and provide the correct layout based on that.

We can of course discuss this proposed solution :) See you tomorrow and enjoy the rest of your birthday.

The meat and potatoes of the stepper. Returns a list of top-level
StepperItems to be rendered in the template.
"""
layout = getattr(self, "layout", False)
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it not make sense to add layout as a required argument in the stepper's init?

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 don't think so, because in almost all cases the Stepper can determine its type (and thereby layout) all by itself. The createviews are the only exception I can imagine. So if the layout was an argument it definitely should not be required.

Second, the concept of a "layout" is completely internal to the stepper implementation. I don't think every View that instantiates a stepper should need to know what a layout is and when to choose which one. The createviews know what type of proposal they're creating, because they already have to, and that should be good enough.

I think this is important because there may not exist a 1:1 mapping between proposal types and layouts. A practice proposal might use the same layout as a regular proposal, but require a different checker flow because it asks for a practice reason. Because a checker can do both of those things with just the proposal type, it makes sense to me to put that in a ProposalTypeChecker which then also determines the layout.

Adding to the above, although it isn't necessarily relevant to your comment, is that I think that the state of the stepper object should only be modified by Checkers. Once an object is complicated enough to warrant this kind of crazy depth-first search operation with Checkers, I want to respect that and not also change object state in various object methods.

Otherwise, I can imagine myself stepping through the checkers for ages trying to debug something, only to find that it was in fact that one thing I decided to put in a Stepper method instead.



class renderable:

Copy link
Contributor

Choose a reason for hiding this comment

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

I like this living here :)


class StepperItem(
renderable,
):
Copy link
Contributor

Choose a reason for hiding this comment

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

Good idea to make this renderable! Streamlines the operation a lot.

continue
# Remaining empty slots are replaced by placeholders
placeholder = PlaceholderItem(
self.stepper,
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this should be self? This caused an attribute error for me.


def __init__(self, *args, **kwargs):
if not self.form_class:
raise ImproperlyConfigured("form_class must be defined")
Copy link
Contributor

Choose a reason for hiding this comment

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

You forgot to import ImproperlyConfigured

@miggol
Copy link
Contributor Author

miggol commented Sep 9, 2024

I was a little hasty pushing the last fixes.

  • Imports were still in the wrong place
  • Incorrect references
  • Super weird stuff going on caused by #4b8b130

But I think everything is fixed now. Thanks for finding the above.

I am having a strange problem where clicking around in the stepper, in a new proposal or older proposal, keeps bringing me to an old proposal ... A few pk's back. Somewhere along the route, the stepper is receiving the wrong proposal object, and redirecting to it. I think this has something to do with ProposalMixin. When I get to the WMOForm and further in the form, the problem doesn´t occur anymore for me.

I think this was caused by #4b8b130 and is now fixed, but please double check if it's still happening.

  • I am not a huge fan of proposal type hint and suggest we get rid of it. It seems like one more place to keep track of this. I would suggest the following:
    • During construction on major/4, the stepper would not appear until after you filled in the first form. I did not mind this design as it gives you kind off a zen start. Maybe we can bring this back? This would then, maybe also eliminate the need for the proposal_type_hint, as we could then just examine the proposal, and provide the correct layout based on that.

Although I posted a lengthy motivation above about why it currently is the way it is, I think removing the stepper from the createviews altogether is a totally cool idea that would save us some complexity, including the type hint. The reason I haven't done this is that it would be extra work and debugging right now (just before leaving for the UK), whereas the current implementation at least works.

And if it turns out there's something we hadn't thought about with the alternate proposal types that does require some kind of proposal type hint, well at least it's already there.

Feel free to not include the stepper in the createviews if that saves you some time, should you decide to work on the alternate proposal flows.

Copy link
Contributor

@EdoStorm96 EdoStorm96 left a comment

Choose a reason for hiding this comment

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

Nice that you found the fix! It looks good for now, I´d say :) Nice work!

@miggol miggol merged commit f68d2ca into major/4 Sep 9, 2024
3 checks passed
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