Bugnik
← All problems

12Review Moderation Rules

C++EasyBug fixSuggested time: 60 min

Product review moderation rules (review export, word lists, queue)

Sign in to start

Before you start

You'll get more out of this problem if you're comfortable with:

Language

  • Structs, enums and header/source file pairs in C++
  • Character classification with <cctype> (isalnum, tolower)

Libraries and frameworks

  • std::set, std::map and std::vector from the STL

Build and test tools

  • Reading g++ errors that point at a different line from the real mistake
  • Running the tests and filtering them by name

Reasoning

  • Tokenising text into words and comparing them case-insensitively
  • Inclusive vs exclusive thresholds ("fewer than", "two or more")

Background

The Community Content team at a large online retailer screens every customer review before it appears on a product page. A rules service decides whether each review is published straight away, held for a human moderator, or rejected. Since the last release, abusive reviews are slipping onto product pages and perfectly good short reviews are being thrown away.

How the system works

Review export

Reviews arrive as blocks of key: value lines. A line that is exactly --- (after trimming) ends a review.

id: R1
product: P100
author: dana
rating: 5
verified: yes
body: Boils a full jug in two minutes.
---
id: R2
...
  • Keys are case-insensitive and split from the value at the first : (so the body may contain colons). Keys and values are trimmed. Unknown keys are ignored. Blank lines and lines starting with # are skipped. Empty blocks (for example two --- in a row) are skipped.
  • id, product, author, rating and body are required and must not be empty. rating is a whole number from 1 to 5. verified is optional: yes/true or no/false in any case, default no.
  • A non-blank line without :, an invalid value, a missing field or a duplicate review id is a ParseError.

Ruleset

banned: scam, counterfeit
watch: refund, broken, cheap
min_words: 4
  • banned and watch take comma-separated words. Words are trimmed and lower-cased; empty entries are ignored. Repeated banned/watch lines add to the list.
  • min_words is a whole number from 1 to 100; it defaults to 3.
  • Keys are case-insensitive. Blank and # lines are skipped. A line without : or an unknown key is a ParseError.

Words

A review's words are the runs of letters, digits and apostrophes in its body, lower-cased; every other character (spaces, punctuation, -, / ...) separates words. "Don't BUY, it's 2x!" has the words don't, buy, it's, 2x. Word lists are compared against these words, so matching is case-insensitive and whole-word only (scampi is not scam).

Moderation rules

moderate(review, rules) checks every rule, in this order, and records the reason of each rule that fires:

#RuleEffectReason
1The body contains a banned wordrejectBANNED_WORD
2The body has fewer than min_words words (exactly min_words is fine)rejectTOO_SHORT
3The body contains http://, https:// or www. (any case)holdLINK
4The author is not verified and the rating is 1 or 5holdUNVERIFIED_EXTREME
5The body contains two or more watch words (each occurrence counts, so refund refund is two)holdWATCH_WORDS

The status is REJECT if any reject rule fired, else HOLD if any hold rule fired, else PUBLISH. reasons lists every rule that fired, in the order above (empty when published).

Moderation queue

runQueue(reviews, rules) moderates reviews in input order:

  • A review whose product and author (author compared case-insensitively) match an earlier review in the same queue is rejected with the single reason DUPLICATE; no other rules are checked for it. The earlier review counts whatever its own status was.
  • Counts of published, held and rejected reviews; heldIds and rejectedIds in input order; every verdict by review id.
  • products: for each product with at least one published review, the number published and averageTenths, the mean star rating × 10 rounded half up (4.25 stars → 43, 3.666… → 37).

Project structure

include/
  util.h        ParseError, trim / toLower / split, words, number and yes/no parsing
  review.h      Review, review export parser
  rules.h       Ruleset, ruleset parser
  moderator.h   Status, Verdict, containsLink, moderate
  queue.h       ProductSummary, QueueReport, runQueue
src/            implementations of the above
tests/          unit tests (testlib.h is the test framework, runner.cpp the entry point, fixtures.h shared data)

Your task

Moderators have reported problems since the last release:

  • The project no longer builds.
  • Reviews that shout a banned word in capitals ("SCAM") are published instead of rejected.
  • Short but perfectly fine reviews such as "Works exactly as described." are rejected even though they meet the minimum length.

Find and fix the defects so that all tests pass.

  • Do not modify files in tests/.
  • Keep your changes minimal. Fix the defects; don't rewrite modules.
  • Additional hidden tests check the behaviour described in this README.