2018-11-17 23:25:38 +00:00
|
|
|
// TODO: docstring
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/csv"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
2018-11-25 13:21:21 +00:00
|
|
|
"strconv"
|
2018-11-17 23:25:38 +00:00
|
|
|
"text/template"
|
2018-11-18 20:48:58 +00:00
|
|
|
"time"
|
2018-11-17 23:25:38 +00:00
|
|
|
)
|
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
var timeCSVFormat = "2.1.06 15:04" // input format
|
|
|
|
var timeXMLDateFormat = "2006-01-02" // frab xml format for dates
|
|
|
|
var timeXMLTimeFormat = "15:04" // frab xml format for times
|
|
|
|
|
|
|
|
const csvColTitle = 0
|
|
|
|
const csvColRoom = 1
|
|
|
|
const csvColStart = 2
|
|
|
|
const csvColEnd = 3
|
|
|
|
const csvColSpeaker = 4
|
|
|
|
const csvColLang = 5
|
|
|
|
|
2018-11-17 23:25:38 +00:00
|
|
|
type frabSchedule struct {
|
|
|
|
Conf conference
|
|
|
|
Days []day
|
|
|
|
}
|
|
|
|
|
|
|
|
type conference struct {
|
|
|
|
Title string
|
|
|
|
Start string // date (yyyy-mm-dd)
|
|
|
|
End string // date (yyyy-mm-dd)
|
|
|
|
Days int
|
|
|
|
}
|
|
|
|
|
|
|
|
type day struct {
|
|
|
|
Index int
|
|
|
|
Date string // date (yyyy-mm-dd)
|
|
|
|
Start string // timedate (RFC3339)
|
|
|
|
End string // timedate (RFC3339)
|
|
|
|
Rooms []room
|
|
|
|
}
|
|
|
|
|
|
|
|
type room struct {
|
|
|
|
Name string
|
|
|
|
Events []event
|
|
|
|
}
|
|
|
|
|
|
|
|
type event struct {
|
|
|
|
ID int
|
2018-11-18 20:48:58 +00:00
|
|
|
GUID string // autogen UUID
|
2018-11-17 23:25:38 +00:00
|
|
|
|
|
|
|
Room string
|
|
|
|
Title string
|
|
|
|
Date string // timedate (RFC3339)
|
|
|
|
Start string // time (hh:mm)
|
|
|
|
Duration string // time (hh:mm)
|
2018-11-18 20:48:58 +00:00
|
|
|
Language string
|
2018-11-17 23:25:38 +00:00
|
|
|
|
|
|
|
Persons []person
|
|
|
|
}
|
|
|
|
|
|
|
|
type person struct {
|
|
|
|
ID int
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
func fmtDuration(d time.Duration) string {
|
|
|
|
d = d.Round(time.Minute)
|
|
|
|
h := d / time.Hour
|
|
|
|
d -= h * time.Hour
|
|
|
|
m := d / time.Minute
|
|
|
|
return fmt.Sprintf("%02d:%02d", h, m)
|
|
|
|
}
|
|
|
|
|
2018-11-17 23:25:38 +00:00
|
|
|
func main() {
|
2018-11-18 20:48:58 +00:00
|
|
|
fmt.Println("Conference Schedule: CSV to Frab (Infobeamer) Converter")
|
2018-11-17 23:25:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Data Input
|
|
|
|
*/
|
|
|
|
// read CSV contents
|
|
|
|
csvFile, err := os.Open("schedule.csv")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error opening CSV file:\n", err)
|
|
|
|
}
|
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
csvReader := csv.NewReader(csvFile)
|
|
|
|
csvReader.Comma = ';' // MS excel default export format
|
|
|
|
records, err := csvReader.ReadAll()
|
2018-11-17 23:25:38 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error reading CSV file:\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// omit title row
|
|
|
|
csvRecords := records[1:]
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Data Conversion
|
|
|
|
*/
|
2018-11-25 13:21:21 +00:00
|
|
|
roomsList := make(map[time.Time]map[string]int) // map of day's start time to map of room names
|
|
|
|
var conferenceStart time.Time
|
|
|
|
var conferenceEnd time.Time
|
|
|
|
loc, _ := time.LoadLocation("Europe/Berlin")
|
|
|
|
|
|
|
|
for idx, row := range csvRecords {
|
|
|
|
// parse event times
|
|
|
|
eventStart, err := time.ParseInLocation(timeCSVFormat, row[csvColStart], loc)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error parsing start date in CSV file, line ", idx+1, ":\n", err)
|
|
|
|
}
|
|
|
|
eventEnd, err := time.ParseInLocation(timeCSVFormat, row[csvColEnd], loc)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error parsing end date in CSV file, line ", idx+1, ":\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create unique list of the rooms for each day
|
|
|
|
dayStart := eventStart.Truncate(24 * time.Hour)
|
|
|
|
if roomsList[dayStart] == nil {
|
|
|
|
roomsList[dayStart] = make(map[string]int)
|
|
|
|
}
|
|
|
|
roomsList[dayStart][row[csvColRoom]] = 1
|
|
|
|
|
|
|
|
// get conference start and end date
|
|
|
|
if idx == 0 {
|
|
|
|
conferenceStart = eventStart
|
|
|
|
conferenceEnd = eventEnd
|
|
|
|
}
|
|
|
|
if eventStart.Before(conferenceStart) {
|
|
|
|
conferenceStart = eventStart
|
|
|
|
}
|
|
|
|
if eventEnd.After(conferenceEnd) {
|
|
|
|
conferenceEnd = eventEnd
|
|
|
|
}
|
|
|
|
}
|
2018-11-17 23:25:38 +00:00
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
conferenceStart = conferenceStart.Truncate(24 * time.Hour)
|
|
|
|
conferenceEnd = conferenceEnd.Truncate(24 * time.Hour).Add(24 * time.Hour)
|
|
|
|
|
|
|
|
// convert csv entries into event and sort in days
|
|
|
|
events := make(map[int][]event) // map of day index to day's events
|
|
|
|
eventID := 100
|
2018-11-25 14:44:03 +00:00
|
|
|
speakerID := 1
|
2018-11-25 13:21:21 +00:00
|
|
|
confDays := make(map[int]day) // map of day index to day structure
|
|
|
|
|
|
|
|
for idx, row := range csvRecords {
|
|
|
|
eventStart, err := time.ParseInLocation(timeCSVFormat, row[csvColStart], loc)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error parsing start date in CSV file, line ", idx+1, ":\n", err)
|
|
|
|
}
|
|
|
|
eventEnd, err := time.ParseInLocation(timeCSVFormat, row[csvColEnd], loc)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error parsing end date in CSV file, line ", idx+1, ":\n", err)
|
|
|
|
}
|
|
|
|
|
2018-11-25 14:44:03 +00:00
|
|
|
// create event struct and sort into day map
|
|
|
|
var eventSpeaker []person
|
|
|
|
if row[csvColSpeaker] != "" {
|
|
|
|
eventSpeaker = append(eventSpeaker, person{speakerID, row[csvColSpeaker]})
|
|
|
|
}
|
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
dayIdx := int(eventStart.Sub(conferenceStart).Hours()/24) + 1
|
|
|
|
eventGUID := strconv.Itoa(dayIdx) + "-" + strconv.Itoa(eventID)
|
|
|
|
eventDateStr := eventStart.Format(time.RFC3339)
|
|
|
|
eventStartStr := eventStart.Format(timeXMLTimeFormat)
|
|
|
|
eventDurationStr := fmtDuration(eventEnd.Sub(eventStart))
|
|
|
|
|
2018-11-25 14:44:03 +00:00
|
|
|
events[dayIdx] = append(events[dayIdx], event{eventID, eventGUID, row[csvColRoom], row[csvColTitle], eventDateStr, eventStartStr, eventDurationStr, row[csvColLang], eventSpeaker})
|
2018-11-25 13:21:21 +00:00
|
|
|
|
|
|
|
// create days
|
|
|
|
if _, ok := confDays[dayIdx]; !ok {
|
|
|
|
dayDateStr := eventStart.Format(timeXMLDateFormat)
|
|
|
|
dayStart := eventStart.Truncate(24 * time.Hour)
|
|
|
|
dayEnd := dayStart.Add(24 * time.Hour)
|
|
|
|
dayStartStr := dayStart.Format(time.RFC3339)
|
|
|
|
dayEndStr := dayEnd.Format(time.RFC3339)
|
|
|
|
|
|
|
|
var dayRooms []room
|
|
|
|
for roomName := range roomsList[dayStart] {
|
|
|
|
dayRooms = append(dayRooms, room{roomName, nil})
|
|
|
|
}
|
|
|
|
|
|
|
|
confDays[dayIdx] = day{dayIdx, dayDateStr, dayStartStr, dayEndStr, dayRooms}
|
|
|
|
}
|
|
|
|
|
|
|
|
eventID++
|
|
|
|
}
|
2018-11-18 20:48:58 +00:00
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
// sort events to rooms and day structures
|
|
|
|
for dayIdx, eventDay := range events {
|
|
|
|
for _, event := range eventDay {
|
|
|
|
for roomIdx, roomName := range confDays[dayIdx].Rooms {
|
|
|
|
if roomName.Name == event.Room {
|
|
|
|
confDays[dayIdx].Rooms[roomIdx].Events = append(confDays[dayIdx].Rooms[roomIdx].Events, event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-17 23:25:38 +00:00
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
// new conference
|
|
|
|
confDurationDays := int(conferenceEnd.Sub(conferenceStart).Hours() / 24)
|
|
|
|
conf := conference{"Wälderhaus Veranstaltung", conferenceStart.Format(time.RFC3339), conferenceEnd.Format(time.RFC3339), confDurationDays}
|
2018-11-17 23:25:38 +00:00
|
|
|
|
2018-11-25 13:21:21 +00:00
|
|
|
var days []day
|
|
|
|
for _, d := range confDays {
|
|
|
|
days = append(days, d)
|
|
|
|
}
|
2018-11-17 23:25:38 +00:00
|
|
|
|
|
|
|
sched := frabSchedule{conf, days}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Data Export
|
|
|
|
*/
|
|
|
|
// open output file
|
|
|
|
xmlOutput, err := os.Create("schedule.xml")
|
|
|
|
defer xmlOutput.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error opening XML file for output:\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
t := template.New("Frab XML Template")
|
|
|
|
t, err = t.ParseFiles("tmpl/schedule.xml")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error parsing XML template:\n", err)
|
|
|
|
}
|
|
|
|
err = t.ExecuteTemplate(xmlOutput, "schedule.xml", sched)
|
|
|
|
if err != nil {
|
|
|
|
log.Println("Error executing XML template:\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print Summary
|
|
|
|
*/
|
2018-11-25 13:21:21 +00:00
|
|
|
fmt.Println("- Conference Start:", conferenceStart)
|
|
|
|
fmt.Println("- Conference End :", conferenceEnd)
|
|
|
|
fmt.Println("- Days :", confDurationDays)
|
|
|
|
fmt.Println("- Events :", eventID-100)
|
|
|
|
|
|
|
|
// fmt.Println("Dump days:")
|
|
|
|
// for _, day := range days {
|
|
|
|
// fmt.Println("Day", day.Index)
|
|
|
|
// for _, room := range day.Rooms {
|
|
|
|
// fmt.Println(" Room", room.Name)
|
|
|
|
// for _, event := range room.Events {
|
|
|
|
// fmt.Println(" Event", event)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
2018-11-17 23:25:38 +00:00
|
|
|
|
|
|
|
fmt.Println("done")
|
|
|
|
}
|