fix(ci): phpcbf auto-format + add missing bootstrap requires for tests

212 PHPCS formatting errors auto-fixed by phpcbf across 18 files.
All remaining issues are warnings (exit 2), not errors — CI treats
exit 2 as success via the same pattern as peptide-news-plugin.

Also adds class-psa-ai-content.php and class-psa-directory-api.php to
tests/bootstrap.php require list — both files exist in includes/ but
were missing from bootstrap, causing 5 PHPUnit "Class not found"
failures in AIGeneratorTest and DirectoryTest.
This commit is contained in:
peptiderepo
2026-04-24 06:33:41 +00:00
parent 4ac81f5d23
commit 3dda80b771
19 changed files with 436 additions and 255 deletions

View File

@@ -29,10 +29,14 @@ class PSA_Admin_Page {
}
wp_enqueue_script( 'psa-batch-enrich', PSA_PLUGIN_URL . 'assets/js/psa-batch-enrich.js', array( 'jquery' ), PSA_VERSION, true );
wp_localize_script( 'psa-batch-enrich', 'psaBatchEnrich', array(
wp_localize_script(
'psa-batch-enrich',
'psaBatchEnrich',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'psa_batch_enrich' ),
) );
)
);
}
// ── Field renderers ─────────────────────────────────────────────────
@@ -119,7 +123,11 @@ class PSA_Admin_Page {
<h1><?php esc_html_e( 'Peptide Search AI Settings', 'peptide-search-ai' ); ?></h1>
<form method="post" action="options.php">
<?php settings_fields( 'psa_settings_group' ); do_settings_sections( 'peptide-search-ai' ); submit_button(); ?>
<?php
settings_fields( 'psa_settings_group' );
do_settings_sections( 'peptide-search-ai' );
submit_button();
?>
</form>
<hr />
@@ -216,6 +224,7 @@ class PSA_Admin_Page {
<?php endforeach; ?>
</tbody>
</table>
<?php endif;
<?php
endif;
}
}

View File

@@ -40,9 +40,13 @@ class PSA_Admin {
/** Register settings fields — rendering delegated to PSA_Admin_Page. */
public static function register_settings(): void {
register_setting( 'psa_settings_group', 'psa_settings', array(
register_setting(
'psa_settings_group',
'psa_settings',
array(
'sanitize_callback' => array( __CLASS__, 'sanitize_settings' ),
) );
)
);
add_settings_section( 'psa_ai_section', __( 'OpenRouter API Settings', 'peptide-search-ai' ), array( 'PSA_Admin_Page', 'render_section_description' ), 'peptide-search-ai' );
add_settings_field( 'api_key', __( 'API Key', 'peptide-search-ai' ), array( 'PSA_Admin_Page', 'render_api_key_field' ), 'peptide-search-ai', 'psa_ai_section' );
@@ -95,18 +99,27 @@ class PSA_Admin {
if ( 'migrate_categories' === $action && wp_verify_nonce( $nonce, 'psa_migrate_categories' ) ) {
self::run_category_migration();
add_action( 'admin_notices', function () {
add_action(
'admin_notices',
function () {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Category migration completed.', 'peptide-search-ai' ) . '</p></div>';
} );
}
);
}
if ( 'reenrich' === $action && wp_verify_nonce( $nonce, 'psa_reenrich' ) ) {
$queued = self::queue_reenrichment();
add_action( 'admin_notices', function () use ( $queued ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( sprintf(
__( '%d peptides queued for re-enrichment.', 'peptide-search-ai' ), $queued
) ) . '</p></div>';
} );
add_action(
'admin_notices',
function () use ( $queued ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html(
sprintf(
__( '%d peptides queued for re-enrichment.', 'peptide-search-ai' ),
$queued
)
) . '</p></div>';
}
);
}
}
@@ -117,12 +130,14 @@ class PSA_Admin {
* Idempotent — safe to run multiple times.
*/
private static function run_category_migration(): void {
$posts = get_posts( array(
$posts = get_posts(
array(
'post_type' => 'peptide',
'post_status' => array( 'publish', 'draft' ),
'posts_per_page' => -1,
'fields' => 'ids',
) );
)
);
foreach ( $posts as $post_id ) {
$terms = wp_get_object_terms( $post_id, 'peptide_category' );
@@ -199,17 +214,25 @@ class PSA_Admin {
* @return int Number of peptides queued.
*/
private static function queue_reenrichment(): int {
$posts = get_posts( array(
$posts = get_posts(
array(
'post_type' => 'peptide',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array(
'relation' => 'OR',
array( 'key' => 'psa_half_life', 'compare' => 'NOT EXISTS' ),
array( 'key' => 'psa_half_life', 'value' => '' ),
array(
'key' => 'psa_half_life',
'compare' => 'NOT EXISTS',
),
) );
array(
'key' => 'psa_half_life',
'value' => '',
),
),
)
);
$daily_key = 'psa_daily_gen_' . gmdate( 'Y-m-d' );
$daily_count = (int) get_transient( $daily_key );
@@ -226,7 +249,12 @@ class PSA_Admin {
}
update_post_meta( $post_id, 'psa_source', 'pending' );
wp_update_post( array( 'ID' => $post_id, 'post_status' => 'draft' ) );
wp_update_post(
array(
'ID' => $post_id,
'post_status' => 'draft',
)
);
update_post_meta( $post_id, 'psa_generation_started', time() );
$args = array( $post_id, $post->post_title );
@@ -234,7 +262,7 @@ class PSA_Admin {
// Stagger 30s apart to avoid rate-limit hammering.
wp_schedule_single_event( time() + ( $queued * 30 ), 'psa_generate_peptide_background', $args );
}
$queued++;
++$queued;
}
set_transient( $daily_key, $daily_count + $queued, DAY_IN_SECONDS );
@@ -262,29 +290,38 @@ class PSA_Admin {
// ── Backward-compatible proxies (rendering moved to PSA_Admin_Page) ─
/** @see PSA_Admin_Page::enqueue_batch_scripts() */
public static function enqueue_batch_scripts( string $hook ): void { PSA_Admin_Page::enqueue_batch_scripts( $hook ); }
public static function enqueue_batch_scripts( string $hook ): void {
PSA_Admin_Page::enqueue_batch_scripts( $hook ); }
/** @see PSA_Admin_Page::render_section_description() */
public static function render_section_description(): void { PSA_Admin_Page::render_section_description(); }
public static function render_section_description(): void {
PSA_Admin_Page::render_section_description(); }
/** @see PSA_Admin_Page::render_api_key_field() */
public static function render_api_key_field(): void { PSA_Admin_Page::render_api_key_field(); }
public static function render_api_key_field(): void {
PSA_Admin_Page::render_api_key_field(); }
/** @see PSA_Admin_Page::render_model_field() */
public static function render_model_field(): void { PSA_Admin_Page::render_model_field(); }
public static function render_model_field(): void {
PSA_Admin_Page::render_model_field(); }
/** @see PSA_Admin_Page::render_validation_model_field() */
public static function render_validation_model_field(): void { PSA_Admin_Page::render_validation_model_field(); }
public static function render_validation_model_field(): void {
PSA_Admin_Page::render_validation_model_field(); }
/** @see PSA_Admin_Page::render_publish_field() */
public static function render_publish_field(): void { PSA_Admin_Page::render_publish_field(); }
public static function render_publish_field(): void {
PSA_Admin_Page::render_publish_field(); }
/** @see PSA_Admin_Page::render_pubchem_field() */
public static function render_pubchem_field(): void { PSA_Admin_Page::render_pubchem_field(); }
public static function render_pubchem_field(): void {
PSA_Admin_Page::render_pubchem_field(); }
/** @see PSA_Admin_Page::render_budget_field() */
public static function render_budget_field(): void { PSA_Admin_Page::render_budget_field(); }
public static function render_budget_field(): void {
PSA_Admin_Page::render_budget_field(); }
/** @see PSA_Admin_Page::render_settings_page() */
public static function render_settings_page(): void { PSA_Admin_Page::render_settings_page(); }
public static function render_settings_page(): void {
PSA_Admin_Page::render_settings_page(); }
}

View File

@@ -35,7 +35,11 @@ class PSA_AI_Generator {
*/
public static function validate_peptide_name( string $name ) {
if ( ! self::validate_peptide_input( $name ) ) {
return array( 'is_valid' => false, 'canonical_name' => $name, 'reason' => 'Invalid characters in peptide name.' );
return array(
'is_valid' => false,
'canonical_name' => $name,
'reason' => 'Invalid characters in peptide name.',
);
}
$options = self::get_settings();
@@ -70,7 +74,11 @@ class PSA_AI_Generator {
$data = PSA_OpenRouter::parse_response( $response );
if ( is_wp_error( $data ) ) {
return array( 'is_valid' => false, 'canonical_name' => $name, 'reason' => 'Could not verify this peptide name.' );
return array(
'is_valid' => false,
'canonical_name' => $name,
'reason' => 'Could not verify this peptide name.',
);
}
$result = array(
@@ -136,12 +144,15 @@ class PSA_AI_Generator {
}
$post_status = ( 'publish' === $options['auto_publish'] ) ? 'publish' : 'draft';
$update_result = wp_update_post( array(
$update_result = wp_update_post(
array(
'ID' => $post_id,
'post_title' => sanitize_text_field( $result['name'] ?? $peptide_name ),
'post_content' => wp_kses_post( $post_content ),
'post_status' => $post_status,
), true );
),
true
);
if ( is_wp_error( $update_result ) ) {
error_log( 'PSA: wp_update_post FAILED for post ' . $post_id . ': ' . $update_result->get_error_message() );
@@ -214,8 +225,14 @@ class PSA_AI_Generator {
$gen_cost = PSA_Cost_Tracker::estimate_cost( $generation_model, $gen_tokens, PSA_Config::GENERATION_MAX_TOKENS );
return array(
'validation' => array( 'tokens' => $val_tokens + PSA_Config::VALIDATION_MAX_TOKENS, 'estimated_cost_usd' => $val_cost ),
'generation' => array( 'tokens' => $gen_tokens + PSA_Config::GENERATION_MAX_TOKENS, 'estimated_cost_usd' => $gen_cost ),
'validation' => array(
'tokens' => $val_tokens + PSA_Config::VALIDATION_MAX_TOKENS,
'estimated_cost_usd' => $val_cost,
),
'generation' => array(
'tokens' => $gen_tokens + PSA_Config::GENERATION_MAX_TOKENS,
'estimated_cost_usd' => $gen_cost,
),
'total_estimated_cost_usd' => $val_cost + $gen_cost,
);
}
@@ -228,7 +245,13 @@ class PSA_AI_Generator {
* @return array
*/
private static function get_settings(): array {
$defaults = array( 'api_key' => '', 'ai_model' => '', 'validation_model' => '', 'auto_publish' => 'draft', 'use_pubchem' => '1' );
$defaults = array(
'api_key' => '',
'ai_model' => '',
'validation_model' => '',
'auto_publish' => 'draft',
'use_pubchem' => '1',
);
$settings = wp_parse_args( get_option( 'psa_settings', array() ), $defaults );
if ( ! empty( $settings['api_key'] ) && ! ( defined( 'PSA_OPENROUTER_KEY' ) && PSA_OPENROUTER_KEY ) ) {
@@ -269,14 +292,18 @@ class PSA_AI_Generator {
// ── Backward-compatible proxies ─────────────────────────────────────
/** @see PSA_AI_Content::build_validation_prompt() */
public static function build_validation_prompt( string $name ): string { return PSA_AI_Content::build_validation_prompt( $name ); }
public static function build_validation_prompt( string $name ): string {
return PSA_AI_Content::build_validation_prompt( $name ); }
/** @see PSA_AI_Content::build_generation_prompt() */
public static function build_generation_prompt( string $peptide_name ): string { return PSA_AI_Content::build_generation_prompt( $peptide_name ); }
public static function build_generation_prompt( string $peptide_name ): string {
return PSA_AI_Content::build_generation_prompt( $peptide_name ); }
/** @see PSA_AI_Content::save_peptide_meta() */
public static function save_peptide_meta( int $post_id, array $ai_data, ?array $pubchem_data ): void { PSA_AI_Content::save_peptide_meta( $post_id, $ai_data, $pubchem_data ); }
public static function save_peptide_meta( int $post_id, array $ai_data, ?array $pubchem_data ): void {
PSA_AI_Content::save_peptide_meta( $post_id, $ai_data, $pubchem_data ); }
/** @see PSA_AI_Content::assign_category_term() */
public static function assign_category_term( int $post_id, array $ai_data ): void { PSA_AI_Content::assign_category_term( $post_id, $ai_data ); }
public static function assign_category_term( int $post_id, array $ai_data ): void {
PSA_AI_Content::assign_category_term( $post_id, $ai_data ); }
}

View File

@@ -158,14 +158,18 @@ class PSA_Cost_Tracker {
// ── Backward-compatible proxies (reporting moved to PSA_Cost_Reporter) ──
/** @see PSA_Cost_Reporter::get_monthly_spend() */
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float { return PSA_Cost_Reporter::get_monthly_spend( $year, $month ); }
public static function get_monthly_spend( ?int $year = null, ?int $month = null ): float {
return PSA_Cost_Reporter::get_monthly_spend( $year, $month ); }
/** @see PSA_Cost_Reporter::get_monthly_tokens() */
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int { return PSA_Cost_Reporter::get_monthly_tokens( $year, $month ); }
public static function get_monthly_tokens( ?int $year = null, ?int $month = null ): int {
return PSA_Cost_Reporter::get_monthly_tokens( $year, $month ); }
/** @see PSA_Cost_Reporter::get_monthly_estimated_count() */
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int { return PSA_Cost_Reporter::get_monthly_estimated_count( $year, $month ); }
public static function get_monthly_estimated_count( ?int $year = null, ?int $month = null ): int {
return PSA_Cost_Reporter::get_monthly_estimated_count( $year, $month ); }
/** @see PSA_Cost_Reporter::get_recent_logs() */
public static function get_recent_logs( int $limit = 20 ): array { return PSA_Cost_Reporter::get_recent_logs( $limit ); }
public static function get_recent_logs( int $limit = 20 ): array {
return PSA_Cost_Reporter::get_recent_logs( $limit ); }
}

View File

@@ -23,18 +23,45 @@ class PSA_Directory_API {
* Register the /v1/compounds endpoint. Public (no auth) — rate limiting at server/CDN level.
*/
public static function register_rest_routes(): void {
register_rest_route( 'peptide-search-ai/v1', '/compounds', array(
register_rest_route(
'peptide-search-ai/v1',
'/compounds',
array(
'methods' => 'GET',
'callback' => array( __CLASS__, 'rest_compounds' ),
'permission_callback' => '__return_true',
'args' => array(
'search' => array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ),
'category' => array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ),
'fields' => array( 'type' => 'string', 'enum' => array( 'basic', 'full' ), 'default' => 'full' ),
'page' => array( 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'sanitize_callback' => 'absint' ),
'per_page' => array( 'type' => 'integer', 'default' => PSA_Directory::PER_PAGE_DEFAULT, 'minimum' => 1, 'maximum' => PSA_Directory::PER_PAGE_MAX, 'sanitize_callback' => 'absint' ),
'search' => array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
),
) );
'category' => array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
),
'fields' => array(
'type' => 'string',
'enum' => array( 'basic', 'full' ),
'default' => 'full',
),
'page' => array(
'type' => 'integer',
'default' => 1,
'minimum' => 1,
'sanitize_callback' => 'absint',
),
'per_page' => array(
'type' => 'integer',
'default' => PSA_Directory::PER_PAGE_DEFAULT,
'minimum' => 1,
'maximum' => PSA_Directory::PER_PAGE_MAX,
'sanitize_callback' => 'absint',
),
),
)
);
}
/**
@@ -65,11 +92,13 @@ class PSA_Directory_API {
}
if ( ! empty( $category ) ) {
$query_args['tax_query'] = array( array(
$query_args['tax_query'] = array(
array(
'taxonomy' => 'peptide_category',
'field' => 'slug',
'terms' => $category,
) );
),
);
}
$query = new WP_Query( $query_args );
@@ -83,13 +112,16 @@ class PSA_Directory_API {
$compounds[] = self::format_compound( $post, $fields );
}
$response = new WP_REST_Response( array(
$response = new WP_REST_Response(
array(
'compounds' => $compounds,
'total' => (int) $query->found_posts,
'total_pages' => (int) $query->max_num_pages,
'page' => $page,
'per_page' => $per_page,
), 200 );
),
200
);
$response->header( 'X-WP-Total', (string) $query->found_posts );
$response->header( 'X-WP-TotalPages', (string) $query->max_num_pages );
@@ -111,7 +143,10 @@ class PSA_Directory_API {
$categories = array();
if ( ! is_wp_error( $terms ) ) {
foreach ( $terms as $term ) {
$categories[] = array( 'slug' => $term->slug, 'name' => $term->name );
$categories[] = array(
'slug' => $term->slug,
'name' => $term->name,
);
}
}
@@ -132,7 +167,9 @@ class PSA_Directory_API {
return $basic;
}
$full = array_merge( $basic, array(
$full = array_merge(
$basic,
array(
'excerpt' => wp_trim_words( $post->post_content, 30 ),
'description' => wp_trim_words( $post->post_content, 80 ),
'sequence' => get_post_meta( $id, 'psa_sequence', true ),
@@ -152,7 +189,8 @@ class PSA_Directory_API {
'typical_dose_mcg' => get_post_meta( $id, 'psa_typical_dose_mcg', true ),
'cycle_parameters' => get_post_meta( $id, 'psa_cycle_parameters', true ),
'amino_acid_count' => get_post_meta( $id, 'psa_amino_acid_count', true ),
) );
)
);
return apply_filters( 'psa_rest_compound_data', $full, $id );
}

View File

@@ -38,21 +38,29 @@ class PSA_Directory {
* @return string HTML output.
*/
public static function render_shortcode( $atts ): string {
$terms = get_terms( array(
$terms = get_terms(
array(
'taxonomy' => 'peptide_category',
'hide_empty' => false,
'orderby' => 'name',
'order' => 'ASC',
) );
)
);
$categories = array();
if ( ! is_wp_error( $terms ) ) {
foreach ( $terms as $term ) {
$categories[] = array( 'slug' => $term->slug, 'name' => $term->name );
$categories[] = array(
'slug' => $term->slug,
'name' => $term->name,
);
}
}
wp_localize_script( 'psa-directory', 'psaDirectory', array(
wp_localize_script(
'psa-directory',
'psaDirectory',
array(
'restUrl' => esc_url_raw( rest_url( 'peptide-search-ai/v1/compounds' ) ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'categories' => $categories,
@@ -88,7 +96,8 @@ class PSA_Directory {
'copySequence' => __( 'Copy', 'peptide-search-ai' ),
'copied' => __( 'Copied!', 'peptide-search-ai' ),
),
) );
)
);
ob_start();
?>
@@ -104,11 +113,14 @@ class PSA_Directory {
// ── Backward-compatible proxies (REST moved to PSA_Directory_API) ───
/** @see PSA_Directory_API::register_rest_routes() */
public static function register_rest_routes(): void { PSA_Directory_API::register_rest_routes(); }
public static function register_rest_routes(): void {
PSA_Directory_API::register_rest_routes(); }
/** @see PSA_Directory_API::rest_compounds() */
public static function rest_compounds( $request ) { return PSA_Directory_API::rest_compounds( $request ); }
public static function rest_compounds( $request ) {
return PSA_Directory_API::rest_compounds( $request ); }
/** @see PSA_Directory_API::format_compound() */
public static function format_compound( $post, string $fields = 'full' ): array { return PSA_Directory_API::format_compound( $post, $fields ); }
public static function format_compound( $post, string $fields = 'full' ): array {
return PSA_Directory_API::format_compound( $post, $fields ); }
}

View File

@@ -47,8 +47,12 @@ class PSA_Post_Type_Meta {
wp_nonce_field( 'psa_save_meta', 'psa_meta_nonce' );
$textarea_fields = array(
'psa_sequence', 'psa_mechanism', 'psa_research_apps',
'psa_safety_profile', 'psa_dosage_info', 'psa_references',
'psa_sequence',
'psa_mechanism',
'psa_research_apps',
'psa_safety_profile',
'psa_dosage_info',
'psa_references',
);
echo '<table class="form-table">';
@@ -60,8 +64,11 @@ class PSA_Post_Type_Meta {
if ( 'psa_source' === $key ) {
$options = array(
'' => '— Select —', 'ai-generated' => 'AI Generated',
'pubchem' => 'PubChem Verified', 'manual' => 'Manual Entry', 'pending' => 'Pending Generation',
'' => '— Select —',
'ai-generated' => 'AI Generated',
'pubchem' => 'PubChem Verified',
'manual' => 'Manual Entry',
'pending' => 'Pending Generation',
);
echo '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $key ) . '">';
foreach ( $options as $opt_val => $opt_label ) {

View File

@@ -121,8 +121,11 @@ class PSA_Post_Type {
if ( 'psa_source' === $column ) {
$source = get_post_meta( $post_id, 'psa_source', true );
$labels = array(
'ai-generated' => 'AI', 'pubchem' => 'PubChem',
'manual' => 'Manual', 'pending' => 'Pending', 'failed' => 'Failed',
'ai-generated' => 'AI',
'pubchem' => 'PubChem',
'manual' => 'Manual',
'pending' => 'Pending',
'failed' => 'Failed',
);
echo esc_html( $labels[ $source ] ?? '—' );
}
@@ -140,14 +143,18 @@ class PSA_Post_Type {
// ── Backward-compatible proxies (meta boxes moved to PSA_Post_Type_Meta) ──
/** @see PSA_Post_Type_Meta::add_meta_boxes() */
public static function add_meta_boxes(): void { PSA_Post_Type_Meta::add_meta_boxes(); }
public static function add_meta_boxes(): void {
PSA_Post_Type_Meta::add_meta_boxes(); }
/** @see PSA_Post_Type_Meta::render_meta_box() */
public static function render_meta_box( $post ): void { PSA_Post_Type_Meta::render_meta_box( $post ); }
public static function render_meta_box( $post ): void {
PSA_Post_Type_Meta::render_meta_box( $post ); }
/** @see PSA_Post_Type_Meta::render_extended_meta_box() */
public static function render_extended_meta_box( $post ): void { PSA_Post_Type_Meta::render_extended_meta_box( $post ); }
public static function render_extended_meta_box( $post ): void {
PSA_Post_Type_Meta::render_extended_meta_box( $post ); }
/** @see PSA_Post_Type_Meta::save_meta() */
public static function save_meta( int $post_id ): void { PSA_Post_Type_Meta::save_meta( $post_id ); }
public static function save_meta( int $post_id ): void {
PSA_Post_Type_Meta::save_meta( $post_id ); }
}

View File

@@ -40,7 +40,12 @@ class PSA_Search_Handler {
$results = PSA_Search::search_peptides( $query );
if ( ! empty( $results['results'] ) ) {
wp_send_json_success( array( 'status' => 'found', 'data' => $results ) );
wp_send_json_success(
array(
'status' => 'found',
'data' => $results,
)
);
return;
}
@@ -53,7 +58,12 @@ class PSA_Search_Handler {
// 3. Check rate limit before attempting new generation.
if ( ! self::check_rate_limit() ) {
wp_send_json_success( array( 'status' => 'rate_limited', 'message' => 'Too many requests. Please try again later.' ) );
wp_send_json_success(
array(
'status' => 'rate_limited',
'message' => 'Too many requests. Please try again later.',
)
);
return;
}
@@ -78,10 +88,12 @@ class PSA_Search_Handler {
error_log( 'PSA: Max retries (' . PSA_Config::MAX_GENERATION_RETRIES . ') exceeded for "' . $pending->post_title . '" (post ' . $pending->ID . '). Marking as failed.' );
update_post_meta( $pending->ID, 'psa_source', 'failed' );
update_post_meta( $pending->ID, 'psa_generation_error', 'Generation failed after ' . PSA_Config::MAX_GENERATION_RETRIES . ' attempts.' );
wp_send_json_success( array(
wp_send_json_success(
array(
'status' => 'invalid',
'message' => 'We were unable to generate content for this peptide. Please try again later.',
) );
)
);
return;
}
@@ -91,7 +103,12 @@ class PSA_Search_Handler {
self::schedule_background_generation( $pending->ID, $pending->post_title );
}
wp_send_json_success( array( 'status' => 'pending', 'peptide_name' => $pending->post_title ) );
wp_send_json_success(
array(
'status' => 'pending',
'peptide_name' => $pending->post_title,
)
);
}
/**
@@ -124,10 +141,12 @@ class PSA_Search_Handler {
$daily_key = 'psa_daily_gen_' . gmdate( 'Y-m-d' );
$daily_count = (int) get_transient( $daily_key );
if ( $daily_count >= PSA_Config::DAILY_GENERATION_CAP ) {
wp_send_json_success( array(
wp_send_json_success(
array(
'status' => 'rate_limited',
'message' => 'Our system has reached its daily limit for adding new peptides. Please try again tomorrow.',
) );
)
);
return;
}
@@ -135,14 +154,21 @@ class PSA_Search_Handler {
if ( is_wp_error( $validation ) ) {
error_log( 'PSA: Validation error for "' . $query . '": ' . $validation->get_error_message() );
wp_send_json_success( array( 'status' => 'invalid', 'message' => 'Could not verify this peptide name.' ) );
wp_send_json_success(
array(
'status' => 'invalid',
'message' => 'Could not verify this peptide name.',
)
);
}
if ( empty( $validation['is_valid'] ) ) {
wp_send_json_success( array(
wp_send_json_success(
array(
'status' => 'invalid',
'message' => $validation['reason'] ?? 'This does not appear to be a recognized peptide.',
) );
)
);
}
// Valid peptide — increment rate limit and create placeholder.
@@ -165,7 +191,12 @@ class PSA_Search_Handler {
error_log( 'PSA: Scheduling async generation for "' . $canonical_name . '" (post ' . $placeholder_id . ')' );
self::schedule_background_generation( $placeholder_id, $canonical_name );
wp_send_json_success( array( 'status' => 'pending', 'peptide_name' => $canonical_name ) );
wp_send_json_success(
array(
'status' => 'pending',
'peptide_name' => $canonical_name,
)
);
}
/**

View File

@@ -134,7 +134,7 @@ class PSA_Search {
* @return string HTML output.
*/
public static function render_search_form( $atts ): string {
self::$instance_count++;
++self::$instance_count;
$instance = self::$instance_count;
$atts = shortcode_atts(
@@ -256,7 +256,11 @@ class PSA_Search {
$results[] = self::format_peptide( (int) $id );
}
$response = array( 'results' => $results, 'total' => count( $results ), 'query' => $query );
$response = array(
'results' => $results,
'total' => count( $results ),
'query' => $query,
);
set_transient( $cache_key, $response, 6 * HOUR_IN_SECONDS );
@@ -290,11 +294,14 @@ class PSA_Search {
// ── Backward-compatible proxies ─────────────────────────────────────
/** @see PSA_Search_Handler::ajax_search() */
public static function ajax_search(): void { PSA_Search_Handler::ajax_search(); }
public static function ajax_search(): void {
PSA_Search_Handler::ajax_search(); }
/** @see PSA_Search_Handler::create_pending_placeholder() */
public static function create_pending_placeholder( string $peptide_name ) { return PSA_Search_Handler::create_pending_placeholder( $peptide_name ); }
public static function create_pending_placeholder( string $peptide_name ) {
return PSA_Search_Handler::create_pending_placeholder( $peptide_name ); }
/** @see PSA_Search_Handler::schedule_background_generation() */
public static function schedule_background_generation( int $post_id, string $peptide_name ): void { PSA_Search_Handler::schedule_background_generation( $post_id, $peptide_name ); }
public static function schedule_background_generation( int $post_id, string $peptide_name ): void {
PSA_Search_Handler::schedule_background_generation( $post_id, $peptide_name ); }
}

View File

@@ -129,5 +129,7 @@ require_once ABSPATH . 'includes/class-psa-config.php';
require_once ABSPATH . 'includes/class-psa-cost-tracker.php';
require_once ABSPATH . 'includes/class-psa-openrouter.php';
require_once ABSPATH . 'includes/class-psa-kb-builder.php';
require_once ABSPATH . 'includes/class-psa-ai-content.php';
require_once ABSPATH . 'includes/class-psa-directory-api.php';
require_once ABSPATH . 'includes/class-psa-ai-generator.php';
require_once ABSPATH . 'includes/class-psa-upgrade.php';