Skip to content

Commit

Permalink
schedule scraping kinda works, added boilerplate
Browse files Browse the repository at this point in the history
  • Loading branch information
smeggmann99 committed Oct 2, 2024
1 parent 76a59ec commit b38b70a
Show file tree
Hide file tree
Showing 30 changed files with 2,242 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}/main.go",
}
]
}
8 changes: 8 additions & 0 deletions app/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"scraper": {
"optivum_base_url": "https://zsem.edu.pl/plany/plany/",
"division_endpoint": "o%d.html",
"teacher_endpoint": "n%d.html",
"room_endpoint": "s%d.html"
}
}
21 changes: 21 additions & 0 deletions app/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// main.go
package main

import (
"fmt"

"smuggr.xyz/optivum-bsf/common/config"
"smuggr.xyz/optivum-bsf/core/scraper"
)

func main() {
if err := config.Initialize(); err != nil {
panic(err)
}

if err := scraper.Initialize(); err != nil {
panic(err)
}

fmt.Println(scraper.ScrapeDivision(1))
}
51 changes: 51 additions & 0 deletions common/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// config.go
package config

import (
"fmt"
"os"

"github.com/joho/godotenv"
"github.com/spf13/viper"
)

var Global GlobalConfig

func loadEnv() error {
err := godotenv.Load()
if err != nil {
return err
}

return nil
}

func loadConfig(config *GlobalConfig) error {
if err := viper.ReadInConfig(); err != nil {
return err
}

err := viper.Unmarshal(config)
if err != nil {
return err
}

return nil
}

func Initialize() error {
fmt.Println("initializing config...")

if err := loadEnv(); err != nil {
return err
}

viper.AddConfigPath(os.Getenv("CONFIG_PATH"))
viper.SetConfigType(os.Getenv("CONFIG_TYPE"))

if err := loadConfig(&Global); err != nil {
return err
}

return nil
}
28 changes: 28 additions & 0 deletions common/config/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// config/models.go
package config

type GeneralConfig struct {
Debug bool `mapstructure:"debug"`
SyncSchedule bool `mapstructure:"sync_schedule"`
RelayOnDuration int `mapstructure:"relay_on_duration"`
}

type ScraperConfig struct {
OptivumBaseUrl string `mapstructure:"optivum_base_url"`
DivisionEndpoint string `mapstructure:"division_endpoint"`
TeacherEndpoint string `mapstructure:"teacher_endpoint"`
RoomEndpoint string `mapstructure:"room_endpoint"`
}

type DevicesConfig struct {
DisplayAddress uint16 `mapstructure:"display_address"`
RTCAddress uint16 `mapstructure:"rtc_address"`
I2CBus string `mapstructure:"i2c_bus"`
RelayPin string `mapstructure:"relay_pin"`
}

type GlobalConfig struct {
General GeneralConfig `mapstructure:"general"`
Scraper ScraperConfig `mapstructure:"scraper"`
Devices DevicesConfig `mapstructure:"devices"`
}
61 changes: 61 additions & 0 deletions common/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// utils.go
package utils

import (
"fmt"
"net"
"net/http"
"strings"
)

func GetIP() (string, error) {
interfaces, err := net.Interfaces()
if err != nil {
return "", err
}

for _, i := range interfaces {
// Skip down or loopback interfaces
if i.Flags&net.FlagUp == 0 || i.Flags&net.FlagLoopback != 0 {
continue
}

addrs, err := i.Addrs()
if err != nil {
return "", err
}

for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}

// Only return non-loopback IPv4 addresses
if ip != nil && ip.To4() != nil {
return ip.String(), nil
}
}
}
return "", fmt.Errorf("no valid IPv4 address found")
}

func CheckURL(url string) bool {
resp, err := http.Get(url)
if err != nil {
return false
}
defer resp.Body.Close()

return resp.StatusCode == http.StatusOK
}

func IsEmptyOrInvisible(text string) bool {
text = strings.ReplaceAll(text, "\u00a0", " ")
text = strings.TrimSpace(text)

return text == ""
}
88 changes: 88 additions & 0 deletions core/scraper/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// scraper/models.go
package scraper

import (
"fmt"
"strconv"
"strings"
"time"
)

type TimeString string

func (t TimeString) ToTimestamp() (Timestamp, error) {
parts := strings.Split(string(t), ":")
if len(parts) < 2 || len(parts) > 3 {
return Timestamp{}, fmt.Errorf("invalid time format: %s", t)
}

hour, err := strconv.Atoi(parts[0])
if err != nil {
return Timestamp{}, fmt.Errorf("invalid hour: %w", err)
}

minute, err := strconv.Atoi(parts[1])
if err != nil {
return Timestamp{}, fmt.Errorf("invalid minute: %w", err)
}

return Timestamp{Hour: hour, Minute: minute}, nil
}

type Timestamp struct {
Hour int `json:"h"`
Minute int `json:"m"`
}

func (t Timestamp) String() string {
return fmt.Sprintf("%02d:%02d", t.Hour, t.Minute)
}

func (t Timestamp) Compare(time time.Time) bool {
return t.Hour == time.Hour() && t.Minute == time.Minute()
}

type TimeRange struct {
Start Timestamp `json:"start"`
End Timestamp `json:"end"`
}

func (tr TimeRange) String() string {
return fmt.Sprintf("%s-%s", tr.Start, tr.End)
}

type Teacher struct {
Designator string `json:"designator"`
FullName string `json:"full_name"`
Schedule Schedule `json:"schedule"`
}

type Room struct {
Designator string `json:"designator"`
FullName string `json:"full_name"`
Schedule Schedule `json:"schedule"`
BuildingName string `json:"building_name"`
}

type Lesson struct {
FullName string `json:"full_name"`
Teacher string `json:"teacher"`
Room string `json:"room"`
Division string `json:"division"`
TimeRange TimeRange `json:"time_range"`
}

func (l Lesson) String() string {
return fmt.Sprintf("[%s %s %s %s %s]", l.FullName, l.Teacher, l.Room, l.Division, l.TimeRange)
}

// Schedule is a 3D array of lessons, first dimension is day,
// second is the lesson number, third are the lessons
type Schedule [][][]Lesson

type Division struct {
Index uint `json:"index"`
Designator string `json:"designator"`
FullName string `json:"full_name"`
Schedule Schedule `json:"schedule"`
}
Loading

0 comments on commit b38b70a

Please sign in to comment.