Add Noten parsing and rework some existing code
This commit is contained in:
parent
b7bea8cf09
commit
eb9f2ed4ce
@ -19,6 +19,7 @@ import (
|
||||
var (
|
||||
sessionCache string
|
||||
username string
|
||||
showGrades bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -29,6 +30,7 @@ func init() {
|
||||
defaultCacheDir := path.Join(homeDir, ".cache/go-lsf/sessions")
|
||||
flag.StringVar(&sessionCache, "session-cache", defaultCacheDir, "path where the session tokens are located")
|
||||
flag.StringVar(&username, "username", "", "username to login with in lsf")
|
||||
flag.BoolVar(&showGrades, "noten", false, "list grades")
|
||||
flag.Parse()
|
||||
|
||||
err = os.MkdirAll(sessionCache, os.ModePerm)
|
||||
@ -51,7 +53,16 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("s: %+v\n", s)
|
||||
if showGrades {
|
||||
noten, err := s.Noten()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Noten:")
|
||||
for _, note := range noten {
|
||||
fmt.Println(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readUsername() (string, error) {
|
||||
@ -69,7 +80,7 @@ func readPassword() (string, error) {
|
||||
fmt.Print("Password: ")
|
||||
b, err := terminal.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not read password")
|
||||
return "", err
|
||||
}
|
||||
fmt.Print("\n")
|
||||
password = string(b)
|
||||
@ -101,20 +112,16 @@ func session(username string) (*lsf.Session, error) {
|
||||
return nil, errors.Wrapf(err, "could not read session file %s", sessionPath)
|
||||
}
|
||||
sid := strings.TrimSuffix(string(b), "\n")
|
||||
s := &lsf.Session{
|
||||
SID: sid,
|
||||
}
|
||||
valid, err := s.Valid()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not check session")
|
||||
}
|
||||
if !valid {
|
||||
fmt.Println("session is not valid")
|
||||
s, err := lsf.NewSessionBySID(sid)
|
||||
if err == lsf.ErrInvalSID {
|
||||
log.Println("session is not valid")
|
||||
err := os.Remove(sessionPath)
|
||||
if err != nil {
|
||||
log.Println(errors.Wrap(err, "could not remove invalid session file"))
|
||||
}
|
||||
return login(username)
|
||||
} else if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get session by existing sid")
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
@ -122,7 +129,7 @@ func session(username string) (*lsf.Session, error) {
|
||||
func login(username string) (*lsf.Session, error) {
|
||||
password, err := readPassword()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not login")
|
||||
return nil, errors.Wrap(err, "could not read password")
|
||||
}
|
||||
s, err := lsf.Login(username, password)
|
||||
if err != nil {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package lsf
|
||||
|
||||
// Modul hold information about a module
|
||||
type Modul struct {
|
||||
Nr int
|
||||
}
|
||||
|
62
noten.go
62
noten.go
@ -1,10 +1,66 @@
|
||||
package lsf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/pkg/errors"
|
||||
//"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func (s *Session) Noten() (map[Modul]float32, error) {
|
||||
return nil, errors.New("noten not implemented yet")
|
||||
// Note is a grade with associated data like the related module
|
||||
type Note struct {
|
||||
Nr string
|
||||
Name string
|
||||
Semester string
|
||||
Note string
|
||||
Status string
|
||||
CPs string
|
||||
Versuch string
|
||||
}
|
||||
|
||||
func (n *Note) String() string {
|
||||
return fmt.Sprintf("%s: %s", n.Name, n.Note)
|
||||
}
|
||||
|
||||
// Noten returns a list of the grades of all graded or signed up modules
|
||||
func (s *Session) Noten() ([]*Note, error) {
|
||||
var noten []*Note
|
||||
client := &http.Client{}
|
||||
url := "https://lsf.hs-worms.de/qisserver/rds?state=notenspiegelStudent&next=list.vm&nextdir=qispos/notenspiegel/student&createInfos=Y&struct=auswahlBaum&nodeID=auswahlBaum%7Cabschluss%3Aabschl%3D05%2Cstgnr%3D1%7Cstudiengang%3Astg%3D938%2Cpversion%3D2018&expand=0&asi=" + s.ASI
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not prepare the request")
|
||||
}
|
||||
req.Header.Add("Cookie", fmt.Sprintf("JSESSIONID=%s", s.SID))
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not do the request")
|
||||
}
|
||||
doc, err := goquery.NewDocumentFromResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find all tables. The last one contains the grades. Get all children rows.
|
||||
rows := doc.Find("table").Last().Find("tr")
|
||||
// The first three are the header, the average mark and a divider. So lets ignore them.
|
||||
rows.Slice(3, rows.Length()).Each(func(_ int, row *goquery.Selection) {
|
||||
vals := row.ChildrenFiltered("td").Map(func(i int, cell *goquery.Selection) string {
|
||||
return strings.TrimSpace(cell.Text())
|
||||
})
|
||||
note := Note{
|
||||
Nr: vals[0],
|
||||
Name: vals[1],
|
||||
Semester: vals[2],
|
||||
Note: vals[3],
|
||||
Status: vals[4],
|
||||
CPs: vals[5],
|
||||
Versuch: vals[7],
|
||||
}
|
||||
noten = append(noten, ¬e)
|
||||
})
|
||||
|
||||
return noten, nil
|
||||
}
|
||||
|
70
session.go
70
session.go
@ -5,17 +5,30 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Error variables
|
||||
var (
|
||||
ErrInvalSID error = errors.New("invalid session id")
|
||||
)
|
||||
|
||||
// Session contains the session id and whatever this obscure asi token is.
|
||||
type Session struct {
|
||||
SID string
|
||||
// ASI is a string you somehow have to pass to some endpoints.
|
||||
ASI string
|
||||
}
|
||||
|
||||
// Valid checks if the session id of the session is valid
|
||||
func (s *Session) Valid() (bool, error) {
|
||||
client := &http.Client{}
|
||||
// GET the logged in mail page.
|
||||
// If correctly logged in there is a logout button.
|
||||
// Otherwise there is a login button.
|
||||
req, err := http.NewRequest("GET", "https://lsf.hs-worms.de/qisserver/rds?state=user&type=8&topitem=functions&breadCrumbSource=portal", nil)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not prepare the request")
|
||||
@ -39,6 +52,9 @@ func (s *Session) Valid() (bool, error) {
|
||||
return false, errors.New("unexpected response body")
|
||||
}
|
||||
|
||||
// Login tries to login with the given username and password.
|
||||
// If successful a new Session with the session id and asi
|
||||
// will be created and returned.
|
||||
func Login(username, password string) (*Session, error) {
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error { // don't follow redirects
|
||||
@ -61,8 +77,13 @@ func Login(username, password string) (*Session, error) {
|
||||
if resp.StatusCode == 302 {
|
||||
for _, c := range resp.Cookies() {
|
||||
if c.Name == "JSESSIONID" {
|
||||
asi, err := asi(c.Value)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get asi")
|
||||
}
|
||||
return &Session{
|
||||
SID: c.Value,
|
||||
ASI: asi,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@ -73,3 +94,52 @@ func Login(username, password string) (*Session, error) {
|
||||
}
|
||||
return nil, errors.New("unexpected response status code")
|
||||
}
|
||||
|
||||
// NewSessionBySID checks whether the given session id is valid and if so
|
||||
// a new Session with the session id and asi will be created and returned.
|
||||
func NewSessionBySID(sid string) (*Session, error) {
|
||||
s := &Session{
|
||||
SID: sid,
|
||||
}
|
||||
valid, err := s.Valid()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not check session")
|
||||
}
|
||||
if !valid {
|
||||
return nil, ErrInvalSID
|
||||
}
|
||||
asi, err := asi(sid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get asi")
|
||||
}
|
||||
return &Session{
|
||||
SID: sid,
|
||||
ASI: asi,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func asi(sid string) (string, error) {
|
||||
client := &http.Client{}
|
||||
// GET Request with JESSIONID cookie to sitemap endpoint
|
||||
req, err := http.NewRequest("GET", "https://lsf.hs-worms.de/qisserver/rds?state=sitemap&topitem=leer&breadCrumbSource=portal", nil)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not prepare the request")
|
||||
}
|
||||
req.Header.Add("Cookie", fmt.Sprintf("JSESSIONID=%s", sid))
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not do the request")
|
||||
}
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not read the response body")
|
||||
}
|
||||
// The asi is in some links as GET parameter.
|
||||
// Filter it with regexp
|
||||
re := regexp.MustCompile(`asi=([^"]+)`)
|
||||
match := re.FindSubmatch(b)
|
||||
if len(match) != 2 {
|
||||
return "", errors.New("no asi found")
|
||||
}
|
||||
return string(match[1]), nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user