-
Notifications
You must be signed in to change notification settings - Fork 2
/
psychonautwiki.go
290 lines (255 loc) · 8.3 KB
/
psychonautwiki.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
package drugdose
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
"time"
"github.com/hasura/go-graphql-client"
"database/sql"
// MySQL driver needed for sql module
_ "github.com/go-sql-driver/mysql"
// SQLite driver needed for sql module
_ "modernc.org/sqlite"
)
type PsychonautwikiSubstance []struct {
Name string
Roas []struct {
Name string
Dose struct {
Units string
Threshold float64
Light struct {
Min float64
Max float64
}
Common struct {
Min float64
Max float64
}
Strong struct {
Min float64
Max float64
}
}
Duration struct {
Onset struct {
Min float64
Max float64
Units string
}
Comeup struct {
Min float64
Max float64
Units string
}
Peak struct {
Min float64
Max float64
Units string
}
Offset struct {
Min float64
Max float64
Units string
}
Total struct {
Min float64
Max float64
Units string
}
}
}
}
// Used to initialise the GraphQL struct, using the source address from
// the drugdose Config struct.
//
// returns the GraphQL struct used with github.com/hasura/go-graphql-client
func (cfg *Config) InitGraphqlClient() (error, graphql.Client) {
const printN string = "InitGraphqlClient()"
client := graphql.Client{}
if !cfg.AutoFetch {
printNameVerbose(cfg.VerbosePrinting, printN, "Automatic fetching is disabled, returning.")
return nil, client
}
var proxy func(*http.Request) (*url.URL, error) = nil
if cfg.ProxyURL != "" && cfg.ProxyURL != "none" {
goturl, err := url.Parse(cfg.ProxyURL)
if err != nil {
err = fmt.Errorf("%s%w", sprintName(printN), err)
return err, client
}
proxy = http.ProxyURL(goturl)
}
var CustomTransport http.RoundTripper = &http.Transport{
Proxy: proxy,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
httpClient := http.Client{
Transport: CustomTransport,
}
gotsrcData := GetSourceData()
if gotsrcData == nil {
return errors.New(sprintName(printN, "GetSourceData() returned nil, returning.")), client
}
api := gotsrcData[cfg.UseSource].API_ADDRESS
apiURL := "https://" + api
client_new := graphql.NewClient(apiURL, &httpClient)
return nil, *client_new
}
// FetchPsyWiki gets information from Psychonautwiki about a given substance
// and stores it in the local info table. The table is determined by the
// source chosen in the Config struct. The name of the table is the same as the
// name of the source, in this case "psychonautwiki".
//
// db - open database connection
//
// ctx - context to be passed to sql queries
//
// errChannel - the gorouting channel which returns the errors
// (set to nil if function doesn't need to be concurrent)
//
// drugname - the substance to get information about
//
// client - the initialised structure for the graphql client,
// best done using InitGraphqlClient(), but can be done manually if needed
//
// username - the user that requested the fetch request
func (cfg *Config) FetchPsyWiki(db *sql.DB, ctx context.Context,
errChannel chan<- ErrorInfo, drugname string, client graphql.Client,
username string) ErrorInfo {
const printN string = "FetchPsyWiki()"
tempErrInfo := ErrorInfo{
Err: nil,
Action: ActionFetchFromPsychonautWiki,
Username: username,
}
if !cfg.AutoFetch {
printNameVerbose(cfg.VerbosePrinting, printN, "Automatic fetching is disabled, returning.")
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
drugname = cfg.MatchAndReplace(db, ctx, drugname, NameTypeSubstance)
ret := checkIfExistsDB(db, ctx,
"drugName",
"psychonautwiki",
cfg.DBDriver,
cfg.DBSettings[cfg.DBDriver].Path,
nil,
drugname)
if ret {
printNameVerbose(cfg.VerbosePrinting, printN, "Drug already in DB, returning. No need to fetch anything from Psychonautwiki.")
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
xtraTxt := ""
if cfg.ProxyURL != "" && cfg.ProxyURL != "none" {
xtraTxt += " ; configured proxy: " + fmt.Sprintf("%q", cfg.ProxyURL)
}
printNameF(printN, "Fetching from source: %q ; substance: %q%s\n", cfg.UseSource, drugname, xtraTxt)
// This is the graphql query for Psychonautwiki.
// The way it works is, the full query is generated
// using the PsychonautwikiSubstance struct.
var query struct {
PsychonautwikiSubstance `graphql:"substances(query: $dn)"`
}
// Since the query has to be a string, the module has provided
// an argument allowing to map a variable to the string.
variables := map[string]interface{}{
"dn": drugname,
}
err := client.Query(ctx, &query, variables)
if err != nil {
tempErrInfo.Err = fmt.Errorf("%s%w", sprintName(printN, "client.Query(): "), err)
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
InfoDrug := []DrugInfo{}
if len(query.PsychonautwikiSubstance) != 0 {
subs := query.PsychonautwikiSubstance
for i := 0; i < len(subs); i++ {
if len(subs[i].Roas) != 0 {
for o := 0; o < len(subs[i].Roas); o++ {
printNameVerbose(cfg.VerbosePrinting, printN, "From source:", cfg.UseSource, "; Substance:", subs[i].Name,
"; Route:", subs[i].Roas[o])
tempInfoDrug := DrugInfo{}
tempInfoDrug.DrugName = subs[i].Name
tempInfoDrug.DrugRoute = subs[i].Roas[o].Name
tempInfoDrug.Threshold = float32(subs[i].Roas[o].Dose.Threshold)
tempInfoDrug.LowDoseMin = float32(subs[i].Roas[o].Dose.Light.Min)
tempInfoDrug.LowDoseMax = float32(subs[i].Roas[o].Dose.Light.Max)
tempInfoDrug.MediumDoseMin = float32(subs[i].Roas[o].Dose.Common.Min)
tempInfoDrug.MediumDoseMax = float32(subs[i].Roas[o].Dose.Common.Max)
tempInfoDrug.HighDoseMin = float32(subs[i].Roas[o].Dose.Strong.Min)
tempInfoDrug.HighDoseMax = float32(subs[i].Roas[o].Dose.Strong.Max)
tempInfoDrug.DoseUnits = subs[i].Roas[o].Dose.Units
tempInfoDrug.OnsetMin = float32(subs[i].Roas[o].Duration.Onset.Min)
tempInfoDrug.OnsetMax = float32(subs[i].Roas[o].Duration.Onset.Max)
tempInfoDrug.OnsetUnits = subs[i].Roas[o].Duration.Onset.Units
tempInfoDrug.ComeUpMin = float32(subs[i].Roas[o].Duration.Comeup.Min)
tempInfoDrug.ComeUpMax = float32(subs[i].Roas[o].Duration.Comeup.Max)
tempInfoDrug.ComeUpUnits = subs[i].Roas[o].Duration.Comeup.Units
tempInfoDrug.PeakMin = float32(subs[i].Roas[o].Duration.Peak.Min)
tempInfoDrug.PeakMax = float32(subs[i].Roas[o].Duration.Peak.Max)
tempInfoDrug.PeakUnits = subs[i].Roas[o].Duration.Peak.Units
tempInfoDrug.OffsetMin = float32(subs[i].Roas[o].Duration.Offset.Min)
tempInfoDrug.OffsetMax = float32(subs[i].Roas[o].Duration.Offset.Max)
tempInfoDrug.OffsetUnits = subs[i].Roas[o].Duration.Offset.Units
tempInfoDrug.TotalDurMin = float32(subs[i].Roas[o].Duration.Total.Min)
tempInfoDrug.TotalDurMax = float32(subs[i].Roas[o].Duration.Total.Max)
tempInfoDrug.TotalDurUnits = subs[i].Roas[o].Duration.Total.Units
InfoDrug = append(InfoDrug, tempInfoDrug)
}
} else {
tempErrInfo.Err = fmt.Errorf("%s%w: %+v", sprintName(printN), NoROAForSubs, subs[i])
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
}
if len(InfoDrug) != 0 {
errInfo := cfg.AddToInfoTable(db, ctx, nil, InfoDrug, username)
err := errInfo.Err
if err != nil {
tempErrInfo.Err = fmt.Errorf("%s%w", sprintName(printN), err)
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
} else {
tempErrInfo.Err = fmt.Errorf("%s%w", sprintName(printN), StructSliceEmpty)
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
} else {
tempErrInfo.Err = fmt.Errorf("%s%w", sprintName(printN), PsychonautwikiEmptyResp)
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
if errChannel != nil {
errChannel <- tempErrInfo
}
return tempErrInfo
}
// NoROAForSubs is the error returned when no routes of administration is
// returned for a substance could be retrieved from a source.
var NoROAForSubs error = errors.New("no route of administration for substance")
var StructSliceEmpty error = errors.New("struct slice is empty, nothing added to DB")
var PsychonautwikiEmptyResp error = errors.New("Psychonautwiki returned nothing")