Skip to content

Commit

Permalink
Merge pull request #1 from nvnieuwk/feat/programmable-values
Browse files Browse the repository at this point in the history
Feat/programmable values
  • Loading branch information
nvnieuwk authored Nov 13, 2023
2 parents 0ec7666 + be46809 commit 3948a82
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: '1.21'

- name: Install dependencies
run: go get .
Expand Down
48 changes: 20 additions & 28 deletions convert/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package bedgovcf

import (
"fmt"
"log"
"os"
"strings"
Expand All @@ -28,62 +27,55 @@ func ReadConfig(configString string) Config {

// Validate the config
func (c *Config) validate() {
if c.Chrom.Field == "" {
log.Printf("No field defined for CHROM, defaulting to the column 0")
c.Chrom.Field = "0"
if c.Chrom.Value == "" {
log.Printf("No value defined for CHROM, defaulting to the column 0")
c.Chrom.Value = "$0"
}

if c.Pos.Field == "" {
log.Printf("No field defined for POS, defaulting to the column 1")
c.Pos.Field = "1"
if c.Pos.Value == "" {
log.Printf("No value defined for POS, defaulting to the column 1")
c.Pos.Value = "$1"
}

if c.Id.Field == "" && c.Id.Prefix == "" {
log.Printf("No field or prefix specified for the ID, defaulting to prefix 'id_")
if c.Id.Value == "" && c.Id.Prefix == "" {
log.Printf("No value or prefix specified for the ID, defaulting to prefix 'id_")
c.Id.Prefix = "id_"
}

if c.Ref.Field == "" && c.Ref.Value == "" {
log.Printf("No field or value specified for the REF, defaulting to value 'N")
if c.Ref.Value == "" {
log.Printf("No value specified for the REF, defaulting to value 'N")
c.Ref.Value = "N"
}

if c.Alt.Field == "" && c.Alt.Value == "" {
log.Printf("No field or value specified for the ALT, defaulting to value '<CNV>")
if c.Alt.Value == "" {
log.Printf("No value specified for the ALT, defaulting to value '<CNV>")
c.Alt.Value = "<CNV>"
}

if c.Qual.Field == "" && c.Qual.Value == "" {
log.Printf("No field or value specified for the QUAL, defaulting to value '.'")
if c.Qual.Value == "" {
log.Printf("No value specified for the QUAL, defaulting to value '.'")
c.Qual.Value = "."
}

if c.Filter.Field == "" && c.Filter.Value == "" {
log.Printf("No field or value specified for the FILTER, defaulting to value 'PASS'")
if c.Filter.Value == "" {
log.Printf("No value specified for the FILTER, defaulting to value 'PASS'")
c.Filter.Value = "PASS"
}

errStrings := []string{}

if len(c.Info) != 0 {
for k, v := range c.Info {
if v.Field == "" && v.Value == "" {
s := fmt.Sprintf("No field or value specified for the INFO/%v", strings.ToUpper(k))
errStrings = append(errStrings, s)
if v.Value == "" {
log.Printf("No value specified for the INFO/%v", strings.ToUpper(k))
}
}
}

if len(c.Format) != 0 {
for k, v := range c.Format {
if v.Field == "" && v.Value == "" {
s := fmt.Sprintf("No field or value specified for the FORMAT/%v", strings.ToUpper(k))
errStrings = append(errStrings, s)
if v.Value == "" {
log.Printf("No value specified for the FORMAT/%v", strings.ToUpper(k))
}
}
}

if len(errStrings) != 0 {
log.Fatalf("The following errors were found in the config file:\n%v", strings.Join(errStrings, "\n"))
}
}
143 changes: 143 additions & 0 deletions convert/resolve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package bedgovcf

import (
"fmt"
"log"
"math"
"slices"
"strconv"
"strings"
)

func resolveField(configValues []string, bedValues []string, bedHeader []string) string {

input := []string{}
for _, v := range configValues {
if strings.HasPrefix(v, "$") {
var headerIndex int
for j, w := range bedHeader {
if w == v[1:] {
headerIndex = j
break
}
}
input = append(input, bedValues[headerIndex])
continue
} else {
input = append(input, v)
}
}

function := ""
if strings.HasPrefix(input[0], "~") {
function = configValues[0][1:]
} else {
return strings.Join(input, " ")
}

switch function {
case "round":
// ~round <value>
float, err := strconv.ParseFloat(input[1], 64)
if err != nil {
log.Fatalf("Failed to parse the value (%v) to a float: %v", input[1], err)
}
round := math.Round(float)
if round == -0 {
round = 0
}
return fmt.Sprintf("%v", round)
case "sum":
// ~sum <value1> <value2> ...
var sum float64
for _, v := range input[1:] {
float, err := strconv.ParseFloat(v, 64)
if err != nil {
log.Fatalf("Failed to parse the value (%v) to a float: %v", v, err)
}
sum += float
}

return strconv.FormatFloat(sum, 'g', -1, 64)
case "min":
// ~min <startValue> <valueToSubstract1> <valueToSubstract2> ...
min, err := strconv.ParseFloat(input[1], 64)
if err != nil {
log.Fatalf("Failed to parse the value (%v) to a float: %v", input[1], err)
}
for _, v := range input[2:] {
float, err := strconv.ParseFloat(v, 64)
if err != nil {
log.Fatalf("Failed to parse the value (%v) to a float: %v", v, err)
}
min -= float
}
return strconv.FormatFloat(min, 'g', -1, 64)
case "if":
// ~if <value1> <operator> <value2> <value_if_true> <value_if_false>
// supported operators: > < >= <= ==
v1 := input[1]
operator := input[2]
v2 := input[3]
vTrue := input[4]
vFalse := input[5:]

floatV1, err1 := strconv.ParseFloat(v1, 64)
floatV2, err2 := strconv.ParseFloat(v2, 64)

floatOperators := []string{"<", ">", "<=", ">="}
if slices.Contains(floatOperators, operator) && (err1 != nil || err2 != nil) {
log.Fatalf("Failed to parse the values (%v and %v) to a float: %v and %v", v1, v2, err1, err2)
}

vFalseResolved := ""
if strings.HasPrefix(vFalse[0], "~") {
vFalseResolved = resolveField(vFalse, bedValues, bedHeader)
} else {
vFalseResolved = strings.Join(vFalse, " ")
}

switch operator {
case "<":
if floatV1 < floatV2 {
return vTrue
} else {
return vFalseResolved
}
case ">":
if floatV1 > floatV2 {
return vTrue
} else {
return vFalseResolved
}
case ">=":
if floatV1 >= floatV2 {
return vTrue
} else {
return vFalseResolved
}
case "<=":
if floatV1 <= floatV2 {
return vTrue
} else {
return vFalseResolved
}
case "==":
if err1 == nil && err2 == nil {
if floatV1 == floatV2 {
return vTrue
} else {
return vFalseResolved
}
} else {
if v1 == v2 {
return vTrue
} else {
return vFalseResolved
}
}
}
}

return ""
}
Loading

0 comments on commit 3948a82

Please sign in to comment.