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

Update Work Package subject using a Type defined blueprint #17516

Draft
wants to merge 5 commits into
base: dev
Choose a base branch
from

Conversation

mereghost
Copy link
Contributor

@mereghost mereghost commented Dec 20, 2024

Related WP: OP#60003

Wat?

This PR aims to make possible the definition/replacement of a WorkPackage#subject based on blueprint defined on its Type.

How?

There are a lot of moving parts here. The PatternCollection, Pattern and their ActiveRecord value types where already in place.

Now comes the PatternMapper (better name pending), that is capable of taking the blueprint from a Pattern parsing it and resolving it into a string for replacement.

To update the Work Packages, changes where necessary on the SetAttributeService, CreateService and UpdateService.

Details

PatternMapper resolves the tokens (basically fragments on double curly braces, i.e. {{token}}) using a lookup table. If the key isn't found, it tries to call the method on the work package and get its value.

Things get more complicated when we get to custom fields, as those are dynamically added to work packages and can vary from installation to installation.

But all custom field assigned values can be accessed via the custom_field_ID method, so we first check the context:

  • {{custom_field_123}} is considered part of the Work Package
  • {{parent_custom_field_123}} will try to get work_package.parent.custom_field_123
  • {{project_custom_field_123}} will try to get the project attribute 123 in the same way above.

The SetAttributesService needed to be updated so that it overrides whatever subject was there in the first place with a message saying that it will be updated later. This is important in case something fails later on.

Update and Create services do their respective updates very late in the process, to make sure all the changes that might impact the subject are already assigned.

On Create specifically we save first, just in case we need some auto-generated field like created_at.

@mereghost mereghost force-pushed the impl/generate-subjects-on-wp branch from cbf9b44 to 8636fa3 Compare December 20, 2024 16:56
@mereghost mereghost force-pushed the impl/generate-subjects-on-wp branch from a34c017 to fe14ed9 Compare December 24, 2024 09:22
Copy link
Contributor

@brunopagno brunopagno left a comment

Choose a reason for hiding this comment

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

It's looking pretty good. I can take another look after it is moved from draft.

app/models/type.rb Show resolved Hide resolved
Comment on lines +60 to +69
def stringify(value)
case value
when Date, Time, DateTime
value.strftime("%Y-%m-%d")
when NilClass
"N/A"
else
value.to_s
end
end
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm having ideas here, but we might want to build something clever to define default values for types alongside this structure here, so we only need to parse all types in one place.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah... this is very naive right now on purpose.

Comment on lines +34 to +59
MAPPING = {
accountable: ->(wp) { wp.responsible&.name },
assignee: ->(wp) { wp.assigned_to&.name },
author: ->(wp) { wp.author&.name },
category: ->(wp) { wp.category&.name },
creation_date: ->(wp) { wp.created_at },
estimated_time: ->(wp) { wp.estimated_hours },
finish_date: ->(wp) { wp.due_date },
parent: ->(wp) { wp.parent&.id },
parent_author: ->(wp) { wp.parent&.author&.name },
parent_category: ->(wp) { wp.parent&.category&.name },
parent_creation_date: ->(wp) { wp.parent&.created_at },
parent_estimated_time: ->(wp) { wp.parent&.estimated_hours },
parent_finish_date: ->(wp) { wp.parent&.due_date },
parent_priority: ->(wp) { wp.parent&.priority },
priority: ->(wp) { wp.priority },
project: ->(wp) { wp.project_id },
project_active: ->(wp) { wp.project&.active? },
project_name: ->(wp) { wp.project&.name },
project_status: ->(wp) { wp.project&.status_code },
project_parent: ->(wp) { wp.project&.parent_id },
project_public: ->(wp) { wp.project&.public? },
start_date: ->(wp) { wp.start_date },
status: ->(wp) { wp.status&.name },
type: ->(wp) { wp.type&.name }
}.freeze
Copy link
Contributor

Choose a reason for hiding this comment

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

👏

return true unless work_package.type&.replacement_patterns_defined?

if work_package.type.patterns.all_enabled[:subject]
work_package.subject = "Templated by #{work_package.type.name}"
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I follow. Is this a temporary subject? Does this get persisted?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. If there's no subject the contract will fail as it is a required field, so I fill in with something for now so that we can overwrite it later on.

It will get persisted as I might need "persisted" info (like created_at) for the pattern, but all is done within the context of the same request, so the user won't (hopefully) see it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough. Makes sense to me.

... I thought we might want to somehow tag these so that we can query any lingering/stuck cases later. But honestly Templated by is probably good enough for that.

contract_class:)
.call(attributes)
def set_templated_subject(work_package)
return true unless work_package.type&.replacement_patterns_defined?
Copy link
Contributor

Choose a reason for hiding this comment

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

We're doing this a few times. Does it make sense to add a helper in the work_package for syntax sugar?

work_package.type&.replacement_patterns_defined?
work_package.replacement_patterns_defined?

... probably no 😅

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 wondered the same thing. Ended up not doing it... for now. :P

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

Successfully merging this pull request may close these issues.

2 participants