timetable-extension #1
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							@ -23,4 +23,5 @@ require (
 | 
			
		||||
	golang.org/x/crypto v0.24.0 // indirect
 | 
			
		||||
	golang.org/x/sync v0.7.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.16.0 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.sum
									
									
									
									
									
								
							@ -96,6 +96,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								pis/data.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								pis/data.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
package pis
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/yaml.v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Process the YAML data to a struct
 | 
			
		||||
func processYaml(yamlStr string) (*PisData, error) {
 | 
			
		||||
	var pis PisData
 | 
			
		||||
 | 
			
		||||
	err := yaml.Unmarshal([]byte(yamlStr), &pis)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to unmarshal YAML: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = deduplicateCodes(&pis)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &pis, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deduplicate data in place and return error if failed
 | 
			
		||||
func deduplicateCodes(pis *PisData) error {
 | 
			
		||||
	return fmt.Errorf("deduplication logic not present, unable to update")
 | 
			
		||||
}
 | 
			
		||||
@ -7,3 +7,9 @@ type GiteaReleaseData struct {
 | 
			
		||||
	Draft      bool   `json:"draft"`
 | 
			
		||||
	Prerelease bool   `json:"prerelease"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PisData struct {
 | 
			
		||||
	Code     string   `json:"code"`
 | 
			
		||||
	Stops    []string `json:"stops"`
 | 
			
		||||
	Operator string   `json:"operator"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										161
									
								
								pis/update.go
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								pis/update.go
									
									
									
									
									
								
							@ -1,11 +1,166 @@
 | 
			
		||||
package pis
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"archive/tar"
 | 
			
		||||
	"compress/gzip"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	destPath    = "/tmp/pis.tar.gz"
 | 
			
		||||
	extractPath = "/tmp/extract"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Downloads the release tarball, extracts then applies to database
 | 
			
		||||
func runUpdate(tarballUrl string) error {
 | 
			
		||||
	// Download and extract tarball
 | 
			
		||||
	// load PIS yaml files
 | 
			
		||||
	// Run code deduplication
 | 
			
		||||
	err := downloadFile(tarballUrl, destPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Extract to disk
 | 
			
		||||
	file, err := os.Open(destPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error opening file: %v\n", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	if err := extractFiles(file, extractPath); err != nil {
 | 
			
		||||
		fmt.Printf("Error extracting file: %v\n", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Load YAML to string
 | 
			
		||||
	pisData, err := extractYamlData(extractPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Replace database collection with new data
 | 
			
		||||
	// Ensure indeces are present
 | 
			
		||||
 | 
			
		||||
	// Cleanup files
 | 
			
		||||
	cleanupFiles(destPath, extractPath)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Download the tarball to disk
 | 
			
		||||
func downloadFile(url, filepath string) error {
 | 
			
		||||
	resp, err := http.Get(url)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	out, err := os.Create(filepath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer out.Close()
 | 
			
		||||
 | 
			
		||||
	_, err = io.Copy(out, resp.Body)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract tarball to disk
 | 
			
		||||
func extractFiles(gzipStream io.Reader, dest string) error {
 | 
			
		||||
	uncompressedStream, err := gzip.NewReader(gzipStream)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer uncompressedStream.Close()
 | 
			
		||||
 | 
			
		||||
	tarReader := tar.NewReader(uncompressedStream)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		header, err := tarReader.Next()
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		filePath := filepath.Join(dest, header.Name)
 | 
			
		||||
 | 
			
		||||
		switch header.Typeflag {
 | 
			
		||||
		case tar.TypeDir:
 | 
			
		||||
			if err := os.MkdirAll(filepath.Join(dest, header.Name), 0755); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		case tar.TypeReg:
 | 
			
		||||
			outFile, err := os.Create(filePath)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if _, err := io.Copy(outFile, tarReader); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			outFile.Close()
 | 
			
		||||
		default:
 | 
			
		||||
			fmt.Printf("Unable to handle filetype %c in %s\n", header.Typeflag, header.Name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return YAML PIS files as a string
 | 
			
		||||
func extractYamlData(dir string) (string, error) {
 | 
			
		||||
	var allContent strings.Builder // Using a string builder to accumulate content
 | 
			
		||||
 | 
			
		||||
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err // Only returning error since Walk callback doesn't accept string
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check if the path contains 'pis' and has a .yaml or .yml extension
 | 
			
		||||
		if strings.Contains(path, "/pis/") && !info.IsDir() &&
 | 
			
		||||
			(strings.HasSuffix(info.Name(), ".yaml") || strings.HasSuffix(info.Name(), ".yml")) {
 | 
			
		||||
			fmt.Printf("Processing YAML file in 'pis' directory: %s\n", path)
 | 
			
		||||
 | 
			
		||||
			file, err := os.Open(path)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("failed to open YAML file %s: %v", path, err)
 | 
			
		||||
			}
 | 
			
		||||
			defer file.Close()
 | 
			
		||||
 | 
			
		||||
			content, err := io.ReadAll(file)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("failed to read YAML file %s: %v", path, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Accumulate content from each YAML file in 'pis' directory
 | 
			
		||||
			allContent.Write(content)
 | 
			
		||||
			allContent.WriteString("\n") // Add a newline between file contents
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Return the accumulated content as a single string
 | 
			
		||||
	return allContent.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cleans up downloaded and extracted files
 | 
			
		||||
func cleanupFiles(paths ...string) {
 | 
			
		||||
	for _, path := range paths {
 | 
			
		||||
		err := os.RemoveAll(path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Error removing %s: %v\n", path, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user