diff --git a/data.go b/data.go index c273105..74a3d17 100644 --- a/data.go +++ b/data.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "github.com/csunibo/unibo-go/opendata" "os" "path" "strconv" @@ -11,7 +12,7 @@ import ( "github.com/rs/zerolog/log" "github.com/samber/lo" - "github.com/VaiTon/unibocalendar/unibo" + "github.com/VaiTon/unibocalendar/unibo_integ" ) const ( @@ -23,7 +24,7 @@ const ( func downloadOpenDataIfNewer() { // Get package - pack, err := unibo.GetPackage(packageId) + pack, err := opendata.FetchPackage(packageId) if err != nil { log.Warn().Err(err).Msg("unable to get package") return @@ -36,8 +37,8 @@ func downloadOpenDataIfNewer() { } // Get wanted resource - resource := pack.Result.Resources.GetByAlias(resourceAlias) - if resource == nil { + resource, found := pack.Result.Resources.GetByAlias(resourceAlias) + if !found { log.Warn().Msgf("unable to find resource '%s'", resourceAlias) return } @@ -67,7 +68,7 @@ func downloadOpenDataIfNewer() { return } - courses, err := resource.Download() + courses, err := unibo_integ.DownloadResource(resource) if err != nil { log.Panic().Err(err).Msg("Unable to download courses") } @@ -75,7 +76,7 @@ func downloadOpenDataIfNewer() { actualYear := time.Now().Year() // Filter courses by actual year - courses = lo.Filter(courses, func(c unibo.Course, _ int) bool { + courses = lo.Filter(courses, func(c unibo_integ.Course, _ int) bool { return strings.Contains(c.AnnoAccademico, strconv.Itoa(actualYear)) }) @@ -87,7 +88,7 @@ func downloadOpenDataIfNewer() { log.Info().Msg("Opendata file downloaded") } -func saveData(courses []unibo.Course) error { +func saveData(courses []unibo_integ.Course) error { err := createDataFolder() if err != nil { return err @@ -110,7 +111,7 @@ func createDataFolder() error { return os.MkdirAll(path.Dir(coursesPathJson), os.ModePerm) } -func openData() (unibo.CoursesMap, error) { +func openData() (unibo_integ.CoursesMap, error) { // Open file file, err := os.Open(coursesPathJson) if err != nil { @@ -118,7 +119,7 @@ func openData() (unibo.CoursesMap, error) { } // Decode json - courses := make([]unibo.Course, 0) + courses := make([]unibo_integ.Course, 0) err = json.NewDecoder(file).Decode(&courses) if err != nil { return nil, err @@ -131,7 +132,7 @@ func openData() (unibo.CoursesMap, error) { } // Create the map - courseMap := make(unibo.CoursesMap, len(courses)) + courseMap := make(unibo_integ.CoursesMap, len(courses)) for _, course := range courses { courseMap[course.Codice] = course } diff --git a/go.mod b/go.mod index 77000cb..6dc7668 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( github.com/arran4/golang-ical v0.1.0 + github.com/csunibo/unibo-go v0.0.3 github.com/gin-contrib/multitemplate v0.0.0-20230212012517-45920c92c271 github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index c434024..f0efe51 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,10 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhD github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/csunibo/unibo-go v0.0.2 h1:Y2zhrFPJFGHCaU7tmUOxKFiBp3zynntIFhPwSGqzreo= +github.com/csunibo/unibo-go v0.0.2/go.mod h1:h2+xnccHa7x48RNB6d07bpHQ01ozw4oihgDOlvVrJ9U= +github.com/csunibo/unibo-go v0.0.3 h1:NZfW6/2FyY1znyQC6irfWYXjf1UdAdu7fSSWDKYKUNs= +github.com/csunibo/unibo-go v0.0.3/go.mod h1:h2+xnccHa7x48RNB6d07bpHQ01ozw4oihgDOlvVrJ9U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/main.go b/main.go index e32dd88..d7c7fb5 100644 --- a/main.go +++ b/main.go @@ -8,12 +8,14 @@ import ( "os" "path" "slices" - "sort" "strconv" "strings" "text/template" "time" + "github.com/csunibo/unibo-go/curriculum" + "github.com/csunibo/unibo-go/timetable" + ics "github.com/arran4/golang-ical" "github.com/gin-contrib/multitemplate" limits "github.com/gin-contrib/size" @@ -23,7 +25,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/VaiTon/unibocalendar/unibo" + "github.com/VaiTon/unibocalendar/unibo_integ" ) const templateDir = "./templates" @@ -70,7 +72,7 @@ func main() { } } -func setupRouter(courses unibo.CoursesMap) *gin.Engine { +func setupRouter(courses unibo_integ.CoursesMap) *gin.Engine { r := gin.Default() r.Use(compress.Compress()) // Limit payload to 10 MB. This fixes zip bombs. @@ -84,7 +86,9 @@ func setupRouter(courses unibo.CoursesMap) *gin.Engine { }) coursesList := courses.ToList() - sort.Sort(coursesList) + slices.SortFunc(coursesList, func(a, b unibo_integ.Course) int { + return b.Codice - a.Codice + }) r.GET("/courses", func(c *gin.Context) { c.HTML(http.StatusOK, "courses", gin.H{ "courses": coursesList, @@ -97,7 +101,7 @@ func setupRouter(courses unibo.CoursesMap) *gin.Engine { return r } -func coursePage(courses unibo.CoursesMap) func(c *gin.Context) { +func coursePage(courses unibo_integ.CoursesMap) func(c *gin.Context) { return func(c *gin.Context) { courseId := c.Param("id") if courseId == "" { @@ -120,7 +124,7 @@ func coursePage(courses unibo.CoursesMap) func(c *gin.Context) { curricula, err := course.GetAllCurricula() if err != nil { _ = c.Error(fmt.Errorf("unable to retrieve curricula: %w", err)) - curricula = map[int]unibo.Curricula{} + curricula = map[int]curriculum.Curricula{} } c.HTML(http.StatusOK, "course", gin.H{ @@ -132,50 +136,50 @@ func coursePage(courses unibo.CoursesMap) func(c *gin.Context) { var calcache = cache.New(time.Minute*10, time.Minute*30) -func getCoursesCal(courses *unibo.CoursesMap) func(c *gin.Context) { - return func(c *gin.Context) { - id := c.Param("id") - anno := c.Param("anno") +func getCoursesCal(courses *unibo_integ.CoursesMap) func(c *gin.Context) { + return func(ctx *gin.Context) { + id := ctx.Param("id") + anno := ctx.Param("anno") cacheKey := fmt.Sprintf("%s-%s", id, anno) if cal, found := calcache.Get(cacheKey); found { - successCalendar(c, cal.(*bytes.Buffer)) + successCalendar(ctx, cal.(*bytes.Buffer)) return } // Check if id is a number, otherwise return 400 annoInt, err := strconv.Atoi(anno) if err != nil { - c.String(http.StatusBadRequest, "Invalid year") + ctx.String(http.StatusBadRequest, "Invalid year") return } // Check if id is a number, otherwise return 400 idInt, err := strconv.Atoi(id) if err != nil { - c.String(http.StatusBadRequest, "Invalid id") + ctx.String(http.StatusBadRequest, "Invalid id") return } // Check if course exists, otherwise return 404 course, found := courses.FindById(idInt) if !found { - c.String(http.StatusNotFound, "Course not found") + ctx.String(http.StatusNotFound, "Course not found") return } if annoInt <= 0 || annoInt > course.DurataAnni { - c.String(http.StatusBadRequest, "Invalid year") + ctx.String(http.StatusBadRequest, "Invalid year") return } - curriculumId := c.Query("curriculum") - curriculum := unibo.Curriculum{} + curriculumId := ctx.Query("curr") + curr := curriculum.Curriculum{} if curriculumId != "" { - curriculum.Value = curriculumId + curr.Value = curriculumId } - subjectIds := c.Query("subjects") + subjectIds := ctx.Query("subjects") var subjects []string if subjectIds != "" { subjects = strings.Split(subjectIds, ",") @@ -184,30 +188,30 @@ func getCoursesCal(courses *unibo.CoursesMap) func(c *gin.Context) { log.Debug().Any("subjects", subjects).Msg("Subjects") // Try to retrieve timetable, otherwise return 500 - timetable, err := course.GetTimetable(annoInt, curriculum, nil) + courseTimetable, err := course.GetTimetable(annoInt, curr, nil) if err != nil { - _ = c.Error(err) - c.String(http.StatusInternalServerError, "Unable to retrieve timetable") + _ = ctx.Error(err) + ctx.String(http.StatusInternalServerError, "Unable to retrieve timetable") return } - cal, err := createCal(timetable, course, annoInt, subjects) + cal, err := createCal(courseTimetable, course, annoInt, subjects) if err != nil { - _ = c.Error(err) - c.String(http.StatusInternalServerError, "Unable to create calendar") + _ = ctx.Error(err) + ctx.String(http.StatusInternalServerError, "Unable to create calendar") return } buf := bytes.NewBuffer(nil) err = cal.SerializeTo(buf) if err != nil { - _ = c.Error(err) - c.String(http.StatusInternalServerError, "Unable to serialize calendar") + _ = ctx.Error(err) + ctx.String(http.StatusInternalServerError, "Unable to serialize calendar") return } calcache.Set(cacheKey, buf, cache.DefaultExpiration) - successCalendar(c, buf) + successCalendar(ctx, buf) } } @@ -226,8 +230,8 @@ func successCalendar(c *gin.Context, cal *bytes.Buffer) { // // If subjectCodes is not nil, it will be used to filter the timetable by subjects. func createCal( - timetable unibo.Timetable, - course *unibo.Course, + timetable timetable.Timetable, + course *unibo_integ.Course, year int, subjectCodes []string, ) (*ics.Calendar, error) { @@ -250,7 +254,7 @@ func createCal( eventUid := fmt.Sprintf("%x", sha.Sum(nil)) e := cal.AddEvent(eventUid) - e.SetOrganizer(event.Docente) + e.SetOrganizer(event.Teacher) e.SetSummary(event.Title) e.SetStartAt(event.Start.Time) e.SetEndAt(event.End.Time) @@ -258,19 +262,16 @@ func createCal( e.SetDtStampTime(time.Now()) // https://www.kanzaki.com/docs/ical/dtstamp.html b := strings.Builder{} - b.WriteString(fmt.Sprintf("Docente: %s\n", event.Docente)) - if len(event.Aule) > 0 { - b.WriteString(fmt.Sprintf("Aula: %s\n", event.Aule[0].DesRisorsa)) + b.WriteString(fmt.Sprintf("Docente: %s\n", event.Teacher)) + if len(event.Classrooms) > 0 { + b.WriteString(fmt.Sprintf("Aula: %s\n", event.Classrooms[0].Description)) + e.SetLocation(event.Classrooms[0].Description) } b.WriteString(fmt.Sprintf("Cfu: %d\n", event.Cfu)) - b.WriteString(fmt.Sprintf("Periodo: %s\n", event.Periodo)) + b.WriteString(fmt.Sprintf("Periodo: %s\n", event.Interval)) b.WriteString(fmt.Sprintf("Codice modulo: %s\n", event.CodModulo)) e.SetDescription(b.String()) - - if len(event.Aule) > 0 { - e.SetLocation(event.Aule[0].DesRisorsa) - } } calName := fmt.Sprintf("%s - %d year", course.Descrizione, year) @@ -283,9 +284,9 @@ func createCal( return cal, nil } -func filterTimetableBySubjects(timetable unibo.Timetable, codes []string) unibo.Timetable { - filtered := make(unibo.Timetable, 0, len(timetable)) - for _, event := range timetable { +func filterTimetableBySubjects(t timetable.Timetable, codes []string) timetable.Timetable { + filtered := make([]timetable.Event, 0, len(t)) + for _, event := range t { if slices.Contains(codes, event.CodModulo) { filtered = append(filtered, event) } diff --git a/unibo/curriculum.go b/unibo/curriculum.go deleted file mode 100644 index 793395e..0000000 --- a/unibo/curriculum.go +++ /dev/null @@ -1,31 +0,0 @@ -package unibo - -import ( - "fmt" - "strings" -) - -const baseCurriculaIt = "https://corsi.unibo.it/%s/%s/orario-lezioni/@@available_curricula?anno=%d&curricula=" -const baseCurriculaEn = "https://corsi.unibo.it/%s/%s/timetable/@@available_curricula?anno=%d&curricula=" - -type Curriculum struct { - Selected bool `json:"selected"` - Value string `json:"value"` - Label string `json:"label"` -} - -type Curricula []Curriculum - -func GetCurriculaUrl(course CourseId, year int) string { - if strings.Contains(course.Tipologia, "cycle") { - return fmt.Sprintf(baseCurriculaEn, course.Tipologia, course.Id, year) - } - - return fmt.Sprintf(baseCurriculaIt, course.Tipologia, course.Id, year) -} - -func FetchCurricula(course CourseId, year int) (curricula Curricula, err error) { - url := GetCurriculaUrl(course, year) - err = getJson(url, &curricula) - return -} diff --git a/unibo/curriculum_test.go b/unibo/curriculum_test.go deleted file mode 100644 index 140ea6c..0000000 --- a/unibo/curriculum_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package unibo - -import ( - "fmt" - "io" - "net/http" - "testing" -) - -func TestGetCurricula(t *testing.T) { - curricula, err := FetchCurricula(CourseId{Tipologia: "laurea", Id: "informatica"}, 1) - if err != nil { - t.Fatalf("Error while fetching curricula: %s", err) - } - - fmt.Println(curricula) - - url := fmt.Sprintf( - "https://corsi.unibo.it/%s/%s/orario-lezioni/@@orario_reale_json?anno=%d&curriculum=%s", - "laurea", - "informatica", - 1, - curricula[0].Value, - ) - - res, err := http.Get(url) - if err != nil { - t.Fatalf("Error while fetching timetable: %s", err) - } - - body, _ := io.ReadAll(res.Body) - fmt.Println(string(body)) - -} diff --git a/unibo/opendata_test.go b/unibo/opendata_test.go deleted file mode 100644 index 1d6181f..0000000 --- a/unibo/opendata_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package unibo - -import ( - "math/rand" - "testing" - - "github.com/samber/lo" -) - -func BenchmarkCourses_FindById(b *testing.B) { - courses := CoursesMap{} - for i := 0; i < 1000; i++ { - course := genRandomCourse() - courses[course.Codice] = course - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - courses.FindById(rand.Int()) - } -} - -func genRandomCourse() Course { - return Course{ - AnnoAccademico: lo.RandomString(10, lo.LettersCharset), - Immatricolabile: lo.RandomString(10, lo.LettersCharset), - Codice: rand.Int(), - Descrizione: lo.RandomString(10, lo.LettersCharset), - Url: lo.RandomString(10, lo.LettersCharset), - Campus: lo.RandomString(10, lo.LettersCharset), - Ambiti: lo.RandomString(10, lo.LettersCharset), - Tipologia: lo.RandomString(10, lo.LettersCharset), - DurataAnni: rand.Int(), - Internazionale: rand.Intn(2) == 1, - InternazionaleTitolo: lo.RandomString(10, lo.LettersCharset), - InternazionaleLingua: lo.RandomString(10, lo.LettersCharset), - Lingue: lo.RandomString(10, lo.LettersCharset), - Accesso: lo.RandomString(10, lo.LettersCharset), - SedeDidattica: lo.RandomString(10, lo.LettersCharset), - } -} - -func TestCourses_FindById(t *testing.T) { - courses := CoursesMap{1: {Codice: 1}, 2: {Codice: 2}, 3: {Codice: 3}} - - course, found := courses.FindById(2) - if !found { - t.Fatal("course not found") - } - - if course.Codice != 2 { - t.Error("wrong course") - } -} diff --git a/unibo/package.go b/unibo/package.go deleted file mode 100644 index 9dd0d41..0000000 --- a/unibo/package.go +++ /dev/null @@ -1,34 +0,0 @@ -package unibo - -import ( - "encoding/json" - "fmt" -) - -func GetPackageUrl(id string) string { - return fmt.Sprintf("%s/api/3/action/package_show?id=%s", rootUnibo, id) -} - -func GetPackage(id string) (*Package, error) { - url := GetPackageUrl(id) - - html, err := Client.Get(url) - if err != nil { - return nil, err - } - - body := html.Body - pack := Package{} - - err = json.NewDecoder(body).Decode(&pack) - if err != nil { - return nil, err - } - - err = body.Close() - if err != nil { - return nil, err - } - - return &pack, nil -} diff --git a/unibo/resource.go b/unibo/resource.go deleted file mode 100644 index f02cdc2..0000000 --- a/unibo/resource.go +++ /dev/null @@ -1,44 +0,0 @@ -package unibo - -import ( - "fmt" - "strings" -) - -type Resource struct { - Frequency string `json:"frequency"` - Url string `json:"url"` - Id string `json:"id"` - PackageId string `json:"package_id"` - LastMod string `json:"last_modified"` - Alias string `json:"alias"` -} - -func (r Resource) Download() ([]Course, error) { - // Get the resource - res, err := Client.Get(r.Url) - if err != nil { - return nil, err - } - - // Parse the body - var courses []Course - if strings.HasSuffix(r.Url, ".csv") { - courses, err = r.downloadCSV(res.Body) - } - if err != nil { - return nil, err - } - - // Close the body - err = res.Body.Close() - if err != nil { - return nil, err - } - - if courses == nil { - return nil, fmt.Errorf("resource is not a csv file") - } - - return courses, nil -} diff --git a/unibo/timetable.go b/unibo/timetable.go deleted file mode 100644 index 3006910..0000000 --- a/unibo/timetable.go +++ /dev/null @@ -1,91 +0,0 @@ -package unibo - -import ( - "fmt" - "strings" - "time" -) - -const baseTimetable = "https://corsi.unibo.it/%s/%s/orario-lezioni/@@orario_reale_json?anno=%d" -const baseTimetableEn = "https://corsi.unibo.it/%s/%s/timetable/@@orario_reale_json?anno=%d" - -type CalendarTime struct { - time.Time -} - -func (c *CalendarTime) UnmarshalJSON(b []byte) error { - t, err := time.ParseInLocation(`"2006-01-02T15:04:05"`, string(b), time.Local) - if err != nil { - return err - } - - c.Time = t - return nil -} - -func (c *CalendarTime) MarshalJSON() ([]byte, error) { - return []byte(c.Format(`"2006-01-02T15:04:05"`)), nil -} - -type Aula struct { - DesRisorsa string `json:"des_risorsa"` -} - -type TimetableEvent struct { - CodModulo string `json:"cod_modulo"` - PeriodoCalendario string `json:"periodo_calendario"` - CodSdoppiamento string `json:"cod_sdoppiamento"` - Title string `json:"title"` - ExtCode string `json:"extCode"` - Periodo string `json:"periodo"` - Docente string `json:"docente"` - Cfu int `json:"cfu"` - Teledidattica bool `json:"teledidattica"` - Teams string `json:"teams,omitempty"` - Start CalendarTime `json:"start"` - End CalendarTime `json:"end"` - Aule []Aula `json:"aule"` -} - -type Timetable []TimetableEvent - -type TimetablePeriod struct { - Start time.Time - End time.Time -} - -// GetTimetableUrl returns the URL to fetch the timetable for the given course. -// -// If `curriculum` is not empty, it will be used to filter the timetable. -// If `period` is not nil, it will be used to filter the timetable. -func GetTimetableUrl(course CourseId, curriculum Curriculum, year int, period *TimetablePeriod) string { - - var url string - if strings.Contains(course.Tipologia, "cycle") { - url = fmt.Sprintf(baseTimetableEn, course.Tipologia, course.Id, year) - } else { - url = fmt.Sprintf(baseTimetable, course.Tipologia, course.Id, year) - } - - if curriculum != (Curriculum{}) { - url += fmt.Sprintf("&curricula=%s", curriculum.Value) - } - - if period != nil { - url += fmt.Sprintf("&start=%s", period.Start.Format("2006-01-02")) - url += fmt.Sprintf("&end=%s", period.End.Format("2006-01-02")) - } - - return url -} - -func FetchTimetable( - course CourseId, - curriculum Curriculum, - year int, - period *TimetablePeriod, -) (timetable Timetable, err error) { - url := GetTimetableUrl(course, curriculum, year, period) - err = getJson(url, &timetable) - return -} diff --git a/unibo/course.go b/unibo_integ/course.go similarity index 77% rename from unibo/course.go rename to unibo_integ/course.go index f2d16ed..0689439 100644 --- a/unibo/course.go +++ b/unibo_integ/course.go @@ -1,8 +1,10 @@ -package unibo +package unibo_integ import ( "bytes" "fmt" + "github.com/csunibo/unibo-go/curriculum" + "github.com/csunibo/unibo-go/timetable" "io" "regexp" "strconv" @@ -86,6 +88,8 @@ func (c Course) scrapeCourseWebsiteId() (CourseId, error) { found := reg.FindStringSubmatch(buf.String()) if found == nil { return CourseId{}, fmt.Errorf("unable to find course website") + } else if len(found) != 2 { + return CourseId{}, fmt.Errorf("unexpected number of matches: %d (the website has changed?)", len(found)) } // full url -> laurea/IngegneriaInformatica @@ -96,13 +100,13 @@ func (c Course) scrapeCourseWebsiteId() (CourseId, error) { return CourseId{split[0], split[1]}, nil } -func (c Course) GetCurricula(year int) (Curricula, error) { +func (c Course) GetCurricula(year int) (curriculum.Curricula, error) { id, err := c.GetCourseWebsiteId() if err != nil { return nil, err } - curricula, err := FetchCurricula(id, year) + curricula, err := curriculum.FetchCurricula(id.Tipologia, id.Id, year) if err != nil { return nil, err } @@ -110,18 +114,18 @@ func (c Course) GetCurricula(year int) (Curricula, error) { return curricula, nil } -func (c Course) GetAllCurricula() (map[int]Curricula, error) { +func (c Course) GetAllCurricula() (map[int]curriculum.Curricula, error) { id, err := c.GetCourseWebsiteId() if err != nil { return nil, fmt.Errorf("could not get course website id: %w", err) } - currCh := make(chan Curricula) + currCh := make(chan curriculum.Curricula) errCh := make(chan error) for year := 1; year <= c.DurataAnni; year++ { go func(year int) { - curricula, err := FetchCurricula(id, year) + curricula, err := curriculum.FetchCurricula(id.Tipologia, id.Id, year) if err != nil { errCh <- err return @@ -130,7 +134,7 @@ func (c Course) GetAllCurricula() (map[int]Curricula, error) { }(year) } - curriculaMap := make(map[int]Curricula, c.DurataAnni) + curriculaMap := make(map[int]curriculum.Curricula, c.DurataAnni) for year := 1; year <= c.DurataAnni; year++ { select { case curricula := <-currCh: @@ -143,23 +147,23 @@ func (c Course) GetAllCurricula() (map[int]Curricula, error) { return curriculaMap, nil } -func (c Course) GetTimetable(year int, curriculum Curriculum, period *TimetablePeriod) (Timetable, error) { +func (c Course) GetTimetable(year int, curriculum curriculum.Curriculum, period *timetable.Interval) (timetable.Timetable, error) { id, err := c.GetCourseWebsiteId() if err != nil { return nil, err } - timetable, err := FetchTimetable(id, curriculum, year, period) + t, err := timetable.FetchTimetable(id.Tipologia, id.Id, curriculum.Value, year, period) if err != nil { return nil, err } - return timetable, nil + return t, nil } type CoursesMap map[int]Course -func (c CoursesMap) ToList() Courses { +func (c CoursesMap) ToList() []Course { courses := make([]Course, 0, len(c)) for _, course := range c { courses = append(courses, course) @@ -171,23 +175,3 @@ func (c CoursesMap) FindById(id int) (*Course, bool) { course, found := c[id] return &course, found } - -type Courses []Course - -func (c Courses) Len() int { - return len(c) -} - -func (c Courses) Less(i, j int) bool { - if c[i].AnnoAccademico != c[j].AnnoAccademico { - return c[i].AnnoAccademico < c[j].AnnoAccademico - } - if c[i].Tipologia != c[j].Tipologia { - return c[i].Tipologia < c[j].Tipologia - } - return c[i].Codice < c[j].Codice -} - -func (c Courses) Swap(i, j int) { - c[i], c[j] = c[j], c[i] -} diff --git a/unibo/opendata.go b/unibo_integ/resource.go similarity index 65% rename from unibo/opendata.go rename to unibo_integ/resource.go index 99112b3..4c13ac5 100644 --- a/unibo/opendata.go +++ b/unibo_integ/resource.go @@ -1,24 +1,45 @@ -package unibo +package unibo_integ import ( "encoding/csv" + "fmt" "io" "strconv" "strings" -) -const ( - rootUnibo = "https://dati.unibo.it" + "github.com/csunibo/unibo-go/opendata" ) -type Package struct { - Success bool `json:"success"` - Result struct { - Resources Resources +func DownloadResource(resource *opendata.Resource) ([]Course, error) { + // Get the resource + res, err := Client.Get(resource.Url) + if err != nil { + return nil, err + } + + // Parse the body + var courses []Course + if strings.HasSuffix(resource.Url, ".csv") { + courses, err = downloadCSV(res.Body) + } + if err != nil { + return nil, err + } + + // Close the body + err = res.Body.Close() + if err != nil { + return nil, err } + + if courses == nil { + return nil, fmt.Errorf("resource is not a csv file") + } + + return courses, nil } -func (r Resource) downloadCSV(body io.Reader) ([]Course, error) { +func downloadCSV(body io.Reader) ([]Course, error) { courses := make([]Course, 0, 100) reader := csv.NewReader(body) @@ -74,20 +95,3 @@ func (r Resource) downloadCSV(body io.Reader) ([]Course, error) { } return courses, nil } - -type Resources []Resource - -func (r Resources) GetByAlias(alias string) *Resource { - for _, resource := range r { - // Some resources have multiple aliases - rAliases := strings.Split(resource.Alias, ", ") - - // Check if the alias is one of the aliases of the resource - for _, rAlias := range rAliases { - if rAlias == alias { - return &resource - } - } - } - return nil -} diff --git a/unibo/utils.go b/unibo_integ/utils.go similarity index 53% rename from unibo/utils.go rename to unibo_integ/utils.go index f5ed88c..848f1eb 100644 --- a/unibo/utils.go +++ b/unibo_integ/utils.go @@ -1,7 +1,6 @@ -package unibo +package unibo_integ import ( - "encoding/json" "net/http" ) @@ -21,25 +20,3 @@ var Client = http.Client{ http.DefaultTransport, }, } - -func getJson(url string, v interface{}) error { - // Get the resource - res, err := Client.Get(url) - if err != nil { - return err - } - - // Parse the body - err = json.NewDecoder(res.Body).Decode(v) - if err != nil { - return err - } - - // Close the body - err = res.Body.Close() - if err != nil { - return err - } - - return nil -}