Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions backend/alignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,27 @@ func FSAlignments(id Id, entry []int64, databases []string, jobsbase string) ([]
return ReadAlignments[FoldseekAlignmentEntry, int64](id, entry, databases, jobsbase)
}

func FSAllAlignments(id Id, databases []string, jobsbase string) ([]SearchResult, error) {
base := filepath.Join(jobsbase, string(id))
if len(databases) == 0 {
return nil, nil
}
probe := filepath.Join(filepath.Clean(base), "alis_"+databases[0])
reader := Reader[uint32]{}
err := reader.Make(dbpaths(probe))
if err != nil {
return nil, err
}
n := reader.Size()
reader.Delete()

entries := make([]int64, n)
for i := int64(0); i < n; i++ {
entries[i] = i
}
return ReadAlignments[FoldseekAlignmentEntry, int64](id, entries, databases, jobsbase)
}

func ComplexAlignments(id Id, entry []uint32, databases []string, jobsbase string) ([]SearchResult, error) {
return ReadAlignments[ComplexAlignmentEntry, uint32](id, entry, databases, jobsbase)
}
Expand Down
134 changes: 113 additions & 21 deletions backend/folddiscojob.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
Expand All @@ -14,8 +16,13 @@ type FoldDiscoJob struct {
Database []string `json:"database" validate:"required"`
// Mode string `json:"mode" validate:"oneof=3di tmalign 3diaa"`
// TaxFilter string `json:"taxfilter"`
Motif string `json:"motif"`
query string
Motif string `json:"motif"`
// Motifs[i] holds one or more motif strings for queries[i].
// Each motifs[] form value may contain multiple motifs separated by ";".
Motifs [][]string `json:"motifs,omitempty"`
Top int `json:"top,omitempty"`
query string
queries []string
}

const FolddiscoCacheVersion string = "v1"
Expand All @@ -26,6 +33,15 @@ func (r FoldDiscoJob) Hash() Id {
h.Write(([]byte)(JobFoldDisco))
h.Write([]byte(r.query))
h.Write([]byte(r.Motif))
h.Write([]byte(fmt.Sprintf("%d", r.Top)))
for _, q := range r.queries {
h.Write([]byte(q))
}
for _, ms := range r.Motifs {
for _, m := range ms {
h.Write([]byte(m))
}
}
// h.Write([]byte(r.Mode))
// if r.TaxFilter != "" {
// h.Write([]byte(r.TaxFilter))
Expand Down Expand Up @@ -53,14 +69,56 @@ func (r FoldDiscoJob) WritePDB(path string) error {
return nil
}

func NewFoldDiscoJobRequest(query string, motif string, dbs []string, validDbs []Params, resultPath string, email string) (JobRequest, error) {
func (r FoldDiscoJob) IsBatch() bool {
return len(r.Motifs) > 0
}

func (r FoldDiscoJob) WriteBatchFiles(basePath string) error {
var lines []string
for i, q := range r.queries {
ext := ".pdb"
if len(q) > 0 && ismmCIFFirstLine(strings.TrimSpace(q)) {
ext = ".cif"
}
filename := fmt.Sprintf("job_%d%s", i, ext)
fp := filepath.Join(basePath, filename)
if err := os.WriteFile(fp, []byte(q), 0644); err != nil {
return err
}
// Each query can have multiple motifs; emit one batch line per motif.
for _, m := range r.Motifs[i] {
lines = append(lines, fp+"\t"+m)
}
}
content := strings.Join(lines, "\n") + "\n"
return os.WriteFile(filepath.Join(basePath, "query_batch.txt"), []byte(content), 0644)
}

func validateFolddiscoDatabases(dbs []string, validDbs []Params) error {
ids := make([]string, 0)
for _, item := range validDbs {
if item.Motif {
ids = append(ids, item.Path)
}
}
for _, item := range dbs {
if isIn(item, ids) == -1 {
return errors.New("selected databases are not valid")
}
}
return nil
}

func NewFoldDiscoJobRequest(query string, motif string, dbs []string, validDbs []Params, resultPath string, email string, top int) (JobRequest, error) {
if top <= 0 {
top = 1000
}
job := FoldDiscoJob{
max(strings.Count(query, "HEADER"), 1),
dbs,
// mode,
// taxfilter,
motif,
query,
Size: max(strings.Count(query, "HEADER"), 1),
Database: dbs,
Motif: motif,
Top: top,
query: query,
}

request := JobRequest{
Expand All @@ -71,23 +129,57 @@ func NewFoldDiscoJobRequest(query string, motif string, dbs []string, validDbs [
email,
}

ids := make([]string, 0)
for _, item := range validDbs {
if item.Motif {
ids = append(ids, item.Path)
}
if err := validateFolddiscoDatabases(dbs, validDbs); err != nil {
return request, err
}

for _, item := range job.Database {
idx := isIn(item, ids)
if idx == -1 {
return request, errors.New("selected databases are not valid")
return request, nil
}

// NewFoldDiscoBatchJobRequest creates a batch Folddisco job. motifs[i] contains
// the motifs for queries[i]; each entry may hold more than one motif so that a
// single structure can be searched with multiple motif specifications without
// re-uploading the file.
func NewFoldDiscoBatchJobRequest(queries []string, motifs [][]string, dbs []string, validDbs []Params, resultPath string, email string, top int) (JobRequest, error) {
if len(queries) != len(motifs) {
return JobRequest{}, errors.New("queries and motifs must have the same length")
}
if len(queries) == 0 {
return JobRequest{}, errors.New("at least one query is required")
}
for i, ms := range motifs {
if len(ms) == 0 {
return JobRequest{}, fmt.Errorf("query %d has no motifs", i)
}
}

// if !validTaxonFilter(taxfilter) {
// return request, errors.New("invalid taxon filter")
// }
totalSize := 0
for _, q := range queries {
totalSize += max(strings.Count(q, "HEADER"), 1)
}

if top <= 0 {
top = 1000
}
job := FoldDiscoJob{
Size: totalSize,
Database: dbs,
Motifs: motifs,
Top: top,
queries: queries,
}

request := JobRequest{
job.Hash(),
StatusPending,
JobFoldDisco,
job,
email,
}

if err := validateFolddiscoDatabases(dbs, validDbs); err != nil {
return request, err
}

return request, nil
}
6 changes: 6 additions & 0 deletions backend/jobsystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ func (m *JobRequest) WriteSupportFiles(base string) error {
return errors.New("invalid job type")
case JobStructureSearch:
if j, ok := m.Job.(StructureSearchJob); ok {
if j.IsBatch() {
return j.WriteBatchDir(base)
}
return j.WritePDB(filepath.Join(base, "job.pdb"))
}
return errors.New("invalid job type")
Expand Down Expand Up @@ -159,6 +162,9 @@ func (m *JobRequest) WriteSupportFiles(base string) error {
return errors.New("invalid job type")
case JobFoldDisco:
if j, ok := m.Job.(FoldDiscoJob); ok {
if j.IsBatch() {
return j.WriteBatchFiles(base)
}
return j.WritePDB(filepath.Join(base, "job.pdb"))
}
return errors.New("invalid job type")
Expand Down
Loading