Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions base_tier_validation/models/tier_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ class TierValidation(models.AbstractModel):
)
next_review = fields.Char(compute="_compute_next_review")
hide_reviews = fields.Boolean(compute="_compute_hide_reviews")
validation_progress = fields.Integer(
compute="_compute_validation_progress",
string="Validation progress",
help="Percentage of approved tier reviews out of the total assigned "
"for this record. 0 when no reviews exist; useful as a progressbar "
"widget on list views to spot near-done validations.",
)

@api.depends("review_ids.status")
def _compute_validation_progress(self):
approved = self._validated_states()
for rec in self:
total = len(rec.review_ids)
if not total:
rec.validation_progress = 0
continue
done = len(rec.review_ids.filtered(lambda r: r.status in approved))
rec.validation_progress = int(round(done * 100.0 / total))

@api.depends_context("uid")
@api.depends(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ export class ReviewsTable extends Component {
return records.map((record) => record.data);
}

getValidationProgress() {
// Compute live from the embedded review data so the bar updates
// without re-reading the parent record. The server-side
// validation_progress field exists for list-view contexts.
const reviews = this._getReviewData();
if (!reviews.length) return 0;
const done = reviews.filter((r) => r.status === "approved").length;
return Math.round((done * 100) / reviews.length);
}

onToggleCollapse(ev) {
const panelHeading = ev.currentTarget.closest(".panel-heading");
const collapseDiv = panelHeading.nextElementSibling.matches("div#collapse1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ ul.o_review {
display: block;
}

.o_tier_validation_progress_header {
display: flex;
align-items: center;
gap: 16px;

.panel-title {
flex: 0 0 auto;
}
}

.o_tier_validation_progress {
flex: 1 1 auto;
max-width: 320px;
height: 1rem;
margin-right: 24px; // leave room for the rotating caret arrow
}

.panel-heading a:before {
font-family: FontAwesome;
content: "\f0d7";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<t t-name="base_tier_validation.Collapse">
<div class="o_form_sheet panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-heading o_tier_validation_progress_header">
<h4 class="panel-title">
<a
class="o_info_btn"
Expand All @@ -15,6 +15,21 @@
Reviews
</a>
</h4>
<div
class="progress o_tier_validation_progress"
t-if="_getReviewData().length"
role="progressbar"
t-att-aria-valuenow="getValidationProgress()"
aria-valuemin="0"
aria-valuemax="100"
>
<div
class="progress-bar bg-success"
t-att-style="`width: ${getValidationProgress()}%;`"
>
<t t-out="getValidationProgress()" />%
</div>
</div>
</div>
<div id="collapse1" class="panel-collapse collapse active">
<div class="panel-body o_review">
Expand Down
33 changes: 33 additions & 0 deletions base_tier_validation/tests/test_tier_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,39 @@ def test_16b_review_user_count_no_model_access(self):
result = self.test_user_2.with_user(self.test_user_2).review_user_count()
self.assertEqual(result, [])

def test_validation_progress(self):
"""validation_progress is approved / total reviews * 100,
rounded to an integer."""
self.tier_def_obj.create(
{
"model_id": self.tester_model.id,
"review_type": "individual",
"reviewer_id": self.test_user_1.id,
"definition_domain": "[('test_field', '>', 1.0)]",
}
)
self.tier_def_obj.create(
{
"model_id": self.tester_model.id,
"review_type": "individual",
"reviewer_id": self.test_user_2.id,
"definition_domain": "[('test_field', '>', 1.0)]",
}
)
record = self.test_model.create({"test_field": 2.5})
# No reviews yet -> 0%.
self.assertEqual(record.validation_progress, 0)
record.with_user(self.test_user_1).request_validation()
# Two reviews assigned, none approved -> 0%.
self.assertEqual(record.validation_progress, 0)
# User 1 approves their tier.
record.with_user(self.test_user_1).validate_tier()
# One of two -> 50%.
self.assertEqual(record.validation_progress, 50)
# User 2 approves -> 100%.
record.with_user(self.test_user_2).validate_tier()
self.assertEqual(record.validation_progress, 100)

def test_17_search_records_no_validation(self):
"""Search for records that have no validation process started"""
records = self.env["tier.validation.tester"].search(
Expand Down
Loading