Skip to content

Commit

Permalink
Add config to disable automatic assignment assignee reassignment [SDC…
Browse files Browse the repository at this point in the history
…P-860] (superdesk#2132)

* add config to disable automatic assignment reassignment

* handle assignor_user

* add behave tests
  • Loading branch information
devketanpro authored Nov 25, 2024
1 parent dacb79d commit 6b1766f
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 27 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ Below sections include the config options that can be defined in settings.py.
* PLANNING_JSON_ASSIGNED_INFO_EXTENDED
* Defaults to `false`
* If `true`, it will add to planning JSON output additional info for coverages like assigned desk name/email and assigned user name/email.
* ASSIGNMENT_MANUAL_REASSIGNMENT_ONLY
* Default: False (preserves the current behavior where automatic user assignment occurs)
* If true, Disables automatic user assignment for coverage, ensuring that assignments are updated only through explicit manual reassignment

### Authoring Config
* PLANNING_CHECK_FOR_ASSIGNMENT_ON_PUBLISH
Expand Down
248 changes: 248 additions & 0 deletions server/features/assignments.feature
Original file line number Diff line number Diff line change
Expand Up @@ -2084,3 +2084,251 @@ Feature: Assignments
]
}
"""

@auth
@vocabularies
Scenario: Reassignment to In-Progress but User Restricted by Manual Config instead of null
When we post to "/archive"
"""
[{
"type": "text",
"headline": "test headline",
"slugline": "test slugline",
"task": {
"desk": "#desks._id#",
"stage": "#desks.incoming_stage#"
}
}]
"""
When we post to "/planning"
"""
[{
"item_class": "item class value",
"slugline": "test slugline",
"planning_date": "2016-01-02"
}]
"""
Then we get OK response
When we patch "/planning/#planning._id#"
"""
{
"coverages": [{
"planning": {
"ednote": "test coverage, I want 250 words",
"slugline": "test slugline"
},
"assigned_to": {
"desk": "#desks._id#",
"user": "#CONTEXT_USER_ID#",
"assignor_user": "#CONTEXT_USER_ID#"
},
"workflow_status": "active"
}]
}
"""
Then we get OK response
Then we store assignment id in "firstassignment" from coverage 0
When we post to "assignments/link"
"""
[{
"assignment_id": "#firstassignment#",
"item_id": "#archive._id#",
"reassign": true
}]
"""
Then we get OK response
When we get "/archive/#archive._id#"
When we get "/assignments/#firstassignment#"
Then we get OK response
Then we get existing resource
"""
{
"_id": "#firstassignment#",
"planning": {
"ednote": "test coverage, I want 250 words",
"slugline": "test slugline"
},
"assigned_to": {
"desk": "#desks._id#",
"user": "#CONTEXT_USER_ID#",
"state": "in_progress",
"assignor_user": "#CONTEXT_USER_ID#"
}
}
"""
When we post to "/desks" with "FINANCE_DESK_ID" and success
"""
[{"name": "Finance", "desk_type": "production" }]
"""
And we set config assignment manual reassignment only to True
And we post to "/archive/#archive._id#/move"
"""
[{"task": {"desk": "#desks._id#", "stage": "#desks.incoming_stage#"}}]
"""
Then we get OK response
When we get "/assignments/#firstassignment#"
Then we get OK response
Then we get existing resource
"""
{
"_id": "#firstassignment#",
"planning": {
"ednote": "test coverage, I want 250 words",
"slugline": "test slugline"
},
"assigned_to": {
"desk": "#desks._id#",
"user": "#CONTEXT_USER_ID#",
"state": "submitted",
"assignor_user": "#CONTEXT_USER_ID#"
}
}
"""

@auth
@vocabularies
@notification
Scenario: Assignee can not changed as the author of content changes based on Manual config
Given empty "assignments_history"
When we post to "/archive"
"""
[{
"type": "text",
"headline": "test headline",
"slugline": "test slugline",
"task": {
"desk": "#desks._id#",
"stage": "#desks.incoming_stage#"
}
}]
"""
When we post to "/planning"
"""
[{
"item_class": "item class value",
"slugline": "test slugline",
"planning_date": "2016-01-02"
}]
"""
Then we get OK response
When we reset notifications
When we patch "/planning/#planning._id#"
"""
{
"coverages": [{
"planning": {
"ednote": "test coverage, I want 250 words",
"slugline": "test slugline"
},
"assigned_to": {
"desk": "#desks._id#",
"user": "#CONTEXT_USER_ID#"
},
"workflow_status": "active"
}]
}
"""
Then we get OK response
Then we store coverage id in "firstcoverage" from coverage 0
Then we store assignment id in "firstassignment" from coverage 0
And we get notifications
"""
[{
"event": "assignments:created",
"extra": {
"item": "#firstassignment#",
"coverage": "#firstcoverage#",
"planning": "#planning._id#",
"assignment_state": "assigned",
"assigned_user": "#CONTEXT_USER_ID#",
"assigned_desk": "#desks._id#",
"lock_user": null,
"user": "#CONTEXT_USER_ID#",
"original_assigned_desk": null,
"original_assigned_user": null
}
}]
"""
Then we store assignment id in "firstassignment" from coverage 0
When we patch "/archive/#archive._id#"
"""
{"headline": "test headline 2"}
"""
Then we get OK response
When we reset notifications
When we post to "assignments/link"
"""
[{
"assignment_id": "#firstassignment#",
"item_id": "#archive._id#",
"reassign": true
}]
"""
Then we get OK response
And we get notifications
"""
[{
"event": "assignments:updated",
"extra": {
"item": "#firstassignment#",
"coverage": "#firstcoverage#",
"planning": "#planning._id#",
"assignment_state": "in_progress",
"assigned_user": "#CONTEXT_USER_ID#",
"assigned_desk": "#desks._id#",
"lock_user": null,
"user": "#CONTEXT_USER_ID#",
"original_assigned_desk": "#desks._id#",
"original_assigned_user": "#CONTEXT_USER_ID#"
}
}]
"""
When we get "/archive/#archive._id#"
Then we get existing resource
"""
{
"assignment_id": "#firstassignment#"
}
"""
When we get "/assignments/#firstassignment#"
Then we get OK response
Then we get existing resource
"""
{
"_id": "#firstassignment#",
"planning": {
"ednote": "test coverage, I want 250 words",
"slugline": "test slugline"
},
"assigned_to": {
"desk": "#desks._id#",
"user": "#CONTEXT_USER_ID#",
"state": "in_progress"
}
}
"""
When we set config assignment manual reassignment only to True
Then we store "old_assignee_id" with value "#CONTEXT_USER_ID#" to context
When we switch user
When we patch "/archive/#archive._id#"
"""
{"slugline": "I'm changing the user"}
"""
Then we get OK response
When we get "/assignments/#firstassignment#"
Then we get OK response
Then we get existing resource
"""
{
"_id": "#firstassignment#",
"planning": {
"ednote": "test coverage, I want 250 words",
"slugline": "test slugline"
},
"assigned_to": {
"desk": "#desks._id#",
"state": "in_progress",
"user": "#old_assignee_id#"
}
}
"""
5 changes: 5 additions & 0 deletions server/features/steps/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@ def then_set_auto_workflow(context):
context.app.config["PLANNING_AUTO_ASSIGN_TO_WORKFLOW"] = True


@when("we set config assignment manual reassignment only to True")
def then_set_assignment_manual_reassignment_only(context):
context.app.config["ASSIGNMENT_MANUAL_REASSIGNMENT_ONLY"] = True


@when("we set PLANNING_USE_XMP_FOR_PIC_ASSIGNMENTS")
def then_set_use_xmp_for_pic_assignments(context):
ABS_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
Expand Down
9 changes: 6 additions & 3 deletions server/planning/assignments/assignments.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
update_assignment_on_link_unlink,
get_notify_self_on_assignment,
planning_auto_assign_to_workflow,
get_config_assignment_manual_reassignment_only,
)
from icalendar import Calendar, Event
from flask import request, json, current_app as app
Expand Down Expand Up @@ -862,10 +863,12 @@ def _get_empty_updates_for_assignment(self, assignment):

def _set_user_for_assignment(self, assignment, assignee, assignor=None):
updates = self._get_empty_updates_for_assignment(assignment)
updates["assigned_to"]["user"] = assignee

if assignor:
updates["assigned_to"]["assignor_user"] = assignor
if not get_config_assignment_manual_reassignment_only():
updates["assigned_to"]["user"] = assignee

if assignor:
updates["assigned_to"]["assignor_user"] = assignor

return updates

Expand Down
50 changes: 26 additions & 24 deletions server/planning/assignments/assignments_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,32 @@ def on_item_updated(self, updates, original, operation=None):
self._save_history(item, diff, operation)
return

# Split an update to two actions if needed
planning_history_service = get_resource_service("planning_history")
cov_diff = {"coverage_id": original.get("coverage_item"), "assigned_to": {}}
if "priority" in diff.keys():
cov_diff["assigned_to"]["priority"] = diff.pop("priority")
self._save_history(
item,
{"priority": cov_diff["assigned_to"]["priority"]},
ASSIGNMENT_HISTORY_ACTIONS.EDIT_PRIORITY,
)
planning_history_service._save_history(
{"_id": original.get("planning_item")},
cov_diff,
ASSIGNMENT_HISTORY_ACTIONS.EDIT_PRIORITY,
)

if "assigned_to" in diff.keys():
cov_diff["assigned_to"] = diff["assigned_to"]
self._save_history(item, diff, ASSIGNMENT_HISTORY_ACTIONS.REASSIGNED)
planning_history_service._save_history(
{"_id": original.get("planning_item")},
cov_diff,
ASSIGNMENT_HISTORY_ACTIONS.REASSIGNED,
)
if diff:
# Split an update to two actions if needed
planning_history_service = get_resource_service("planning_history")
cov_diff = {"coverage_id": original.get("coverage_item"), "assigned_to": {}}

if "priority" in diff.keys():
cov_diff["assigned_to"]["priority"] = diff.pop("priority")
self._save_history(
item,
{"priority": cov_diff["assigned_to"]["priority"]},
ASSIGNMENT_HISTORY_ACTIONS.EDIT_PRIORITY,
)
planning_history_service._save_history(
{"_id": original.get("planning_item")},
cov_diff,
ASSIGNMENT_HISTORY_ACTIONS.EDIT_PRIORITY,
)

if "assigned_to" in diff.keys():
cov_diff["assigned_to"] = diff["assigned_to"]
self._save_history(item, diff, ASSIGNMENT_HISTORY_ACTIONS.REASSIGNED)
planning_history_service._save_history(
{"_id": original.get("planning_item")},
cov_diff,
ASSIGNMENT_HISTORY_ACTIONS.REASSIGNED,
)

def on_item_deleted(self, doc):
planning = {"_id": doc.get("planning_item")}
Expand Down
4 changes: 4 additions & 0 deletions server/planning/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ def get_config_planning_duplicate_retain_assignee_details(current_app=None):
return (current_app or app).config.get("PLANNING_DUPLICATE_RETAIN_ASSIGNEE_DETAILS", False)


def get_config_assignment_manual_reassignment_only(current_app=None):
return (current_app or app).config.get("ASSIGNMENT_MANUAL_REASSIGNMENT_ONLY", False)


def get_coverage_status_from_cv(qcode: str):
coverage_states = get_resource_service("vocabularies").find_one(req=None, _id="newscoveragestatus")

Expand Down
2 changes: 2 additions & 0 deletions server/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,5 @@ def env(variable, fallback_value=None):
PLANNING_DEFAULT_COVERAGE_STATUS_ON_INGEST = "ncostat:int"

PLANNING_DUPLICATE_RETAIN_ASSIGNEE_DETAILS = False

ASSIGNMENT_MANUAL_REASSIGNMENT_ONLY = False

0 comments on commit 6b1766f

Please sign in to comment.