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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
// Phelpsify is a tool to help assign prayer codes to prayers.
// It requests prayers from bahaiprayers.net via the API url.
// It reads already assigned prayer codes from rel/code.list.
// It reads the conversion from number to language code from rel/lang.csv.
// It writes the new prayer codes to rel/code.list.
// rel/code.list is structured as prayer code, comma, prayer ids from bahaiprayers.net all separated by commas per line.
// rel/lang.csv is a csv file with header id,iso,iso_type,name,english,flag_link,rtl.
// The tool is a command line tool that first asks which languages you want to complete.
// It then presents you a random prayer from those languages that doesn't have
// a prayer code yet. It will then help you find the prayer among the prayers that already have a prayer code using keyword based search.
// When a match is found, the id of the prayer will be added to the list after the prayer code.
// The tool then asks you if you want to add another prayer and repeat the process.
package main
import (
"encoding/csv"
"encoding/json"
"fmt"
"math/rand"
"net/http"
"os"
"strconv"
"strings"
)
//BPNAPI is the API link of bahaiprayers.net
//It is used to get the list of prayers per language by numerical id from lang.csv
var BPNAPI = "https://bahaiprayers.net/api/prayer/prayersystembylanguage?languageid="
type BPNKind string
type Language struct {
Id int
Iso string
IsoType string
Name string
English string
FlagLink string
Rtl bool
}
//BPNAPIOutput is the JSON structure of the API output
type BPNAPIOutput struct {
ErrorMessage string
IsInError bool
Version int
Prayers []Prayer
Tags []struct {
Id int
LanguageId int
Name string
Kind BPNKind
PrayerCount int
}
TagRelations []struct {
Id int
PrayerId int
PrayerTagId int
LanguageId int
}
Urls []interface{}
Languages []struct {
Id int
Name string
English string
IsLeftToRight bool
FlagLink string
}
}
type Prayer struct {
Id int
AuthorId int
LanguageId int
Text string
Tags []struct {
Id int
Name string
Kind BPNKind
}
Tagkind struct {
Kind BPNKind
}
Urls []interface{}
}
func (p Prayer) Author() string {
if p.AuthorId > 0 && p.AuthorId < 4 {
return []string{"Báb", "Bahá'u'lláh", "Abdu'l-Bahá"}[p.AuthorId-1]
}
return "Unknown Author"
}
type PrayerCode struct {
Code string
Language string
}
var Languages []Language
var CodeList map[string][]int
var PrayersWithCode map[PrayerCode]Prayer
// ReadLangCSV reads rel/lang.csv and puts it in Languages
// It does so by matching each CSV field to a struct field
func ReadLangCSV() error {
file, err := os.Open("rel/lang.csv")
if err != nil {
return err
}
defer file.Close()
reader := csv.NewReader(file)
langCSV, err := reader.ReadAll()
if err != nil {
return err
}
for _, lang := range langCSV {
var language Language
language.Id, _ = strconv.Atoi(lang[0])
language.Iso = lang[1]
language.IsoType = lang[2]
language.Name = lang[3]
language.English = lang[4]
language.FlagLink = lang[5]
language.Rtl, _ = strconv.ParseBool(lang[6])
Languages = append(Languages, language)
}
return nil
}
func ReadCodeList() error {
file, err := os.Open("rel/code.list")
if err != nil {
return err
}
defer file.Close()
CodeList = make(map[string][]int)
reader := csv.NewReader(file)
for {
line, err := reader.Read()
if err != nil {
break
}
CodeList[line[0]] = make([]int, 0)
for _, prayerIDstr := range line[1:] {
prayerID, err := strconv.Atoi(prayerIDstr)
if err != nil {
return err
}
CodeList[line[0]] = append(CodeList[line[0]], prayerID)
}
}
return nil
}
func WriteCodeList(codeList map[string][]int) error {
file, err := os.Create("rel/code.list")
if err != nil {
return err
}
defer file.Close()
writer := csv.NewWriter(file)
for code, prayerIDs := range codeList {
line := make([]string, 0)
line = append(line, code)
for _, prayerID := range prayerIDs {
line = append(line, strconv.Itoa(prayerID))
}
writer.Write(line)
}
writer.Flush()
return nil
}
func AskLanguages() []Language {
// Ask "Which languages do you want to complete?"
// The answer is a list of language codes that must be converted to numbers
// using the conversion from lang.csv in Languages
fmt.Print("Which languages do you want to complete? ")
var languages []string
fmt.Scanln(&languages)
var outLangs []Language
for _, language := range languages {
for _, lang := range Languages {
if lang.Iso == language || lang.English == language {
outLangs = append(outLangs, lang)
}
}
}
return outLangs
}
func Code(p Prayer) string {
// Get the prayer code for a prayer via CodeList
// or return an empty string if it's not on the list
for code, prayerIDs := range CodeList {
for _, prayerID := range prayerIDs {
if p.Id == prayerID {
return code
}
}
}
return ""
}
func ReadPrayers(lang []Language) []Prayer {
var prayers []Prayer
for _, language := range lang {
response, err := http.Get(BPNAPI + strconv.Itoa(language.Id))
if err != nil {
fmt.Println(err)
return nil
}
defer response.Body.Close()
var output BPNAPIOutput
err = json.NewDecoder(response.Body).Decode(&output)
if err != nil {
fmt.Println(err)
return nil
}
for _, prayer := range output.Prayers {
if Code(prayer) == "" {
prayers = append(prayers, prayer)
}
}
}
return prayers
}
func main() {
err := ReadLangCSV()
if err != nil {
panic(err)
}
err = ReadCodeList()
if err != nil {
panic(err)
}
// Iterate over all languages and read in all prayers
// with a language code to PrayersWithCode
for _, language := range Languages {
prayers := ReadPrayers([]Language{language})
for _, prayer := range prayers {
code := Code(prayer)
if code != "" {
prayerCode := PrayerCode{code, language.Iso}
PrayersWithCode[prayerCode] = prayer
}
}
}
for {
languages := AskLanguages()
prayers := ReadPrayers(languages)
// randomize the order of the prayers
for i := len(prayers) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
prayers[i], prayers[j] = prayers[j], prayers[i]
}
// pick the first prayer from the resulting list that
// doesn't have a code in CodeList.
var prayer Prayer
var code string
for _, p := range prayers {
if Code(p) == "" {
prayer = p
break
}
}
// Present the text, id and author of the prayer
fmt.Println(prayer.Text)
fmt.Println("ID:", prayer.Id)
fmt.Println("Author:", prayer.Author())
for code == "" {
// Ask for a keyword
fmt.Print("Input a keyword for this prayer: ")
var keyword string
fmt.Scanln(&keyword)
var Matches []Prayer
// Check for the prayer text of each prayer in
// PrayersWithCode if there is a match with the keyword
// and add it to Matches
for _, prayer := range PrayersWithCode {
if strings.Contains(prayer.Text, keyword) {
Matches = append(Matches, prayer)
}
}
// If there are no matches, ask again
if len(Matches) == 0 {
fmt.Println("No matches found.")
continue
}
// Ask which of the matches to use
fmt.Println("Which of the following matches?")
for i, match := range Matches {
fmt.Println(i+1, ":", match.Text)
fmt.Print("Does this match? (y/n) ")
var answer string
fmt.Scanln(&answer)
if answer == "y" {
prayer = match
code = Code(match)
break
}
}
}
// Add the code to CodeList
CodeList[code] = append(CodeList[code], prayer.Id)
// Ask if the user wants to identify another prayer
// or if they want to quit
// To identify another prayer, continue
// To quit, save the CodeList and quit
fmt.Print("Identify another prayer? (y/n) ")
var answer string
fmt.Scanln(&answer)
if answer == "n" {
err = WriteCodeList(CodeList)
if err != nil {
panic(err)
}
break
}
}
}
|