Skip to content

Commit

Permalink
WordCheck API returning bad words with levels (#1379)
Browse files Browse the repository at this point in the history
  • Loading branch information
70ray authored Dec 8, 2024
1 parent 578abd5 commit 349b6e1
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 20 deletions.
64 changes: 64 additions & 0 deletions SETUP/tests/unittests/ApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,28 @@ protected function validate_text(string $projectid, string $text): array
return $router->route($path, []);
}

protected function wordcheck(string $projectid, string $text, $languages = [], $accepted_words = []): array
{
global $request_body;

$request_body = ["text" => $text, "languages" => $languages, "accepted_words" => $accepted_words];
$_SERVER["REQUEST_METHOD"] = "PUT";
$path = "v1/projects/$projectid/wordcheck";
$router = ApiRouter::get_router();
return $router->route($path, []);
}

protected function wordcheck_report(string $projectid, string $page_name, array $accepted_words = [])
{
global $request_body;

$_SERVER["REQUEST_METHOD"] = "PUT";
$request_body = ["accepted_words" => $accepted_words];
$path = "v1/projects/$projectid/pages/$page_name/wordcheck";
$router = ApiRouter::get_router();
return $router->route($path, []);
}

//---------------------------------------------------------------------------
// tests

Expand Down Expand Up @@ -689,6 +711,48 @@ public function test_validate_good_text()
$expected = ["invalid_chars" => []];
$this->assertEquals($expected, $response);
}

public function test_wordcheck_bad_words()
{
$project = $this->_create_available_project();
$response = $this->wordcheck($project->projectid, "Fort Snelling b[oe]uf a1l file");
$expected = [
"bad_words" => ["Snelling" => WC_WORLD, "b[oe]uf" => WC_WORLD, "a1l" => WC_SITE],
"messages" => [],
];
$this->assertEquals($expected, $response);
}

public function test_wordcheck_accept()
{
$project = $this->_create_available_project();
$response = $this->wordcheck($project->projectid, "Fort Snelling test\nfile", [], ["Snelling"]);
$expected = [
"bad_words" => [],
"messages" => [],
];
$this->assertEquals($expected, $response);
}

public function test_wordcheck_report()
{
global $pguser;

$pguser = $this->TEST_USERNAME;
$project = $this->_create_available_project();

// check out a page
$this->checkout($project->projectid, "P1.proj_avail");

// report wordcheck results
$accepted_words = ["Snelling", "b[oe]uf"];
$this->wordcheck_report($project->projectid, "001.png", $accepted_words);
[$result] = load_wordcheck_events($project->projectid);
$this->assertEquals("P1", $result[1]);
$this->assertEquals("001.png", $result[2]);
$this->assertEquals("ProjectTest_php", $result[3]);
$this->assertEquals($accepted_words, $result[4]);
}
}

// this mocks the function in index.php
Expand Down
91 changes: 91 additions & 0 deletions api/dp-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,61 @@ paths:
default:
$ref: '#/components/responses/UnexpectedError'

/projects/{projectid}/wordcheck:
put:
tags:
- project
description: Checks the given text for bad words, returns an array of bad words with their type and an array of messages
parameters:
- $ref: '#/components/parameters/projectid'
requestBody:
description: text, accepted words and languages
required: true
content:
application/json:
schema:
type: object
properties:
text:
type: string
accepted_words:
type: array
items:
type: string
languages:
type: array
items:
type: string
responses:
200:
description: wordcheck data
content:
application/json:
schema:
type: object
properties:
bad_words:
type: array
items:
type: object
additionalProperties:
type: integer
description: Keys are bad words, values are levels
messages:
type: array
items:
type: string
'400':
$ref: '#/components/responses/InvalidValue'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimitExceeded'
default:
$ref: '#/components/responses/UnexpectedError'

/projects/{projectid}/pages/{pagename}:
put:
tags:
Expand Down Expand Up @@ -1064,6 +1119,42 @@ paths:
default:
$ref: '#/components/responses/UnexpectedError'

/projects/{projectid}/pages/{pagename}/wordcheck:
put:
tags:
- project
description: Report wordcheck reults
parameters:
- $ref: '#/components/parameters/projectid'
- $ref: '#/components/parameters/pagename'
requestBody:
description: accepted words if any
required: true
content:
application/json:
schema:
type: object
properties:
accepted_words:
type: array
items:
type: string
responses:
200:
description: OK
'400':
$ref: '#/components/responses/InvalidValue'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimitExceeded'
default:
$ref: '#/components/responses/UnexpectedError'

/stats/site:
get:
tags:
Expand Down
3 changes: 2 additions & 1 deletion api/v1.inc
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ $router->add_route("GET", "v1/queues/:queueid", "api_v1_queue");
$router->add_route("GET", "v1/queues/:queueid/stats", "api_v1_queue_stats");
$router->add_route("GET", "v1/queues/:queueid/projects", "api_v1_queue_projects");


$router->add_route("PUT", "v1/projects/:projectid/checkout", "api_v1_project_checkout");
$router->add_route("PUT", "v1/projects/:projectid/validatetext", "api_v1_project_validatetext");
$router->add_route("PUT", "v1/projects/:projectid/wordcheck", "api_v1_project_wordcheck");
$router->add_route("PUT", "v1/projects/:projectid/pages/:pagename", "api_v1_project_page");
$router->add_route("GET", "v1/projects/:projectid/pages/:pagename", "api_v1_project_page");
$router->add_route("PUT", "v1/projects/:projectid/pages/:pagename/wordcheck", "api_v1_project_page_wordcheck");

$router->add_route("GET", "v1/stats/site", "api_v1_stats_site");
$router->add_route("GET", "v1/stats/site/projects/stages", "api_v1_stats_site_projects_stages");
Expand Down
82 changes: 63 additions & 19 deletions api/v1_projects.inc
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,23 @@ function api_v1_project_validatetext(string $method, array $data, array $query_p
return ["invalid_chars" => $invalid_characters];
}

function api_v1_project_wordcheck($method, $data, array $query_params)
{
$project = $data[":projectid"];
$accepted_words = receive_data_from_request_body("accepted_words") ?? [];
$languages = receive_data_from_request_body("languages") ?? [];
if (!$languages) {
$languages = $project->languages;
}

$text = receive_project_text_from_request_body();
[$bad_words, $languages, $messages] = get_bad_word_levels_for_project_text($text, $project->projectid, $languages, $accepted_words);
return [
"bad_words" => $bad_words,
"messages" => $messages,
];
}

function api_v1_project_page(string $method, array $data, array $query_params)
{
global $pguser;
Expand All @@ -818,23 +835,9 @@ function api_v1_project_page(string $method, array $data, array $query_params)
$proof_project = new ProofProject($project);

$page_state = $query_params['pagestate'] ?? null;
if (null === $page_state) {
throw new InvalidValue("No page state found in request.");
}
if (!in_array($page_state, Rounds::get_page_states())) {
throw new InvalidValue(sprintf("%s is not a valid page state", $page_state));
}
if ($page_state != $project_page->page_state) {
$err = sprintf(
_('Page "%1$s" is not in state "%2$s"; it is now in state "%3$s".'),
$project_page->page_name,
$page_state,
$project_page->page_state
);
throw new ProjectPageInconsistentStateException($err);
}

validate_page_state($project_page, $page_state);
$proof_project_page = new ProofProjectPage($proof_project, $project_page);

$page_action = $query_params['pageaction'] ?? null;
switch ($page_action) {
case null:
Expand All @@ -859,6 +862,23 @@ function api_v1_project_page(string $method, array $data, array $query_params)
}
}

function api_v1_project_page_wordcheck(string $method, array $data, array $query_params): void
{
try {
$project = $data[":projectid"];
$proof_project = new ProofProject($project);

$project_page = $data[":pagename"];
$proof_project_page = new ProofProjectPage($proof_project, $project_page);

$proof_project_page->wc_report(receive_data_from_request_body("accepted_words") ?? []);
} catch (ProjectException $exception) {
throw new NotFoundError($exception->getMessage(), $exception->getCode());
} catch (UserAccessException $exception) {
throw new ForbiddenError($exception->getMessage(), $exception->getCode());
}
}

function validate_project_state($project, $state)
{
if (null === $state) {
Expand All @@ -878,12 +898,36 @@ function validate_project_state($project, $state)
}
}

function receive_project_text_from_request_body($field): string
function validate_page_state($project_page, $page_state)
{
$request_data = api_get_request_body();
$page_text = $request_data[$field] ?? null;
if (null === $page_state) {
throw new InvalidValue("No page state found in request.");
}
if (!in_array($page_state, Rounds::get_page_states())) {
throw new InvalidValue(sprintf("%s is not a valid page state", $page_state));
}
if ($page_state != $project_page->page_state) {
$err = sprintf(
_('Page "%1$s" is not in state "%2$s"; it is now in state "%3$s".'),
$project_page->page_name,
$page_state,
$project_page->page_state
);
throw new ProjectPageInconsistentStateException($err);
}
}

function receive_project_text_from_request_body(): string
{
$page_text = receive_data_from_request_body("text");
if (null === $page_text) {
throw new InvalidValue("There is no text");
}
return $page_text;
}

function receive_data_from_request_body($field)
{
$request_data = api_get_request_body();
return $request_data[$field] ?? null;
}
21 changes: 21 additions & 0 deletions pinc/ProofProject.inc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,27 @@ class ProofProjectPage extends LPage
"saved" => $this->can_be_reverted_to_last_save(),
];
}

public function wc_report(array $accepted_words): void
{
global $pguser;

_Page_require(
$this->projectid,
$this->imagefile,
[$this->round->page_out_state, $this->round->page_temp_state],
$this->round->user_column_name,
$pguser,
'report'
);
save_wordcheck_event(
$this->projectid,
$this->round->id,
$this->imagefile,
$pguser,
$accepted_words
);
}
}

class ProofProject
Expand Down

0 comments on commit 349b6e1

Please sign in to comment.