Пример #1
0
Файл: db.go Проект: js-arias/jdh
// searchExtern searches an extern id.
func searchExtern(serv string, extern []string) string {
	seid := serv + ":"
	for _, e := range extern {
		sv, id, _ := jdh.ParseExtern(e)
		if sv == seid {
			return id
		}
	}
	return ""
}
Пример #2
0
// Validate validates that a taxon is valid in the database, and set some
// canonical values. It returns an error if the taxon is not valid.
func (t *taxonomy) validate(tax *jdh.Taxon) error {
	tax.Id = strings.TrimSpace(tax.Id)
	tax.Name = strings.Join(strings.Fields(tax.Name), " ")
	if (len(tax.Id) == 0) || (len(tax.Name) == 0) {
		return errors.New("taxon without identification")
	}
	if _, ok := t.ids[tax.Id]; ok {
		return fmt.Errorf("taxon id %s already in use", tax.Id)
	}
	p := t.root
	if tax.Rank > jdh.Species {
		tax.Rank = jdh.Unranked
	}
	if len(tax.Parent) > 0 {
		var ok bool
		if p, ok = t.ids[tax.Parent]; !ok {
			return fmt.Errorf("taxon %s parent [%s] not in database", tax.Name, tax.Parent)
		}
	}
	if p == t.root {
		if !tax.IsValid {
			return fmt.Errorf("taxon %s is a synonym without a parent", tax.Name)
		}
	} else if !p.data.IsValid {
		return fmt.Errorf("taxon %s parent [%s] is a synonym", tax.Name, p.data.Name)
	}
	if !p.isDescValid(tax.Rank, tax.IsValid) {
		return fmt.Errorf("taxon %s rank incompatible with database hierarchy", tax.Name)
	}
	ext := tax.Extern
	tax.Extern = nil
	for _, e := range ext {
		serv, id, err := jdh.ParseExtern(e)
		if err != nil {
			continue
		}
		if len(id) == 0 {
			continue
		}
		add := true
		for _, ex := range tax.Extern {
			if strings.HasPrefix(ex, serv) {
				add = false
				break
			}
		}
		if !add {
			continue
		}
		if _, ok := t.ids[e]; !ok {
			tax.Extern = append(tax.Extern, e)
		}
	}
	return nil
}
Пример #3
0
// Validate validates that a raster is valid in the database, and set some
// canonical values. It returns an error if the raster is not valid.
func (d *distros) validate(ras *jdh.Raster) error {
	ras.Id = strings.TrimSpace(ras.Id)
	ras.Taxon = strings.TrimSpace(ras.Taxon)
	if (len(ras.Id) == 0) || (len(ras.Taxon) == 0) {
		return errors.New("raster without identification")
	}
	if _, ok := d.ids[ras.Id]; ok {
		return fmt.Errorf("raster id %s already in use", ras.Id)
	}
	if !d.db.t.isInDB(ras.Taxon) {
		return fmt.Errorf("taxon %s [associated with raster %s] not in dabtase", ras.Taxon, ras.Id)
	}
	if ras.Cols == 0 {
		return fmt.Errorf("raster %s with an invalid number of cols: 0", ras.Id)
	}
	if ras.Raster == nil {
		return fmt.Errorf("raster %s without rasterized data", ras.Id)
	}
	if ras.Source > jdh.MachineModel {
		ras.Source = jdh.UnknownRaster
	}
	ras.Reference = strings.TrimSpace(ras.Reference)
	ext := ras.Extern
	ras.Extern = nil
	for _, e := range ext {
		serv, id, err := jdh.ParseExtern(e)
		if err != nil {
			continue
		}
		if len(id) == 0 {
			continue
		}
		add := true
		for _, ex := range ras.Extern {
			if strings.HasPrefix(ex, serv) {
				add = false
				break
			}
		}
		if !add {
			continue
		}
		if _, ok := d.ids[e]; !ok {
			ras.Extern = append(ras.Extern, e)
		}
	}
	return nil
}
Пример #4
0
// Validates that a dataset is valid in the database. When posible canonical
// values will be set. If the dataset is invalid it will return an error.
func (d *datasets) validate(set *jdh.Dataset) error {
	set.Id = strings.TrimSpace(set.Id)
	set.Title = strings.Join(strings.Fields(set.Title), " ")
	if (len(set.Id) == 0) || (len(set.Title) == 0) {
		return errors.New("dataset without identification")
	}
	if _, ok := d.ids[set.Id]; ok {
		return fmt.Errorf("dataset id %s already in use", set.Id)
	}
	set.Citation = strings.Join(strings.Fields(set.Citation), " ")
	set.License = strings.Join(strings.Fields(set.License), " ")
	if u, err := url.Parse(set.Url); err != nil {
		set.Url = ""
	} else {
		set.Url = u.String()
	}
	ext := set.Extern
	set.Extern = nil
	for _, e := range ext {
		serv, id, err := jdh.ParseExtern(e)
		if err != nil {
			continue
		}
		if len(id) == 0 {
			continue
		}
		add := true
		for _, ex := range set.Extern {
			if strings.HasPrefix(ex, serv) {
				add = false
				break
			}
		}
		if !add {
			continue
		}
		if _, ok := d.ids[e]; !ok {
			set.Extern = append(set.Extern, e)
		}
	}
	return nil
}
Пример #5
0
// AddExtern adds an extern id to an specimen.
func (tr *trees) addExtern(ph *phylogeny, extern string) error {
	serv, id, err := jdh.ParseExtern(extern)
	if err != nil {
		return err
	}
	if len(id) == 0 {
		return nil
	}
	if or, ok := tr.ids[extern]; ok {
		return fmt.Errorf("extern id %s of %s alredy in use by %s", extern, ph.data.Id, or.data.Id)
	}
	// the service is already assigned, then overwrite
	for i, e := range ph.data.Extern {
		if strings.HasPrefix(e, serv) {
			delete(tr.ids, e)
			ph.data.Extern[i] = extern
			tr.ids[extern] = ph
			return nil
		}
	}
	ph.data.Extern = append(ph.data.Extern, extern)
	tr.ids[extern] = ph
	return nil
}
Пример #6
0
// ValPhy validates that a tree is valid in the database, and set some
// canonical values. It returns an error if the tree is not valid.
func (tr *trees) valPhy(phy *jdh.Phylogeny) error {
	phy.Id = strings.TrimSpace(phy.Id)
	if len(phy.Id) == 0 {
		return errors.New("phylogeny without identification")
	}
	if _, ok := tr.ids[phy.Id]; ok {
		return fmt.Errorf("phylogeny id %s alredy in use", phy.Id)
	}
	phy.Name = strings.Join(strings.Fields(phy.Name), " ")
	phy.Root = ""
	ext := phy.Extern
	phy.Extern = nil
	for _, e := range ext {
		serv, id, err := jdh.ParseExtern(e)
		if err != nil {
			continue
		}
		if len(id) == 0 {
			continue
		}
		add := true
		for _, ex := range phy.Extern {
			if strings.HasPrefix(ex, serv) {
				add = false
				break
			}
		}
		if !add {
			continue
		}
		if _, ok := tr.ids[e]; !ok {
			phy.Extern = append(phy.Extern, e)
		}
	}
	return nil
}
Пример #7
0
// AddExtern adds an extern id to an specimen.
func (d *distros) addExtern(rd *raster, extern string) error {
	serv, id, err := jdh.ParseExtern(extern)
	if err != nil {
		return err
	}
	if len(id) == 0 {
		return nil
	}
	if or, ok := d.ids[extern]; ok {
		return fmt.Errorf("extern id %s of %s alredy in use by %s", extern, rd.data.Id, or.data.Id)
	}
	// the service is already assigned, then overwrite
	for i, e := range rd.data.Extern {
		if strings.HasPrefix(e, serv) {
			delete(d.ids, e)
			rd.data.Extern[i] = extern
			d.ids[extern] = rd
			return nil
		}
	}
	rd.data.Extern = append(rd.data.Extern, extern)
	d.ids[extern] = rd
	return nil
}
Пример #8
0
// AddExtern adds an extern id to a taxon.
func (t *taxonomy) addExtern(tx *taxon, extern string) error {
	serv, id, err := jdh.ParseExtern(extern)
	if err != nil {
		return err
	}
	if len(id) == 0 {
		return nil
	}
	if ot, ok := t.ids[extern]; ok {
		return fmt.Errorf("extern id %s of %s alredy in use by %s", extern, tx.data.Name, ot.data.Name)
	}
	// the service is already assigned, then overwrite
	for i, e := range tx.data.Extern {
		if strings.HasPrefix(e, serv) {
			delete(t.ids, e)
			tx.data.Extern[i] = extern
			t.ids[extern] = tx
			return nil
		}
	}
	tx.data.Extern = append(tx.data.Extern, extern)
	t.ids[extern] = tx
	return nil
}
Пример #9
0
// AddExtern adds an extern id to a dataset.
func (d *datasets) addExtern(sd *setData, extern string) error {
	serv, id, err := jdh.ParseExtern(extern)
	if err != nil {
		return err
	}
	if len(id) == 0 {
		return nil
	}
	if ot, ok := d.ids[extern]; ok {
		return fmt.Errorf("extern id %s of %s alredy in use by %s", extern, sd.data.Id, ot.data.Id)
	}
	// the service is already assigned, then overwrite
	for i, e := range sd.data.Extern {
		if strings.HasPrefix(e, serv) {
			delete(d.ids, e)
			sd.data.Extern[i] = extern
			d.ids[extern] = sd
			return nil
		}
	}
	sd.data.Extern = append(sd.data.Extern, extern)
	d.ids[extern] = sd
	return nil
}
Пример #10
0
// AddExtern adds an extern id to an specimen.
func (s *specimens) addExtern(sp *specimen, extern string) error {
	serv, id, err := jdh.ParseExtern(extern)
	if err != nil {
		return err
	}
	if len(id) == 0 {
		return nil
	}
	if or, ok := s.ids[extern]; ok {
		return fmt.Errorf("extern id %s of %s alredy in use by %s", extern, sp.data.Id, or.data.Id)
	}
	// the service is already assigned, then overwrite
	for i, e := range sp.data.Extern {
		if strings.HasPrefix(e, serv) {
			delete(s.ids, e)
			sp.data.Extern[i] = extern
			s.ids[extern] = sp
			return nil
		}
	}
	sp.data.Extern = append(sp.data.Extern, extern)
	s.ids[extern] = sp
	return nil
}
Пример #11
0
// SetTree sets a value of a tree in the database.
func (tr *trees) setTree(vals []jdh.KeyValue) error {
	id := ""
	for _, kv := range vals {
		if len(kv.Value) == 0 {
			continue
		}
		if kv.Key == jdh.KeyId {
			id = kv.Value[0]
			break
		}
	}
	if len(id) == 0 {
		return errors.New("tree without identification")
	}
	ph, ok := tr.ids[id]
	if !ok {
		return nil
	}
	phy := ph.data
	for _, kv := range vals {
		switch kv.Key {
		case jdh.TreName:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if phy.Name == v {
				continue
			}
			phy.Name = v
		case jdh.KeyComment:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if phy.Comment == v {
				continue
			}
			phy.Comment = v
		case jdh.KeyExtern:
			ok := false
			for _, v := range kv.Value {
				v = strings.TrimSpace(v)
				if len(v) == 0 {
					continue
				}
				serv, ext, err := jdh.ParseExtern(v)
				if err != nil {
					return err
				}
				if len(ext) == 0 {
					if !tr.delExtern(ph, serv) {
						continue
					}
					ok = true
					continue
				}
				if tr.addExtern(ph, v) != nil {
					continue
				}
				ok = true
			}
			if !ok {
				continue
			}
		default:
			continue
		}
		tr.changed = true
	}
	return nil
}
Пример #12
0
// Set sets a value of a raster in the database.
func (d *distros) set(vals []jdh.KeyValue) error {
	id := ""
	for _, kv := range vals {
		if len(kv.Value) == 0 {
			continue
		}
		if kv.Key == jdh.KeyId {
			id = kv.Value[0]
			break
		}
	}
	if len(id) == 0 {
		return errors.New("raster without identification")
	}
	rd, ok := d.ids[id]
	if !ok {
		return nil
	}
	ras := rd.data
	for _, kv := range vals {
		switch kv.Key {
		case jdh.RasPixel:
			if len(kv.Value) == 0 {
				continue
			}
			v := strings.TrimSpace(kv.Value[0])
			if len(v) == 0 {
				continue
			}
			coor := strings.Split(v, ",")
			if len(coor) != 3 {
				return errors.New("invalid raster pixel reference: " + v)
			}
			x64, err := strconv.ParseInt(coor[0], 10, 0)
			if err != nil {
				return err
			}
			y64, err := strconv.ParseInt(coor[1], 10, 0)
			if err != nil {
				return err
			}
			p64, err := strconv.ParseInt(coor[2], 10, 0)
			if err != nil {
				return err
			}
			pt, px := image.Pt(int(x64), int(y64)), int(p64)
			if ras.Raster.At(pt) == px {
				continue
			}
			ras.Raster.Set(pt, px)
		case jdh.RDisSource:
			v := jdh.UnknownRaster
			if len(kv.Value) > 0 {
				v = jdh.GetRasterSource(strings.TrimSpace(kv.Value[0]))
			}
			if ras.Source == v {
				continue
			}
		case jdh.RDisTaxon:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if len(v) == 0 {
				continue
			}
			if ras.Taxon == v {
				continue
			}
			tax, ok := d.taxId[v]
			if !ok {
				if !d.db.t.isInDB(v) {
					continue
				}
				tax = &rasTaxon{
					id:   v,
					rsLs: list.New(),
				}
				tax.elem = d.taxLs.PushBack(tax)
				d.taxId[tax.id] = tax
			}
			oldtax := rd.taxon
			oldtax.rsLs.Remove(rd.elem)
			rd.elem = tax.rsLs.PushBack(rd)
			rd.taxon = tax
			rd.data.Taxon = tax.id
			if oldtax.rsLs.Len() == 0 {
				oldtax.rsLs = nil
				d.taxLs.Remove(oldtax.elem)
				oldtax.elem = nil
				delete(d.taxId, oldtax.id)
			}
		case jdh.KeyComment:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if ras.Comment == v {
				continue
			}
			ras.Comment = v
		case jdh.KeyExtern:
			ok := false
			for _, v := range kv.Value {
				v = strings.TrimSpace(v)
				if len(v) == 0 {
					continue
				}
				serv, ext, err := jdh.ParseExtern(v)
				if err != nil {
					return err
				}
				if len(ext) == 0 {
					if !d.delExtern(rd, serv) {
						continue
					}
					ok = true
					continue
				}
				if d.addExtern(rd, v) != nil {
					continue
				}
				ok = true
			}
			if !ok {
				continue
			}
		case jdh.KeyReference:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if ras.Reference == v {
				continue
			}
			ras.Reference = v
		default:
			continue
		}
		d.changed = true
	}
	return nil
}
Пример #13
0
// Set sets a value of a taxon in the taxonomy.
func (t *taxonomy) set(vals []jdh.KeyValue) error {
	id := ""
	for _, kv := range vals {
		if len(kv.Value) == 0 {
			continue
		}
		if kv.Key == jdh.KeyId {
			id = kv.Value[0]
			break
		}
	}
	if len(id) == 0 {
		return errors.New("taxon without identification")
	}
	tx, ok := t.ids[id]
	if !ok {
		return nil
	}
	tax := tx.data
	for _, kv := range vals {
		switch kv.Key {
		case jdh.KeyComment:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if tax.Comment == v {
				continue
			}
			tax.Comment = v
		case jdh.KeyExtern:
			ok := false
			for _, v := range kv.Value {
				v := strings.TrimSpace(v)
				if len(v) == 0 {
					continue
				}
				serv, ext, err := jdh.ParseExtern(v)
				if err != nil {
					continue
				}
				if len(ext) == 0 {
					if !t.delExtern(tx, serv) {
						continue
					}
					ok = true
					continue
				}
				if t.addExtern(tx, v) != nil {
					continue
				}
				ok = true
			}
			if !ok {
				continue
			}
		case jdh.TaxAuthority:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if tax.Authority == v {
				continue
			}
			tax.Authority = v
		case jdh.TaxName:
			nm := ""
			if len(kv.Value) > 0 {
				nm = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if len(nm) == 0 {
				return fmt.Errorf("new name for %s undefined", tx.data.Name)
			}
			if tax.Name == nm {
				continue
			}
			// remove the old name
			nmLow := strings.ToLower(tax.Name)
			v := t.names.Lookup(nmLow).([]*taxon)
			v = delTaxFromList(v, tx)
			if len(v) > 0 {
				t.names.Set(nmLow, v)
			} else {
				t.names.Delete(nmLow)
			}
			tax.Name = nm
			// add the new name
			nmLow = strings.ToLower(tax.Name)
			vi := t.names.Lookup(nmLow)
			var txLs []*taxon
			if vi == nil {
				txLs = []*taxon{tx}
			} else {
				txLs = append(vi.([]*taxon), tx)
			}
			t.names.Set(nmLow, txLs)
		case jdh.TaxParent:
			p := t.root
			if len(kv.Value) > 0 {
				v := strings.TrimSpace(kv.Value[0])
				if len(v) == 0 {
					continue
				}
				var ok bool
				p, ok = t.ids[v]
				if !ok {
					return fmt.Errorf("new parent [%s] for taxon %s not in database", kv.Value, tax.Name)
				}
			}
			if tx.parent == p {
				continue
			}
			if (!tax.IsValid) && (p == t.root) {
				return fmt.Errorf("taxon %s is a synonym, it requires a parent", tax.Name)
			}
			if (p != t.root) && (!p.data.IsValid) {
				return fmt.Errorf("new parent [%s] for taxon %s is a synonym", p.data.Name, tax.Name)
			}
			if !p.isDescValid(tax.Rank, tax.IsValid) {
				return fmt.Errorf("taxon %s rank incompatible with new parent [%s] hierarchy", tax.Name, p.data.Name)
			}
			tx.parent.childs = delTaxFromList(tx.parent.childs, tx)
			p.childs = append(p.childs, tx)
			tx.parent = p
			if p == t.root {
				tax.Parent = ""
			} else {
				tax.Parent = p.data.Id
			}
		case jdh.TaxRank:
			v := jdh.Unranked
			if len(kv.Value) > 0 {
				v = jdh.GetRank(strings.TrimSpace(kv.Value[0]))
			}
			if tax.Rank == v {
				continue
			}
			if !tx.parent.isDescValid(v, tax.IsValid) {
				return fmt.Errorf("new rank [%s] for taxon %s incompatible with taxonomy hierarchy", v, tax.Name)
			}
			tax.Rank = v
		case jdh.TaxSynonym:
			p := tx.parent
			if len(kv.Value) > 0 {
				v := strings.TrimSpace(kv.Value[0])
				if len(v) == 0 {
					continue
				}
				var ok bool
				p, ok = t.ids[v]
				if !ok {
					return fmt.Errorf("new parent [%s] for taxon %s not in database", kv.Value, tax.Name)
				}
			}
			if (p == tx.parent) && (!tax.IsValid) {
				continue
			}
			if p == t.root {
				return fmt.Errorf("taxon %s can not be a synonym: no new parent defined", tax.Name)
			}
			if p != tx.parent {
				if !p.isDescValid(tax.Rank, false) {
					return fmt.Errorf("taxon %s rank incompatible with new parent [%s] hierarchy", tax.Name, p.data.Name)
				}
				tx.parent.childs = delTaxFromList(tx.parent.childs, tx)
				tx.parent = p
				tax.Parent = p.data.Id
				p.childs = append(p.childs, tx)
			}
			for _, d := range tx.childs {
				d.parent = p
				d.data.Parent = p.data.Id
				p.childs = append(p.childs, d)
			}
			tx.childs = nil
			tax.IsValid = false
		case jdh.TaxValid:
			if tax.IsValid {
				continue
			}
			tx.parent.childs = delTaxFromList(tx.parent.childs, tx)
			tx.parent = tx.parent.parent
			tx.data.Parent = tx.parent.data.Id
			tx.parent.childs = append(tx.parent.childs, tx)
			tax.IsValid = true
		default:
			continue
		}
		t.changed = true
	}
	return nil
}
Пример #14
0
// Set sets one or more values of a dataset element.
func (d *datasets) set(vals []jdh.KeyValue) error {
	id := ""
	for _, kv := range vals {
		if len(kv.Value) == 0 {
			continue
		}
		if kv.Key == jdh.KeyId {
			id = kv.Value[0]
			break
		}
	}
	if len(id) == 0 {
		return errors.New("dataset without identification")
	}
	sd, ok := d.ids[id]
	if !ok {
		return nil
	}
	set := sd.data
	for _, kv := range vals {
		switch kv.Key {
		case jdh.KeyComment:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if set.Comment == v {
				continue
			}
			set.Comment = v
		case jdh.KeyExtern:
			ok := false
			for _, v := range kv.Value {
				v = strings.TrimSpace(v)
				if len(v) == 0 {
					continue
				}
				serv, ext, err := jdh.ParseExtern(v)
				if err != nil {
					continue
				}
				if len(ext) == 0 {
					if !d.delExtern(sd, serv) {
						continue
					}
					ok = true
					continue
				}
				if d.addExtern(sd, v) != nil {
					continue
				}
				ok = true
			}
			if !ok {
				continue
			}
		case jdh.DataCitation:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if set.Citation == v {
				continue
			}
			set.Citation = v
		case jdh.DataLicense:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if set.License == v {
				continue
			}
			set.License = v
		case jdh.DataTitle:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if set.Title == v {
				continue
			}
			set.Title = v
		case jdh.DataUrl:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if len(v) == 0 {
				if len(set.Url) == 0 {
					continue
				}
				set.Url = ""
				break
			}
			u, err := url.Parse(v)
			if err != nil {
				continue
			}
			if set.Url == u.String() {
				continue
			}
			set.Url = u.String()
		}
		d.changed = true
	}
	return nil
}
Пример #15
0
// Validate validates that an specimen is valid in the database, and set
// some canonical values. It returns an error if the specimen is not valid.
func (s *specimens) validate(spe *jdh.Specimen) error {
	spe.Id = strings.TrimSpace(spe.Id)
	spe.Taxon = strings.TrimSpace(spe.Taxon)
	if (len(spe.Id) == 0) || (len(spe.Taxon) == 0) {
		return errors.New("specimen without identification")
	}
	if _, ok := s.ids[spe.Id]; ok {
		return fmt.Errorf("specimen id %s already in use", spe.Id)
	}
	spe.Catalog = strings.TrimSpace(spe.Catalog)
	if len(spe.Catalog) > 0 {
		if _, ok := s.ids[spe.Catalog]; ok {
			return fmt.Errorf("specimen catalog code %s already in use", spe.Catalog)
		}
	}
	if !s.db.t.isInDB(spe.Taxon) {
		return fmt.Errorf("taxon %s [associated with specimen %s] not in database", spe.Taxon, spe.Id)
	}
	if !spe.Geography.IsValid() {
		spe.Geography = geography.Location{}
	}
	if !spe.Georef.IsValid() {
		spe.Georef = geography.InvalidGeoref()
	} else if (spe.Georef.Point.Lon == 0) || (spe.Georef.Point.Lon == 1) || (spe.Georef.Point.Lat == 0) || (spe.Georef.Point.Lat == 1) {
		spe.Georef = geography.InvalidGeoref()
	}

	if spe.Basis > jdh.Remote {
		spe.Basis = jdh.UnknownBasis
	}
	spe.Reference = strings.TrimSpace(spe.Reference)
	spe.Determiner = strings.Join(strings.Fields(spe.Determiner), " ")
	spe.Collector = strings.Join(strings.Fields(spe.Collector), " ")
	spe.Dataset = strings.TrimSpace(spe.Dataset)
	if len(spe.Dataset) > 0 {
		if !s.db.d.isInDB(spe.Dataset) {
			spe.Dataset = ""
		}
	}
	ext := spe.Extern
	spe.Extern = nil
	for _, e := range ext {
		serv, id, err := jdh.ParseExtern(e)
		if err != nil {
			continue
		}
		if len(id) == 0 {
			continue
		}
		add := true
		for _, ex := range spe.Extern {
			if strings.HasPrefix(ex, serv) {
				add = false
				break
			}
		}
		if !add {
			continue
		}
		if _, ok := s.ids[e]; !ok {
			spe.Extern = append(spe.Extern, e)
		}
	}
	return nil
}
Пример #16
0
// Set sets a value of an specimen in the database.
func (s *specimens) set(vals []jdh.KeyValue) error {
	id := ""
	for _, kv := range vals {
		if len(kv.Value) == 0 {
			continue
		}
		if kv.Key == jdh.KeyId {
			id = kv.Value[0]
			break
		}
	}
	if len(id) == 0 {
		return errors.New("specimen without identification")
	}
	sp, ok := s.ids[id]
	if !ok {
		return nil
	}
	spe := sp.data
	for _, kv := range vals {
		switch kv.Key {
		case jdh.SpeBasis:
			v := jdh.UnknownBasis
			if len(kv.Value) > 0 {
				v = jdh.GetBasisOfRecord(strings.TrimSpace(kv.Value[0]))
			}
			if spe.Basis == v {
				continue
			}
			spe.Basis = v
		case jdh.SpeCatalog:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if spe.Catalog == v {
				continue
			}
			if len(v) > 0 {
				if _, ok := s.ids[v]; ok {
					return fmt.Errorf("specimen catalog code %s already in use", kv.Value)
				}
			}
			if len(spe.Catalog) > 0 {
				delete(s.ids, spe.Catalog)
			}
			spe.Catalog = v
			if len(v) > 0 {
				s.ids[v] = sp
			}
		case jdh.SpeCollector:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if spe.Collector == v {
				continue
			}
			spe.Collector = v
		case jdh.SpeDataset:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if spe.Dataset == v {
				continue
			}
			if len(v) > 0 {
				if !s.db.d.isInDB(v) {
					continue
				}
			}
			spe.Dataset = v
		case jdh.SpeDate:
			if len(kv.Value) > 0 {
				v := strings.TrimSpace(kv.Value[0])
				t, err := time.Parse(jdh.Iso8601, v)
				if err != nil {
					return err
				}
				if spe.Date.Equal(t) {
					continue
				}
				spe.Date = t
				break
			}
			if spe.Date.IsZero() {
				continue
			}
			spe.Date = time.Time{}
		case jdh.SpeDeterminer:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if spe.Determiner == v {
				continue
			}
			spe.Determiner = v
		case jdh.SpeLocality:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if spe.Locality == v {
				continue
			}
			spe.Locality = v
		case jdh.SpeTaxon:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if len(v) == 0 {
				continue
			}
			if spe.Taxon == v {
				continue
			}
			tax, ok := s.taxId[v]
			if !ok {
				if !s.db.t.isInDB(v) {
					continue
				}
				tax = &speTaxon{
					id:    v,
					specs: list.New(),
				}
				tax.elem = s.taxLs.PushBack(tax)
				s.taxId[tax.id] = tax
			}
			oldtax := sp.taxon
			oldtax.specs.Remove(sp.elem)
			sp.elem = tax.specs.PushBack(sp)
			sp.taxon = tax
			sp.data.Taxon = tax.id
			if oldtax.specs.Len() == 0 {
				oldtax.specs = nil
				s.taxLs.Remove(oldtax.elem)
				oldtax.elem = nil
				delete(s.taxId, oldtax.id)
			}
		case jdh.GeoCountry:
			v := geography.Country("")
			if len(kv.Value) > 0 {
				v = geography.GetCountry(strings.Join(strings.Fields(kv.Value[0]), " "))
			}
			if spe.Geography.Country == v {
				continue
			}
			spe.Geography.Country = v
		case jdh.GeoCounty:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if spe.Geography.County == v {
				continue
			}
			spe.Geography.County = v
		case jdh.GeoLonLat:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if len(v) == 0 {
				if !spe.Georef.IsValid() {
					continue
				}
				spe.Georef = geography.InvalidGeoref()
				break
			}
			coor := strings.Split(v, ",")
			if len(coor) != 2 {
				return errors.New("invalid geographic coordinate values")
			}
			lon, err := strconv.ParseFloat(coor[0], 64)
			if err != nil {
				return err
			}
			lat, err := strconv.ParseFloat(coor[1], 64)
			if err != nil {
				return err
			}
			if (lon == 0) || (lon == 1) || (lat == 0) || (lat == 1) {
				return errors.New("invalid geographic coordinate values")
			}
			if (!geography.IsLon(lon)) || (!geography.IsLat(lat)) {
				return errors.New("invalid geographic coordinate values")
			}
			spe.Georef.Point = geography.Point{Lon: lon, Lat: lat}
		case jdh.GeoSource:
			if !spe.Georef.IsValid() {
				continue
			}
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if spe.Georef.Source == v {
				continue
			}
			spe.Georef.Source = v
		case jdh.GeoState:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if spe.Geography.State == v {
				continue
			}
			spe.Geography.State = v
		case jdh.GeoUncertainty:
			if !spe.Georef.IsValid() {
				continue
			}
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			un64, err := strconv.ParseUint(v, 10, 0)
			if err != nil {
				return err
			}
			un := uint(un64)
			if un == spe.Georef.Uncertainty {
				continue
			}
			spe.Georef.Uncertainty = un
		case jdh.GeoValidation:
			if !spe.Georef.IsValid() {
				continue
			}
			v := ""
			if len(kv.Value) > 0 {
				v = strings.Join(strings.Fields(kv.Value[0]), " ")
			}
			if spe.Georef.Validation == v {
				continue
			}
			spe.Georef.Validation = v
		case jdh.KeyComment:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if spe.Comment == v {
				continue
			}
			spe.Comment = v
		case jdh.KeyExtern:
			ok := false
			for _, v := range kv.Value {
				v = strings.TrimSpace(v)
				if len(v) == 0 {
					continue
				}
				serv, ext, err := jdh.ParseExtern(v)
				if err != nil {
					return err
				}
				if len(ext) == 0 {
					if !s.delExtern(sp, serv) {
						continue
					}
					ok = true
					continue
				}
				if s.addExtern(sp, v) != nil {
					continue
				}
				ok = true
			}
			if !ok {
				continue
			}
		case jdh.KeyReference:
			v := ""
			if len(kv.Value) > 0 {
				v = strings.TrimSpace(kv.Value[0])
			}
			if spe.Reference == v {
				continue
			}
			spe.Reference = v
		default:
			continue
		}
		s.changed = true
	}
	return nil
}