2015-06-25 22:36:58 -04:00
package main
2015-06-27 08:31:38 -04:00
import (
"encoding/json"
2016-10-16 08:19:11 -04:00
"errors"
2017-02-19 07:50:36 -05:00
"gopkg.in/gorp.v1"
2015-06-27 08:31:38 -04:00
"log"
"net/http"
2016-10-16 08:19:11 -04:00
"net/url"
2016-10-26 06:58:14 -04:00
"strconv"
2016-10-16 08:19:11 -04:00
"strings"
2015-06-27 08:31:38 -04:00
)
2015-06-25 22:36:58 -04:00
const (
2016-02-02 21:46:27 -05:00
Currency int64 = 1
Stock = 2
2015-06-25 22:36:58 -04:00
)
2016-10-16 08:19:11 -04:00
func GetSecurityType ( typestring string ) int64 {
if strings . EqualFold ( typestring , "currency" ) {
return Currency
} else if strings . EqualFold ( typestring , "stock" ) {
return Stock
} else {
return 0
}
}
2015-06-25 22:36:58 -04:00
type Security struct {
2016-01-13 20:44:58 -05:00
SecurityId int64
2016-10-16 08:19:11 -04:00
UserId int64
2016-01-13 20:44:58 -05:00
Name string
Description string
Symbol string
2015-06-25 22:36:58 -04:00
// Number of decimal digits (to the right of the decimal point) this
// security is precise to
2015-08-21 06:54:17 -04:00
Precision int
2015-07-04 08:23:57 -04:00
Type int64
2017-06-21 21:25:38 -04:00
// AlternateId is CUSIP for Type=Stock, ISO4217 for Type=Currency
2016-02-02 21:46:27 -05:00
AlternateId string
2015-06-25 22:36:58 -04:00
}
2015-06-27 08:31:38 -04:00
type SecurityList struct {
Securities * [ ] * Security ` json:"securities" `
}
2016-10-16 08:19:11 -04:00
func ( s * Security ) Read ( json_str string ) error {
dec := json . NewDecoder ( strings . NewReader ( json_str ) )
return dec . Decode ( s )
}
2016-01-13 20:44:58 -05:00
2016-10-16 08:19:11 -04:00
func ( s * Security ) Write ( w http . ResponseWriter ) error {
enc := json . NewEncoder ( w )
return enc . Encode ( s )
2015-06-27 08:31:38 -04:00
}
2016-10-16 08:19:11 -04:00
func ( sl * SecurityList ) Write ( w http . ResponseWriter ) error {
enc := json . NewEncoder ( w )
return enc . Encode ( sl )
}
2015-06-27 08:31:38 -04:00
2016-10-26 06:58:14 -04:00
func SearchSecurityTemplates ( search string , _type int64 , limit int64 ) [ ] * Security {
2016-10-16 08:19:11 -04:00
upperSearch := strings . ToUpper ( search )
var results [ ] * Security
for i , security := range SecurityTemplates {
if strings . Contains ( strings . ToUpper ( security . Name ) , upperSearch ) ||
strings . Contains ( strings . ToUpper ( security . Description ) , upperSearch ) ||
strings . Contains ( strings . ToUpper ( security . Symbol ) , upperSearch ) {
if _type == 0 || _type == security . Type {
results = append ( results , & SecurityTemplates [ i ] )
2016-10-26 06:58:14 -04:00
if limit != - 1 && int64 ( len ( results ) ) >= limit {
break
}
2016-10-16 08:19:11 -04:00
}
}
2015-06-27 08:31:38 -04:00
}
2016-10-16 08:19:11 -04:00
return results
2015-06-27 08:31:38 -04:00
}
2017-02-19 07:50:36 -05:00
func FindSecurityTemplate ( name string , _type int64 ) * Security {
for _ , security := range SecurityTemplates {
if name == security . Name && _type == security . Type {
return & security
}
}
return nil
}
2017-06-21 21:25:38 -04:00
func FindCurrencyTemplate ( iso4217 int64 ) * Security {
iso4217string := strconv . FormatInt ( iso4217 , 10 )
for _ , security := range SecurityTemplates {
if security . Type == Currency && security . AlternateId == iso4217string {
return & security
}
}
return nil
}
2016-10-16 08:19:11 -04:00
func GetSecurity ( securityid int64 , userid int64 ) ( * Security , error ) {
var s Security
err := DB . SelectOne ( & s , "SELECT * from securities where UserId=? AND SecurityId=?" , userid , securityid )
if err != nil {
return nil , err
2015-06-27 08:31:38 -04:00
}
2016-10-16 08:19:11 -04:00
return & s , nil
2015-06-27 08:31:38 -04:00
}
2017-06-04 16:01:42 -04:00
func GetSecurityTx ( transaction * gorp . Transaction , securityid int64 , userid int64 ) ( * Security , error ) {
var s Security
err := transaction . SelectOne ( & s , "SELECT * from securities where UserId=? AND SecurityId=?" , userid , securityid )
if err != nil {
return nil , err
}
return & s , nil
}
2016-10-16 08:19:11 -04:00
func GetSecurities ( userid int64 ) ( * [ ] * Security , error ) {
var securities [ ] * Security
_ , err := DB . Select ( & securities , "SELECT * from securities where UserId=?" , userid )
if err != nil {
return nil , err
2016-02-02 21:46:27 -05:00
}
2016-10-16 08:19:11 -04:00
return & securities , nil
2016-02-15 11:28:44 -05:00
}
2016-10-16 08:19:11 -04:00
func InsertSecurity ( s * Security ) error {
err := DB . Insert ( s )
if err != nil {
return err
}
return nil
}
2017-02-19 07:50:36 -05:00
func InsertSecurityTx ( transaction * gorp . Transaction , s * Security ) error {
err := transaction . Insert ( s )
if err != nil {
return err
}
return nil
}
2016-10-16 08:19:11 -04:00
func UpdateSecurity ( s * Security ) error {
transaction , err := DB . Begin ( )
if err != nil {
return err
}
count , err := transaction . Update ( s )
if err != nil {
transaction . Rollback ( )
return err
}
if count != 1 {
transaction . Rollback ( )
return errors . New ( "Updated more than one security" )
}
err = transaction . Commit ( )
if err != nil {
transaction . Rollback ( )
return err
2016-02-15 11:28:44 -05:00
}
2016-10-16 08:19:11 -04:00
return nil
2016-02-02 21:46:27 -05:00
}
2016-10-16 08:19:11 -04:00
func DeleteSecurity ( s * Security ) error {
transaction , err := DB . Begin ( )
if err != nil {
return err
}
// First, ensure no accounts are using this security
accounts , err := transaction . SelectInt ( "SELECT count(*) from accounts where UserId=? and SecurityId=?" , s . UserId , s . SecurityId )
if accounts != 0 {
transaction . Rollback ( )
return errors . New ( "One or more accounts still use this security" )
}
2017-06-21 21:25:38 -04:00
user , err := GetUserTx ( transaction , s . UserId )
if err != nil {
transaction . Rollback ( )
return err
} else if user . DefaultCurrency == s . SecurityId {
transaction . Rollback ( )
return errors . New ( "Cannot delete security which is user's default currency" )
}
2016-10-16 08:19:11 -04:00
count , err := transaction . Delete ( s )
if err != nil {
transaction . Rollback ( )
return err
}
if count != 1 {
transaction . Rollback ( )
return errors . New ( "Deleted more than one security" )
}
err = transaction . Commit ( )
if err != nil {
transaction . Rollback ( )
return err
}
return nil
2015-06-27 08:31:38 -04:00
}
2017-06-04 16:01:42 -04:00
func ImportGetCreateSecurity ( transaction * gorp . Transaction , userid int64 , security * Security ) ( * Security , error ) {
security . UserId = userid
2017-02-19 07:50:36 -05:00
if len ( security . AlternateId ) == 0 {
// Always create a new local security if we can't match on the AlternateId
err := InsertSecurityTx ( transaction , security )
if err != nil {
return nil , err
}
return security , nil
}
var securities [ ] * Security
2017-06-04 16:01:42 -04:00
_ , err := transaction . Select ( & securities , "SELECT * from securities where UserId=? AND Type=? AND AlternateId=? AND Precision=?" , userid , security . Type , security . AlternateId , security . Precision )
2017-02-19 07:50:36 -05:00
if err != nil {
return nil , err
}
// First try to find a case insensitive match on the name or symbol
upperName := strings . ToUpper ( security . Name )
upperSymbol := strings . ToUpper ( security . Symbol )
for _ , s := range securities {
if ( len ( s . Name ) > 0 && strings . ToUpper ( s . Name ) == upperName ) ||
( len ( s . Symbol ) > 0 && strings . ToUpper ( s . Symbol ) == upperSymbol ) {
return s , nil
}
}
// if strings.Contains(strings.ToUpper(security.Name), upperSearch) ||
// Try to find a partial string match on the name or symbol
for _ , s := range securities {
sUpperName := strings . ToUpper ( s . Name )
sUpperSymbol := strings . ToUpper ( s . Symbol )
if ( len ( upperName ) > 0 && len ( s . Name ) > 0 && ( strings . Contains ( upperName , sUpperName ) || strings . Contains ( sUpperName , upperName ) ) ) ||
( len ( upperSymbol ) > 0 && len ( s . Symbol ) > 0 && ( strings . Contains ( upperSymbol , sUpperSymbol ) || strings . Contains ( sUpperSymbol , upperSymbol ) ) ) {
return s , nil
}
}
// Give up and return the first security in the list
if len ( securities ) > 0 {
return securities [ 0 ] , nil
}
// If there wasn't even one security in the list, make a new one
err = InsertSecurityTx ( transaction , security )
if err != nil {
return nil , err
}
return security , nil
}
2015-06-27 08:31:38 -04:00
func SecurityHandler ( w http . ResponseWriter , r * http . Request ) {
2016-10-16 08:19:11 -04:00
user , err := GetUserFromSession ( r )
if err != nil {
WriteError ( w , 1 /*Not Signed In*/ )
return
}
if r . Method == "POST" {
security_json := r . PostFormValue ( "security" )
if security_json == "" {
WriteError ( w , 3 /*Invalid Request*/ )
return
}
var security Security
err := security . Read ( security_json )
if err != nil {
WriteError ( w , 3 /*Invalid Request*/ )
return
}
security . SecurityId = - 1
security . UserId = user . UserId
err = InsertSecurity ( & security )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
w . WriteHeader ( 201 /*Created*/ )
err = security . Write ( w )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
} else if r . Method == "GET" {
var securityid int64
n , err := GetURLPieces ( r . URL . Path , "/security/%d" , & securityid )
if err != nil || n != 1 {
//Return all securities
var sl SecurityList
securities , err := GetSecurities ( user . UserId )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
sl . Securities = securities
err = ( & sl ) . Write ( w )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
} else {
security , err := GetSecurity ( securityid , user . UserId )
if err != nil {
WriteError ( w , 3 /*Invalid Request*/ )
return
}
err = security . Write ( w )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
}
} else {
2015-06-27 08:31:38 -04:00
securityid , err := GetURLID ( r . URL . Path )
2016-10-16 08:19:11 -04:00
if err != nil {
WriteError ( w , 3 /*Invalid Request*/ )
return
}
if r . Method == "PUT" {
security_json := r . PostFormValue ( "security" )
if security_json == "" {
WriteError ( w , 3 /*Invalid Request*/ )
return
}
var security Security
err := security . Read ( security_json )
if err != nil || security . SecurityId != securityid {
2015-06-27 08:31:38 -04:00
WriteError ( w , 3 /*Invalid Request*/ )
return
}
2016-10-16 08:19:11 -04:00
security . UserId = user . UserId
err = UpdateSecurity ( & security )
2015-06-27 08:31:38 -04:00
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
2016-10-16 08:19:11 -04:00
err = security . Write ( w )
2015-06-27 08:31:38 -04:00
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
2016-10-16 08:19:11 -04:00
} else if r . Method == "DELETE" {
security , err := GetSecurity ( securityid , user . UserId )
if err != nil {
WriteError ( w , 3 /*Invalid Request*/ )
return
}
err = DeleteSecurity ( security )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
WriteSuccess ( w )
}
}
}
func SecurityTemplateHandler ( w http . ResponseWriter , r * http . Request ) {
if r . Method == "GET" {
var sl SecurityList
query , _ := url . ParseQuery ( r . URL . RawQuery )
2016-10-26 06:58:14 -04:00
var limit int64 = - 1
2016-10-16 08:19:11 -04:00
search := query . Get ( "search" )
_type := GetSecurityType ( query . Get ( "type" ) )
2016-10-26 06:58:14 -04:00
limitstring := query . Get ( "limit" )
if limitstring != "" {
limitint , err := strconv . ParseInt ( limitstring , 10 , 0 )
if err != nil {
WriteError ( w , 3 /*Invalid Request*/ )
return
}
limit = limitint
}
securities := SearchSecurityTemplates ( search , _type , limit )
2016-10-16 08:19:11 -04:00
sl . Securities = & securities
err := ( & sl ) . Write ( w )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
2015-06-27 08:31:38 -04:00
}
} else {
WriteError ( w , 3 /*Invalid Request*/ )
}
}