fix: added additional selection in recent qso filters
This commit is contained in:
+91
-2
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -637,6 +638,76 @@ func (r *Repo) MarkEQSLSent(ctx context.Context, id int64, date string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bulkEditableCols whitelists the columns BulkSetField may write. Limited to
|
||||
// TEXT fields where setting one value across many QSOs is meaningful: the
|
||||
// per-service QSL/upload status fields, plus "my station"/operator fields that
|
||||
// are naturally constant across a run (grid, antenna, rig, address, …). It
|
||||
// deliberately excludes per-QSO fields (callsign, band, mode, date, RST, the
|
||||
// contacted station's details) and numeric columns (power, zones, lat/lon),
|
||||
// which would be corrupted or meaningless if bulk-set to a single value.
|
||||
var bulkEditableCols = map[string]bool{
|
||||
// QSL / upload status
|
||||
"lotw_sent": true,
|
||||
"lotw_rcvd": true,
|
||||
"eqsl_sent": true,
|
||||
"eqsl_rcvd": true,
|
||||
"qsl_sent": true,
|
||||
"qsl_rcvd": true,
|
||||
"qsl_via": true,
|
||||
"qrzcom_qso_upload_status": true,
|
||||
"clublog_qso_upload_status": true,
|
||||
"hrdlog_qso_upload_status": true,
|
||||
// My station / operator
|
||||
"station_callsign": true,
|
||||
"operator": true,
|
||||
"my_grid": true,
|
||||
"my_country": true,
|
||||
"my_state": true,
|
||||
"my_cnty": true,
|
||||
"my_iota": true,
|
||||
"my_sota_ref": true,
|
||||
"my_pota_ref": true,
|
||||
"my_wwff_ref": true,
|
||||
"my_street": true,
|
||||
"my_city": true,
|
||||
"my_postal_code": true,
|
||||
"my_rig": true,
|
||||
"my_antenna": true,
|
||||
"my_sig": true,
|
||||
"my_sig_info": true,
|
||||
// Misc text
|
||||
"comment": true,
|
||||
"notes": true,
|
||||
"rig": true,
|
||||
"ant": true,
|
||||
}
|
||||
|
||||
// BulkSetField sets one whitelisted column to value on every listed QSO in a
|
||||
// single statement. value "" clears the field. Returns rows affected.
|
||||
func (r *Repo) BulkSetField(ctx context.Context, ids []int64, column, value string) (int64, error) {
|
||||
if !bulkEditableCols[column] {
|
||||
return 0, fmt.Errorf("field %q is not bulk-editable", column)
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
ph := make([]string, len(ids))
|
||||
args := make([]any, 0, len(ids)+2)
|
||||
args = append(args, value, db.NowISO())
|
||||
for i, id := range ids {
|
||||
ph[i] = "?"
|
||||
args = append(args, id)
|
||||
}
|
||||
res, err := r.db.ExecContext(ctx,
|
||||
`UPDATE qso SET `+column+` = ?, updated_at = ? WHERE id IN (`+strings.Join(ph, ",")+`)`,
|
||||
args...)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("bulk set %s: %w", column, err)
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Update overwrites all editable fields of an existing QSO. updated_at is bumped.
|
||||
func (r *Repo) Update(ctx context.Context, q QSO) error {
|
||||
if q.ID == 0 {
|
||||
@@ -829,16 +900,26 @@ var filterableColumns = map[string]bool{
|
||||
"name": true, "qth": true, "address": true, "email": true,
|
||||
"grid": true, "country": true, "state": true, "cnty": true,
|
||||
"dxcc": true, "cont": true, "cqz": true, "ituz": true,
|
||||
"iota": true, "sota_ref": true, "pota_ref": true, "rig": true, "ant": true,
|
||||
"iota": true, "sota_ref": true, "pota_ref": true, "wwff_ref": true, "rig": true, "ant": true,
|
||||
"qsl_sent": true, "qsl_rcvd": true, "qsl_via": true,
|
||||
"lotw_sent": true, "lotw_rcvd": true, "eqsl_sent": true, "eqsl_rcvd": true,
|
||||
"qrzcom_qso_upload_status": true, "clublog_qso_upload_status": true,
|
||||
"qrzcom_qso_upload_status": true, "clublog_qso_upload_status": true, "hrdlog_qso_upload_status": true,
|
||||
"contest_id": true, "srx": true, "stx": true,
|
||||
"prop_mode": true, "sat_name": true,
|
||||
"station_callsign": true, "operator": true, "my_grid": true, "my_country": true,
|
||||
"my_state": true, "my_cnty": true, "my_iota": true, "my_sota_ref": true, "my_pota_ref": true,
|
||||
"my_wwff_ref": true, "my_street": true, "my_city": true, "my_postal_code": true,
|
||||
"my_rig": true, "my_antenna": true, "my_sig": true, "my_sig_info": true,
|
||||
"tx_pwr": true, "comment": true, "notes": true,
|
||||
}
|
||||
|
||||
// dateColumns are stored as full ISO timestamps; a filter on a bare YYYY-MM-DD
|
||||
// value compares on the date part (see conditionSQL) so day filters are exact.
|
||||
var dateColumns = map[string]bool{"qso_date": true, "qso_date_off": true}
|
||||
|
||||
// bareDateRe matches a plain calendar date with no time component.
|
||||
var bareDateRe = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
|
||||
|
||||
// filterableExtras whitelists virtual filter fields stored inside extras_json
|
||||
// (valid ADIF fields we don't promote to columns). The value is the uppercase
|
||||
// ADIF/Extras key; the SQL expression uses json_extract.
|
||||
@@ -885,6 +966,14 @@ func conditionSQL(c Condition) (string, []any, error) {
|
||||
return "", nil, fmt.Errorf("unknown filter field %q", c.Field)
|
||||
}
|
||||
v := c.Value
|
||||
// Date columns hold full ISO timestamps ("2020-01-01T12:34:56.000Z"). When
|
||||
// the user filters on a bare calendar date, compare only the date part so
|
||||
// "= 2020-01-01" matches that whole day and "<= 2020-12-31" includes it
|
||||
// (a raw string compare would otherwise drop times on the boundary day).
|
||||
if dateColumns[strings.ToLower(strings.TrimSpace(c.Field))] && bareDateRe.MatchString(strings.TrimSpace(v)) {
|
||||
col = "substr(" + col + ",1,10)"
|
||||
v = strings.TrimSpace(v)
|
||||
}
|
||||
switch c.Op {
|
||||
case "eq":
|
||||
return col + " = ?", []any{v}, nil
|
||||
|
||||
Reference in New Issue
Block a user