&1'; $output = @shell_exec($cmd); $counts = []; if ($output !== null) { foreach (preg_split('/\\r?\\n/', $output) as $line) { $line = trim($line); if ($line === '') continue; if (preg_match('/^([0-9]+(?:\\.[0-9]+)?)(k?)\\s*:\\s*(.+)$/i', $line, $m)) { $num = (float)$m[1]; if (strtolower($m[2]) === 'k') $num *= 1000; $counts[$m[3]] = (int)round($num); } } } echo json_encode($counts, JSON_UNESCAPED_UNICODE); exit; } /* ---------- DB open (RO unless deleting) ---------- */ $flags = isset($_GET['delete']) ? SQLITE3_OPEN_READWRITE : SQLITE3_OPEN_READONLY; $db = new SQLite3(__DIR__ . '/birds.db', $flags); $db->busyTimeout(1000); /* Paths / lists */ $base_symlink = $home . '/BirdSongs/Extracted/By_Date'; $base = realpath($base_symlink); $confirm_file = __DIR__ . '/confirmed_species_list.txt'; $exclude_file = __DIR__ . '/exclude_species_list.txt'; $whitelist_file = __DIR__ . '/whitelist_species_list.txt'; foreach ([$confirm_file, $exclude_file, $whitelist_file] as $file) { if (!file_exists($file)) touch($file); } $confirmed_species = file_exists($confirm_file) ? file($confirm_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : []; $excluded_species = file_exists($exclude_file) ? array_map(fn($l) => explode('_', trim($l), 2)[0], file($exclude_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)) : []; $whitelisted_species = file_exists($whitelist_file) ? array_map(fn($l) => explode('_', trim($l), 2)[0], file($whitelist_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)) : []; $config = get_config(); $sf_thresh = isset($config['SF_THRESH']) ? (float)$config['SF_THRESH'] : 0.0; /* ---------- helpers ---------- */ function join_path(...$parts): string { return preg_replace('#/+#', '/', implode('/', $parts)); } function can_unlink(string $p): bool { return is_link($p) || is_file($p); } /* Collect files/dirs for a species */ function collect_species_targets(SQLite3 $db, string $species, string $home, $base): array { $stmt = $db->prepare('SELECT Date, Com_Name, Sci_Name, File_Name FROM detections WHERE Sci_Name = :name'); ensure_db_ok($stmt); $stmt->bindValue(':name', $species, SQLITE3_TEXT); $res = $stmt->execute(); $count = 0; $files = []; $dirs = []; $sci = null; while ($row = $res->fetchArray(SQLITE3_ASSOC)) { $count++; if ($sci === null) $sci = $row['Sci_Name']; $dir = str_replace([' ', "'"], ['_', ''], $row['Com_Name']); $candidates = [ join_path($home, 'BirdSongs/Extracted/By_Date', $row['Date'], $dir, $row['File_Name']), join_path($home, 'BirdSongs/Extracted/By_Date/shifted', $row['Date'], $dir, $row['File_Name']), ]; foreach ($candidates as $c) { if (can_unlink($c)) { $files[$c] = true; $dirs[] = dirname($c); continue; } $d = realpath(dirname($c)); if ($d !== false) { $alt = $d . DIRECTORY_SEPARATOR . basename($c); if (can_unlink($alt)) { $files[$alt] = true; $dirs[] = dirname($alt); } } } } return ['count'=>$count, 'files'=>array_keys($files), 'dirs'=>array_values(array_unique($dirs)), 'sci'=>$sci]; } /* ---------- toggle exclude/whitelist/confirmed ---------- */ if (isset($_GET['toggle'], $_GET['species'], $_GET['action'])) { $list = $_GET['toggle']; $species = htmlspecialchars_decode($_GET['species'], ENT_QUOTES); if ($list === 'exclude') { $file = $exclude_file; } elseif ($list === 'whitelist') { $file = $whitelist_file; } elseif ($list === 'confirmed') { $file = $confirm_file; } else { header('Content-Type: text/plain'); echo 'Invalid list type'; exit; } $lines = file_exists($file) ? file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : []; if ($_GET['action'] === 'add') { if (!in_array($species, $lines, true)) $lines[] = $species; } else { $lines = array_values(array_filter($lines, fn($l) => $l !== $species)); } file_put_contents($file, implode("\n", $lines) . (empty($lines) ? "" : "\n")); header('Content-Type: text/plain'); echo 'OK'; exit; } /* ---------- count ---------- */ if (isset($_GET['getcounts'])) { header('Content-Type: application/json'); if ($base === false) { http_response_code(500); exit(json_encode(['error' => 'Base directory not found'])); } $species = htmlspecialchars_decode($_GET['getcounts'], ENT_QUOTES); $info = collect_species_targets($db, $species, $home, $base); echo json_encode(['count' => $info['count'], 'files' => count($info['files'])]); exit; } /* ---------- delete ---------- */ if (isset($_GET['delete'])) { header('Content-Type: application/json'); if ($base === false) { http_response_code(500); exit(json_encode(['error' => 'Base directory not found'])); } $species = htmlspecialchars_decode($_GET['delete'], ENT_QUOTES); $info = collect_species_targets($db, $species, $home, $base); $deleted = count($info['files']); foreach ($info['dirs'] as $dir) { if (exec("sudo rm -r $dir 2>&1", $output)) { echo "Error - files deletion failed : " . implode(", ", $output) . "
"; exit; } } $del = $db->prepare('DELETE FROM detections WHERE Sci_Name = :name'); ensure_db_ok($del); $del->bindValue(':name', $species, SQLITE3_TEXT); $del->execute(); $lines_deleted = $db->changes(); if ($info['sci'] !== null && file_exists($confirm_file)) { $identifier = $info['sci']; $lines = array_values(array_filter($confirmed_species, fn($l) => $l !== $identifier)); file_put_contents($confirm_file, implode("\n", $lines) . (empty($lines) ? "" : "\n")); } echo json_encode(['lines' => $lines_deleted, 'files' => $deleted]); exit; } /* ---------- query species aggregates ---------- */ $sql = <<query($sql); ?>
fetchArray(SQLITE3_ASSOC)) { $common = $row['Com_Name']; $scient = $row['Sci_Name']; $count = (int)$row['Count']; $max_confidence = round((float)$row['MaxConfidence'] * 100, 1); $identifier = $row['Sci_Name'].'_'.$row['Com_Name']; $identifier_sci = $row['Sci_Name']; $lastSeen = $row['LastSeen'] ?? ''; $lastSeenSort = $lastSeen ? (strtotime($lastSeen) ?: 0) : 0; $common_link = "{$common}"; $is_confirmed = in_array($identifier_sci, $confirmed_species, true); $is_excluded = in_array($identifier_sci, $excluded_species, true); $is_whitelisted = in_array($identifier_sci, $whitelisted_species, true); $comnamegraph = str_replace("'", "\'", $row['Com_Name']); $chart_cell = sprintf("", $comnamegraph); $identifier_js = addslashes($identifier); $identifier_sci_js = addslashes($identifier_sci); $confirm_cell = $is_confirmed ? "" : ""; $excl_cell = $is_excluded ? "" : ""; $white_cell = $is_whitelisted ? "" : ""; $sciname_raw = $row['Sci_Name']; $info_url = get_info_url($sciname_raw); if (!empty($info_url)) { $url = $info_url['URL'] ?? $info_url; $scient_link = "{$scient}"; } else { $scient_link = "{$scient}"; } echo "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . ""; } ?>
Common Name Scientific Name Stats Count Max Confidence Last Seen Probability Confirmed Excluded Whitelisted Delete
{$common_link}{$scient_link}{$chart_cell}{$count}{$max_confidence}%{$lastSeen}0.0000".$confirm_cell."".$excl_cell."".$white_cell."