liberu-genealogy / php-dna

DNA processing and manipulating for PHP 8.3
https://www.liberu.co.uk
MIT License
31 stars 23 forks source link

Sweep: Refactor generally to improve quality the file snps.php and maintainbility plus readable by following psr standards #146

Closed curtisdelicata closed 5 months ago

curtisdelicata commented 5 months ago
Checklist - [X] Modify `src/Snps/SNPs.php` ✓ https://github.com/liberu-genealogy/php-dna/commit/d12324a76555bffd1a8ca35b6ded0d0ca3f6129c [Edit](https://github.com/liberu-genealogy/php-dna/edit/sweep/refactor_generally_to_improve_quality_th_7bc25/src/Snps/SNPs.php) - [X] Modify `src/Snps/SNPs.php` ✓ https://github.com/liberu-genealogy/php-dna/commit/d12324a76555bffd1a8ca35b6ded0d0ca3f6129c [Edit](https://github.com/liberu-genealogy/php-dna/edit/sweep/refactor_generally_to_improve_quality_th_7bc25/src/Snps/SNPs.php) - [X] Modify `src/Snps/SNPs.php` ✓ https://github.com/liberu-genealogy/php-dna/commit/d12324a76555bffd1a8ca35b6ded0d0ca3f6129c [Edit](https://github.com/liberu-genealogy/php-dna/edit/sweep/refactor_generally_to_improve_quality_th_7bc25/src/Snps/SNPs.php) - [X] Modify `src/Snps/SNPs.php` ✓ https://github.com/liberu-genealogy/php-dna/commit/d12324a76555bffd1a8ca35b6ded0d0ca3f6129c [Edit](https://github.com/liberu-genealogy/php-dna/edit/sweep/refactor_generally_to_improve_quality_th_7bc25/src/Snps/SNPs.php) - [X] Modify `src/Snps/SNPs.php` ✓ https://github.com/liberu-genealogy/php-dna/commit/d12324a76555bffd1a8ca35b6ded0d0ca3f6129c [Edit](https://github.com/liberu-genealogy/php-dna/edit/sweep/refactor_generally_to_improve_quality_th_7bc25/src/Snps/SNPs.php) - [X] Modify `src/Snps/SNPs.php` ✓ https://github.com/liberu-genealogy/php-dna/commit/d12324a76555bffd1a8ca35b6ded0d0ca3f6129c [Edit](https://github.com/liberu-genealogy/php-dna/edit/sweep/refactor_generally_to_improve_quality_th_7bc25/src/Snps/SNPs.php)
sweep-ai[bot] commented 5 months ago

🚀 Here's the PR! #158

See Sweep's progress at the progress dashboard!
💎 Sweep Pro: I'm using GPT-4. You have unlimited GPT-4 tickets. (tracking ID: 7cebf31c33)

[!TIP] I can email you next time I complete a pull request if you set up your email here!


Actions (click)


Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I think are relevant in decreasing order of relevance (click to expand). If some file is missing from here, you can mention the path in the ticket description. https://github.com/liberu-genealogy/php-dna/blob/3d5b908204bf168b03837aa518a9e3b56b52896e/src/Snps/SNPs.php#L1-L3065 https://github.com/liberu-genealogy/php-dna/blob/3d5b908204bf168b03837aa518a9e3b56b52896e/src/Individual.php#L1-L84 https://github.com/liberu-genealogy/php-dna/blob/3d5b908204bf168b03837aa518a9e3b56b52896e/src/Triangulation.php#L1-L33 https://github.com/liberu-genealogy/php-dna/blob/3d5b908204bf168b03837aa518a9e3b56b52896e/composer.json#L1-L28 https://github.com/liberu-genealogy/php-dna/blob/3d5b908204bf168b03837aa518a9e3b56b52896e/composer.lock#L1-L2493

Step 2: ⌨️ Coding

class SNPs implements Countable, Iterator { private array $_source = []; private array $_snps = []; private int $_build = 0; private ?bool $_phased = null; private ?bool $_build_detected = null; private ?Resources $_resources = null; private ?string $_chip = null; private ?string $_chip_version = null; private ?string $_cluster = null; private int $_position = 0; private array $_keys = []; private array $_duplicate = []; private array $_discrepant_XY = []; private array $_heterozygous_MT = []; private DataFrame $dataFrame; private SNPAnalysis $snpAnalysis; private MathOperations $mathOperations; /** * SNPs constructor. * class Snps implements Countable, Iterator { private array $source = []; private array $snps = []; private int $build = 0; private ?bool $phased = null; private ?bool $buildDetected = null; private ?Resources $resources = null; private ?string $chip = null; private ?string $chipVersion = null; private ?string $cluster = null; private int $position = 0; private array $keys = []; private array $duplicate = []; private array $discrepantXY = []; private array $heterozygousMT = []; private DataFrame $dataFrame; private SnpAnalysis $snpAnalysis; private MathOperations $mathOperations; /** * Snps constructor. *

class SNPs implements Countable, Iterator { private array $_source = []; private array $_snps = []; private int $_build = 0; private ?bool $_phased = null; private ?bool $_build_detected = null; private ?Resources $_resources = null; private ?string $_chip = null; private ?string $_chip_version = null; private ?string $_cluster = null; private int $_position = 0; private array $_keys = []; private array $_duplicate = []; private array $_discrepant_XY = []; private array $_heterozygous_MT = []; private DataFrame $dataFrame; private SNPAnalysis $snpAnalysis; private MathOperations $mathOperations; /** * SNPs constructor. * /** * Snps class represents a collection of SNPs (Single Nucleotide Polymorphisms). * It provides methods to manipulate and analyze the SNPs data. */ class Snps implements Countable, Iterator { /** * @var array $source The source of the SNPs data. */ private array $source = []; /** * @var array $snps The array of SNPs. */ private array $snps = []; /** * @var int $build The build version of the SNPs data. */ private int $build = 0; /** * @var bool|null $phased Indicates if the SNPs are phased. */ private ?bool $phased = null; /** * @var bool|null $buildDetected Indicates if the build version is detected. */ private ?bool $buildDetected = null; /** * @var Resources|null $resources The resources associated with the SNPs. */ private ?Resources $resources = null; /** * @var string|null $chip The chip used for genotyping. */ private ?string $chip = null; /** * @var string|null $chipVersion The version of the chip used for genotyping. */ private ?string $chipVersion = null; /** * @var string|null $cluster The cluster associated with the SNPs. */ private ?string $cluster = null; /** * @var int $position The current position in the SNPs array. */ private int $position = 0; /** * @var array $keys The keys of the SNPs array. */ private array $keys = []; /** * @var array $duplicate The duplicate SNPs. */ private array $duplicate = []; /** * @var array $discrepantXY The discrepant SNPs on the X and Y chromosomes. */ private array $discrepantXY = []; /** * @var array $heterozygousMT The heterozygous SNPs on the mitochondrial DNA. */ private array $heterozygousMT = []; /** * @var DataFrame $dataFrame The DataFrame object for data manipulation. */ private DataFrame $dataFrame; /** * @var SnpAnalysis $snpAnalysis The SnpAnalysis object for SNP analysis. */ private SnpAnalysis $snpAnalysis; /** * @var MathOperations $mathOperations The MathOperations object for mathematical operations. */ private MathOperations $mathOperations; /** * Snps constructor. *

public function __construct( private $file = "", private bool $only_detect_source = False, private bool $assign_par_snps = False, private string $output_dir = "output", private string $resources_dir = "resources", private bool $deduplicate = True, private bool $deduplicate_XY_chrom = True, private bool $deduplicate_MT_chrom = True, private bool $parallelize = False, private int $processes = 1, // cpu count private array $rsids = [], private $ensemblRestClient = null, ) //, $only_detect_source, $output_dir, $resources_dir, $parallelize, $processes) { // $this->_only_detect_source = $only_detect_source; $this->snpFileReader = new SnpFileReader($this->_resources, $this->ensemblRestClient); $this->buildDetector = new BuildDetector(); $this->clusterOverlapCalculator = new ClusterOverlapCalculator($this->_resources); $this->_source = []; $this->_phased = null; $this->_build = 0; $this->_build_detected = null; $this->_cluster = ""; $this->_chip = ""; $this->_chip_version = ""; $this->_source = []; // $this->_phased = false; $this->_build = 0; $this->_build_detected = false; // $this->_output_dir = $output_dir; $this->_resources = new Resources($resources_dir); // $this->_parallelizer = new Parallelizer($parallelize, $processes); $this->_cluster = ""; $this->_chip = ""; $this->dataFrame = new DataFrame(); $this->snpAnalysis = new SNPAnalysis(); $this->mathOperations = new MathOperations(); $this->_chip_version = ""; $this->ensemblRestClient = $ensemblRestClient ?? new Ensembl("https://api.ncbi.nlm.nih.gov", 1); public function __construct( private string $file = "", private bool $onlyDetectSource = false, private bool $assignParSnps = false, private string $outputDir = "output", private string $resourcesDir = "resources", private bool $deduplicate = true, private bool $deduplicateXYChrom = true, private bool $deduplicateMTChrom = true, private bool $parallelize = false, private int $processes = 1, private array $rsids = [], private ?EnsemblRestClient $ensemblRestClient = null, private ?SnpFileReader $snpFileReader = null, private ?BuildDetector $buildDetector = null, private ?ClusterOverlapCalculator $clusterOverlapCalculator = null, private ?Resources $resources = null, private ?DataFrame $dataFrame = null, private ?SnpAnalysis $snpAnalysis = null, private ?MathOperations $mathOperations = null ) { $this->source = []; $this->phased = null; $this->build = 0; $this->buildDetected = null; $this->cluster = ""; $this->chip = ""; $this->chipVersion = ""; $this->resources = $resources ?? new Resources($resourcesDir); $this->ensemblRestClient = $ensemblRestClient ?? new EnsemblRestClient("https://api.ncbi.nlm.nih.gov", 1); $this->snpFileReader = $snpFileReader ?? new SnpFileReader($this->resources, $this->ensemblRestClient); $this->buildDetector = $buildDetector ?? new BuildDetector(); $this->clusterOverlapCalculator = $clusterOverlapCalculator ?? new ClusterOverlapCalculator($this->resources); $this->dataFrame = $dataFrame ?? new DataFrame(); $this->snpAnalysis = $snpAnalysis ?? new SnpAnalysis(); $this->mathOperations = $mathOperations ?? new MathOperations();

public function computeClusterOverlap($cluster_overlap_threshold = 0.95): array { // Sample data for cluster overlap computation $data = [ "cluster_id" => ["c1", "c3", "c4", "c5", "v5"], "company_composition" => [ "23andMe-v4", "AncestryDNA-v1, FTDNA, MyHeritage", "23andMe-v3", "AncestryDNA-v2", "23andMe-v5, LivingDNA", ], "chip_base_deduced" => [ "HTS iSelect HD", "OmniExpress", "OmniExpress plus", "OmniExpress plus", "Illumina GSAs", ], "snps_in_cluster" => array_fill(0, 5, 0), "snps_in_common" => array_fill(0, 5, 0), ]; // Create a DataFrame from the data and set "cluster_id" as the index $df = new DataFrame($data); $df->setIndex("cluster_id"); $to_remap = null; if ($this->build != 37) { // Create a clone of the current object for remapping $to_remap = clone $this; $to_remap->remap(37); // clusters are relative to Build 37 $self_snps = $to_remap->snps()->select(["chrom", "pos"])->dropDuplicates(); } else { $self_snps = $this->snps()->select(["chrom", "pos"])->dropDuplicates(); } // Retrieve chip clusters from resources $chip_clusters = $this->resources->get_chip_clusters(); // Iterate over each cluster in the DataFrame foreach ($df->indexValues() as $cluster) { // Filter chip clusters based on the current cluster $cluster_snps = $chip_clusters->filter(function ($row) use ($cluster) { return strpos($row["clusters"], $cluster) !== false; })->select(["chrom", "pos"]); // Update the DataFrame with the number of SNPs in the cluster and in common with the current object $df->loc[$cluster]["snps_in_cluster"] = count($cluster_snps); $df->loc[$cluster]["snps_in_common"] = count($self_snps->merge($cluster_snps, "inner")); // Calculate overlap ratios for cluster and self $df["overlap_with_cluster"] = $df["snps_in_common"] / $df["snps_in_cluster"]; $df["overlap_with_self"] = $df["snps_in_common"] / count($self_snps); // Find the cluster with the maximum overlap $max_overlap = array_keys($df["overlap_with_cluster"], max($df["overlap_with_cluster"]))[0]; // Check if the maximum overlap exceeds the threshold for both cluster and self if ( $df["overlap_with_cluster"][$max_overlap] > $cluster_overlap_threshold && $df["overlap_with_self"][$max_overlap] > $cluster_overlap_threshold ) { // Update the current object's cluster and chip based on the maximum overlap $this->cluster = $max_overlap; $this->chip = $df["chip_base_deduced"][$max_overlap]; $company_composition = $df["company_composition"][$max_overlap]; // Check if the current object's source is present in the company composition if (strpos($company_composition, $this->source) !== false) { if ($this->source === "23andMe" || $this->source === "AncestryDNA") { // Extract the chip version from the company composition $i = strpos($company_composition, "v"); $this->chip_version = substr($company_composition, $i, $i + 2); } } else { // Log a warning about the SNPs data source not found } } } // Return the computed cluster overlap DataFrame return $df; } public function computeClusterOverlap($clusterOverlapThreshold = 0.95): array { $df = $this->createClusterOverlapDataFrame(); $selfSnps = $this->getSelfSnps(); $chipClusters = $this->resources->getChipClusters(); foreach ($df->indexValues() as $cluster) { $clusterSnps = $this->filterChipClustersByCluster($chipClusters, $cluster); $this->updateDataFrameWithClusterInfo($df, $cluster, $clusterSnps, $selfSnps); $this->calculateOverlapRatios($df, $selfSnps); $maxOverlap = $this->findMaxOverlapCluster($df); $this->updateClusterAndChipInfo($df, $maxOverlap, $clusterOverlapThreshold); } return $df; } private function createClusterOverlapDataFrame(): DataFrame { $data = [ "cluster_id" => ["c1", "c3", "c4", "c5", "v5"], "company_composition" => [ "23andMe-v4", "AncestryDNA-v1, FTDNA, MyHeritage", "23andMe-v3", "AncestryDNA-v2", "23andMe-v5, LivingDNA", ], "chip_base_deduced" => [ "HTS iSelect HD", "OmniExpress", "OmniExpress plus", "OmniExpress plus", "Illumina GSAs", ], "snps_in_cluster" => array_fill(0, 5, 0), "snps_in_common" => array_fill(0, 5, 0), ]; $df = new DataFrame($data); $df->setIndex("cluster_id"); return $df; } private function getSelfSnps(): DataFrame { if ($this->build != 37) { $toRemap = clone $this; $toRemap->remap(37); return $toRemap->snps()->select(["chrom", "pos"])->dropDuplicates(); } return $this->snps()->select(["chrom", "pos"])->dropDuplicates(); } private function filterChipClustersByCluster(DataFrame $chipClusters, string $cluster): DataFrame { return $chipClusters->filter(function ($row) use ($cluster) { return strpos($row["clusters"], $cluster) !== false; })->select(["chrom", "pos"]); } private function updateDataFrameWithClusterInfo(DataFrame $df, string $cluster, DataFrame $clusterSnps, DataFrame $selfSnps): void { $df->loc[$cluster]["snps_in_cluster"] = count($clusterSnps); $df->loc[$cluster]["snps_in_common"] = count($selfSnps->merge($clusterSnps, "inner")); } private function calculateOverlapRatios(DataFrame $df, DataFrame $selfSnps): void { $df["overlap_with_cluster"] = $df["snps_in_common"] / $df["snps_in_cluster"]; $df["overlap_with_self"] = $df["snps_in_common"] / count($selfSnps); } private function findMaxOverlapCluster(DataFrame $df): string { return array_keys($df["overlap_with_cluster"], max($df["overlap_with_cluster"]))[0]; } private function updateClusterAndChipInfo(DataFrame $df, string $maxOverlap, float $clusterOverlapThreshold): void { if ( $df["overlap_with_cluster"][$maxOverlap] > $clusterOverlapThreshold && $df["overlap_with_self"][$maxOverlap] > $clusterOverlapThreshold ) { $this->cluster = $maxOverlap; $this->chip = $df["chip_base_deduced"][$maxOverlap]; $companyComposition = $df["company_composition"][$maxOverlap]; if (strpos($companyComposition, $this->source) !== false) { if ($this->source === "23andMe" || $this->source === "AncestryDNA") { $i = strpos($companyComposition, "v"); $this->chipVersion = substr($companyComposition, $i, $i + 2); } } else { // Log a warning about the SNPs data source not found } } }

public function remap($target_assembly, $complement_bases = true) { $chromosomes_remapped = []; $chromosomes_not_remapped = []; $snps = $this->_snps; if (empty($snps)) { // Logger::warning("No SNPs to remap"); return [$chromosomes_remapped, $chromosomes_not_remapped]; } else { $chromosomes = array_unique(array_column($snps, 'chrom')); $chromosomes_not_remapped = $chromosomes; } $valid_assemblies = ["NCBI36", "GRCh37", "GRCh38", 36, 37, 38]; if (!in_array($target_assembly, $valid_assemblies)) { // Logger::warning("Invalid target assembly"); return [$chromosomes_remapped, $chromosomes_not_remapped]; } if (is_int($target_assembly)) { if ($target_assembly == 36) { $target_assembly = "NCBI36"; } else { $target_assembly = "GRCh" . strval($target_assembly); } } if ($this->_build == 36) { $source_assembly = "NCBI36"; } else { $source_assembly = "GRCh" . strval($this->_build); } if ($source_assembly == $target_assembly) { return [$chromosomes_remapped, $chromosomes_not_remapped]; } $assembly_mapping_data = $this->_resources->getAssemblyMappingData( $source_assembly, $target_assembly ); if (empty($assembly_mapping_data)) { return [$chromosomes_remapped, $chromosomes_not_remapped]; } $tasks = []; foreach ($chromosomes as $chrom) { if (array_key_exists($chrom, $assembly_mapping_data)) { $chromosomes_remapped[] = $chrom; $chromosomes_not_remapped = array_diff($chromosomes_not_remapped, [$chrom]); $mappings = $assembly_mapping_data[$chrom]; $tasks[] = [ "snps" => array_filter($snps, function ($snp) use ($chrom) { return $snp['chrom'] === $chrom; }), "mappings" => $mappings, "complement_bases" => $complement_bases, ]; } else { // Logger::warning( // "Chromosome $chrom not remapped; removing chromosome from SNPs for consistency" // ); $snps = array_filter($snps, function ($snp) use ($chrom) { return $snp['chrom'] !== $chrom; }); } } // remap SNPs $remapped_snps = array_map([$this, '_remapper'], $tasks); $remapped_snps = array_merge(...$remapped_snps); // update SNP positions and genotypes foreach ($remapped_snps as $snp) { $rsid = $snp['rsid']; $this->_snps[$rsid]['pos'] = $snp['pos']; $this->_snps[$rsid]['genotype'] = $snp['genotype']; } foreach ($snps as &$snp) { $snp['pos'] = (int)$snp['pos']; } $this->setSNPs($snps); $this->sort(); $this->_build = (int)substr($target_assembly, -2); return [$chromosomes_remapped, $chromosomes_not_remapped]; } public function remapSnps(string $targetAssembly, bool $complementBases = true): array { $remappedChromosomes = []; $notRemappedChromosomes = []; $snps = $this->snps; if (empty($snps)) { // Logger::warning("No SNPs to remap"); return [$remappedChromosomes, $notRemappedChromosomes]; } $chromosomes = array_unique(array_column($snps, 'chrom')); $notRemappedChromosomes = $chromosomes; $validAssemblies = ["NCBI36", "GRCh37", "GRCh38", 36, 37, 38]; if (!in_array($targetAssembly, $validAssemblies)) { // Logger::warning("Invalid target assembly"); return [$remappedChromosomes, $notRemappedChromosomes]; } $targetAssembly = $this->normalizeAssemblyName($targetAssembly); $sourceAssembly = $this->getSourceAssembly(); if ($sourceAssembly === $targetAssembly) { return [$remappedChromosomes, $notRemappedChromosomes]; } $assemblyMappingData = $this->resources->getAssemblyMappingData($sourceAssembly, $targetAssembly); if (empty($assemblyMappingData)) { return [$remappedChromosomes, $notRemappedChromosomes]; } $remapTasks = $this->createRemapTasks($chromosomes, $assemblyMappingData, $snps, $complementBases); $remappedSnps = $this->executeRemapTasks($remapTasks); $this->updateSnpPositionsAndGenotypes($remappedSnps); $this->setSnps($snps); $this->sortSnps(); $this->build = (int)substr($targetAssembly, -2); return [$remappedChromosomes, $notRemappedChromosomes]; } private function normalizeAssemblyName(string|int $assembly): string { if (is_int($assembly)) { return $assembly === 36 ? "NCBI36" : "GRCh" . strval($assembly); } return $assembly; } private function getSourceAssembly(): string { return $this->build === 36 ? "NCBI36" : "GRCh" . strval($this->build); } private function createRemapTasks(array $chromosomes, array $assemblyMappingData, array $snps, bool $complementBases): array { $tasks = []; foreach ($chromosomes as $chrom) { if (array_key_exists($chrom, $assemblyMappingData)) { $remappedChromosomes[] = $chrom; $notRemappedChromosomes = array_diff($notRemappedChromosomes, [$chrom]); $mappings = $assemblyMappingData[$chrom]; $tasks[] = [ "snps" => array_filter($snps, fn($snp) => $snp['chrom'] === $chrom), "mappings" => $mappings, "complement_bases" => $complementBases, ]; } else { // Logger::warning("Chromosome $chrom not remapped; removing chromosome from SNPs for consistency"); $snps = array_filter($snps, fn($snp) => $snp['chrom'] !== $chrom); } } return $tasks; } private function executeRemapTasks(array $tasks): array { $remappedSnps = array_map([$this, 'remapSnpsTask'], $tasks); return array_merge(...$remappedSnps); } private function updateSnpPositionsAndGenotypes(array $remappedSnps): void { foreach ($remappedSnps as $snp) { $rsid = $snp['rsid']; $this->snps[$rsid]['pos'] = $snp['pos']; $this->snps[$rsid]['genotype'] = $snp['genotype']; } foreach ($this->snps as &$snp) { $snp['pos'] = (int)$snp['pos']; } }

public function save( $filename = "", $vcf = false, $atomic = true, $vcf_alt_unavailable = ".", $vcf_chrom_prefix = "", $vcf_qc_only = false, $vcf_qc_filter = false, $kwargs = [] ) { if (!array_key_exists("sep", $kwargs)) { $kwargs["sep"] = "\t"; } $w = new Writer( [ 'snps' => $this, 'filename' => $filename, 'vcf' => $vcf, 'atomic' => $atomic, 'vcf_alt_unavailable' => $vcf_alt_unavailable, 'vcf_chrom_prefix' => $vcf_chrom_prefix, 'vcf_qc_only' => $vcf_qc_only, 'vcf_qc_filter' => $vcf_qc_filter ], $kwargs ); $result = $w->write(); [$path, $extra] = $result; if (count($extra) == 1 && !$extra[0]->isEmpty()) { $this->_discrepant_vcf_position = $extra[0]; $this->_discrepant_vcf_position->setIndex("rsid"); // logger::warning( // count($this->discrepant_vcf_position) . " SNP positions were found to be discrepant when saving VCF" // ); } return $path; } public function saveSnps( string $filename = "", bool $vcf = false, bool $atomic = true, string $vcfAltUnavailable = ".", string $vcfChromPrefix = "", bool $vcfQcOnly = false, bool $vcfQcFilter = false, array $kwargs = [] ): string { if (!array_key_exists("sep", $kwargs)) { $kwargs["sep"] = "\t"; } $writer = new Writer( [ 'snps' => $this, 'filename' => $filename, 'vcf' => $vcf, 'atomic' => $atomic, 'vcf_alt_unavailable' => $vcfAltUnavailable, 'vcf_chrom_prefix' => $vcfChromPrefix, 'vcf_qc_only' => $vcfQcOnly, 'vcf_qc_filter' => $vcfQcFilter ], $kwargs ); [$path, $extra] = $writer->write(); if (count($extra) === 1 && !$extra[0]->isEmpty()) { $this->discrepantVcfPosition = $extra[0]; $this->discrepantVcfPosition->setIndex("rsid"); // logger::warning( // count($this->discrepantVcfPosition) . " SNP positions were found to be discrepant when saving VCF" // ); } return $path; }


Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/refactor_generally_to_improve_quality_th_7bc25.


🎉 Latest improvements to Sweep:
  • New dashboard launched for real-time tracking of Sweep issues, covering all stages from search to coding.
  • Integration of OpenAI's latest Assistant API for more efficient and reliable code planning and editing, improving speed by 3x.
  • Use the GitHub issues extension for creating Sweep issues directly from your editor.

💡 To recreate the pull request edit the issue title or description. Something wrong? Let us know.

This is an automated message generated by Sweep AI.