mcgraw-hill-flashcards/main.go
2024-02-14 11:37:29 -05:00

232 lines
6.3 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
)
const (
// These should be set to control the book that is downloaded.
Language = "Spanish"
BookName = "Complete Spanish Step-by-Step"
)
const (
// These are for the program and should not be changed.
GetMenuUrl = "https://mhe-language-lab.azurewebsites.net/api/GetSubMenus?parentID=%d"
GetFlashCardsUrl = "https://mhe-language-lab.azurewebsites.net/api/GetFlashCards?menuID=%d"
FlashCards = "Flashcards"
FlashCardsStudyMode = "Flashcards: Study Mode"
FileStart = `#separator:tab
#html:true
#deck column:3
`
)
func main() {
err := DoEverything()
if err != nil {
fmt.Println(err)
}
}
func DoEverything() error {
// Get the list of languages and select the desired language.
languageMenu, err := GetMenu(0)
if err != nil {
return err
}
var languageId int
for _, languageEntry := range languageMenu {
if languageEntry.MenuTitle == Language {
languageId = languageEntry.MenuID
break
}
}
// Get the list of books for the selected language and select the desired book.
bookMenu, err := GetMenu(languageId)
if err != nil {
return err
}
var bookId int
for _, bookEntry := range bookMenu {
if bookEntry.MenuTitle == BookName {
bookId = bookEntry.MenuID
break
}
}
// Get the chapters of the book and download the flashcards.
bookOptions, err := GetMenu(bookId)
if err != nil {
return err
}
var flashCardId int
for _, options := range bookOptions {
if options.MenuTitle == FlashCards {
flashCardId = options.MenuID
break
}
}
chapters, err := GetMenu(flashCardId)
if err != nil {
return err
}
err = os.MkdirAll("output", 0755)
if err != nil {
return err
}
fileOutput := strings.Builder{}
fileOutput.WriteString(FileStart)
for _, chapter := range chapters {
// Make 1-digit numbers start with zero, so that alphabetical sorting works correctly.
if chapter.MenuTitle[1] == '.' {
chapter.MenuTitle = "0" + chapter.MenuTitle
}
// Strip html tags from the chapter title
chapter.MenuTitle = strings.ReplaceAll(chapter.MenuTitle, "<i>", "")
chapter.MenuTitle = strings.ReplaceAll(chapter.MenuTitle, "</i>", "")
fmt.Printf("%+v\n", chapter.MenuTitle)
// Get all sections in the chapter and get the study mode flashcards in each section.
sections, err := GetMenu(chapter.MenuID)
if err != nil {
return err
}
var chapterCards []*Card
for _, section := range sections {
fmt.Printf(" %+v\n", section.MenuTitle)
modes, err := GetMenu(section.MenuID)
if err != nil {
return err
}
for _, mode := range modes {
if mode.MenuTitle == FlashCardsStudyMode {
cards, err := GetFlashCards(mode.MenuID)
if err != nil {
return err
}
for _, card := range cards {
if strings.Contains(card.SideA, "\r\n") {
// There is a broken card in the Spanish course that totally mangles the output.
// Handle that card correctly. There are several cards combined into one message, this will
// actually put them in the output correctly.
parts := strings.Split(card.SideA, "\r\n")
for _, part := range parts[1:] {
aAndB := strings.Split(part, "\t")
if len(aAndB) == 2 {
chapterCards = append(chapterCards, &Card{
SideA: aAndB[0],
SideB: aAndB[1],
})
}
}
continue
}
chapterCards = append(chapterCards, card)
}
}
}
}
if len(chapterCards) == 0 {
continue
}
for _, card := range chapterCards {
title := chapter.MenuTitle
if chapter.MenuTitle[2:4] == ". " {
title = chapter.MenuTitle[0:4] + "(" + Language[0:1] + "2E) " + chapter.MenuTitle[4:]
}
fileOutput.WriteString(fmt.Sprintf("%s\t%s\t%s %s\n", card.SideA, card.SideB, BookName, title))
}
for _, card := range chapterCards {
title := chapter.MenuTitle
if chapter.MenuTitle[2:4] == ". " {
title = chapter.MenuTitle[0:4] + "(E2" + Language[0:1] + ") " + chapter.MenuTitle[4:]
}
fileOutput.WriteString(fmt.Sprintf("%s\t%s\t%s %s\n", card.SideB, card.SideA, BookName, title))
}
}
fileName := fmt.Sprintf("output/%s.tsv", BookName)
err = os.WriteFile(fileName, []byte(fileOutput.String()), 0644)
if err != nil {
return err
}
fmt.Printf("File written to %s\n", fileName)
return nil
}
func GetMenu(parentID int) ([]*MenuEntry, error) {
resp, err := http.Get(fmt.Sprintf(GetMenuUrl, parentID))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var getMenuResponse []*MenuEntry
if err := json.Unmarshal(body, &getMenuResponse); err != nil {
return nil, err
}
return getMenuResponse, nil
}
func GetFlashCards(menuID int) ([]*Card, error) {
resp, err := http.Get(fmt.Sprintf(GetFlashCardsUrl, menuID))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var getFlashCardsResponse []*Card
if err := json.Unmarshal(body, &getFlashCardsResponse); err != nil {
return nil, err
}
return getFlashCardsResponse, nil
}
type MenuEntry struct {
MenuID int `json:"Menu_ID"`
MenuTitle string `json:"MenuTitle"`
TitleInformation string `json:"TitleInformation"`
Base64Image string `json:"Base64Image"`
MenuFormat string `json:"MenuFormat"`
DeckType string `json:"DeckType"`
SelfScoring bool `json:"SelfScoring"`
DeckTitle string `json:"DeckTitle"`
FlashCardsAndQuiz bool `json:"FlashCardsAndQuiz"`
SideALabel string `json:"SideALabel"`
SideBLabel string `json:"SideBLabel"`
DataDeckID int `json:"DataDeck_ID"`
ForceSideA bool `json:"ForceSideA"`
Unpublished bool `json:"Unpublished"`
}
type Card struct {
CardID int `json:"Card_ID"`
SideA string `json:"SideA"`
SideB string `json:"SideB"`
StyleA string `json:"StyleA"`
StyleB string `json:"StyleB"`
SideAAudio string `json:"SideAAudio"`
SideBAudio string `json:"SideBAudio"`
SideAImage string `json:"SideAImage"`
SideBImage string `json:"SideBImage"`
SideAVideo string `json:"SideAVideo"`
SideBVideo string `json:"SideBVideo"`
SideALabel string `json:"SideALabel"`
SideBLabel string `json:"SideBLabel"`
TTSAudio bool `json:"TTSAudio"`
TTSSideA string `json:"TTSSideA"`
TTSSideB string `json:"TTSSideB"`
}