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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(); }
|
||||
}
|
||||
|
||||
@@ -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 ); }
|
||||
}
|
||||
|
||||
@@ -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 ); }
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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 ); }
|
||||
}
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 ); }
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 ); }
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user