[TASK] multiple Formats

This commit is contained in:
Christian Seyfferth 2020-06-09 09:21:20 +02:00
parent c9197fc92c
commit 2a1c514e4f

910
app.js
View File

@ -1,389 +1,521 @@
if (!String.prototype.includes) { if (!String.prototype.includes) {
String.prototype.includes = function () { String.prototype.includes = function () {
'use strict'; 'use strict';
return String.prototype.indexOf.apply(this, arguments) !== -1; return String.prototype.indexOf.apply(this, arguments) !== -1;
}; };
} }
//extend FileReader //extend FileReader
if (!FileReader.prototype.readAsBinaryString) { if (!FileReader.prototype.readAsBinaryString) {
FileReader.prototype.readAsBinaryString = function (fileData) { FileReader.prototype.readAsBinaryString = function (fileData) {
var binary = ""; var binary = "";
var pt = this; var pt = this;
var reader = new FileReader(); var reader = new FileReader();
reader.onload = function (e) { reader.onload = function (e) {
var bytes = new Uint8Array(reader.result); var bytes = new Uint8Array(reader.result);
var length = bytes.byteLength; var length = bytes.byteLength;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]); binary += String.fromCharCode(bytes[i]);
} }
//pt.result - readonly so assign binary //pt.result - readonly so assign binary
pt.content = binary; pt.content = binary;
$(pt).trigger('onload'); $(pt).trigger('onload');
} }
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
} }
} }
var options = { var options = {
timepicker: { timepicker: {
twelveHour: false, twelveHour: false,
} }
}; };
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
M.AutoInit(); M.AutoInit();
var elems = document.querySelectorAll('.timepicker'); var elems = document.querySelectorAll('.timepicker');
var timepickers = M.Timepicker.init(elems, options.timepicker); var timepickers = M.Timepicker.init(elems, options.timepicker);
}); });
//localStorage persistence //localStorage persistence
var SHIFT_STORAGE_KEY = "dienstplan_chrosey"; var SHIFT_STORAGE_KEY = "dienstplan_chrosey";
var RULE_STORAGE_KEY = "regeln_chrosey"; var RULE_STORAGE_KEY = "regeln_chrosey";
var shiftStorage = { var shiftStorage = {
fetch: function () { fetch: function () {
'use strict'; 'use strict';
var parsed = JSON.parse(localStorage.getItem(SHIFT_STORAGE_KEY) || '[]'), var parsed = JSON.parse(localStorage.getItem(SHIFT_STORAGE_KEY) || '[]'),
shifts = []; shifts = [];
parsed.forEach(function (el, index) { parsed.forEach(function (el, index) {
var shift = Shift.thaw(el); var shift = Shift.thaw(el);
shift.id = index; shift.id = index;
shifts.push(shift); shifts.push(shift);
}); });
shiftStorage.uid = shifts.length; shiftStorage.uid = shifts.length;
return shifts; return shifts;
}, },
save: function (shifts) { save: function (shifts) {
'use strict'; 'use strict';
var json = JSON.stringify(shifts) var json = JSON.stringify(shifts)
localStorage.setItem(SHIFT_STORAGE_KEY, json); localStorage.setItem(SHIFT_STORAGE_KEY, json);
}, },
count: function () { count: function () {
return JSON.parse(localStorage.getItem(SHIFT_STORAGE_KEY) || '[]').length; return JSON.parse(localStorage.getItem(SHIFT_STORAGE_KEY) || '[]').length;
} }
}; };
var ruleStorage = { var ruleStorage = {
fetch: function () { fetch: function () {
'use strict'; 'use strict';
var parsed = JSON.parse(localStorage.getItem(RULE_STORAGE_KEY)) | []; var parsed = JSON.parse(localStorage.getItem(RULE_STORAGE_KEY)) | [];
var rules = parsed.length > 0 var rules = parsed.length > 0
? parsed.map((e,i) => { ? parsed.map((e,i) => {
var r = Rule.thaw(e); var r = Rule.thaw(e);
r.id = i; r.id = i;
return r; return r;
}) })
: Rule.defaults(); : Rule.defaults();
ruleStorage.uid = rules.length; ruleStorage.uid = rules.length;
return rules; return rules;
}, },
save: function (rules) { save: function (rules) {
'use strict'; 'use strict';
var json = JSON.stringify(rules); var json = JSON.stringify(rules);
localStorage.setItem(RULE_STORAGE_KEY, json); localStorage.setItem(RULE_STORAGE_KEY, json);
} }
}; };
Vue.component('chip-input', { Vue.component('ask-format-modal',{
template: ` template: `
<div class="chips no-autoinit" :id="name"></div> <div id="ask-format-modal" class="modal">
`, <div class="modal-content">
data() { <h4>Dienstplanformat</h4>
return { <p>Welches Format soll eingelesen werden?</p>
instance: null, <label v-for="option in options" >
chips: [] <input name="dpFormat" type="radio" :value="option" v-model="picked"/>
} <span>{{ option }}</span>
}, </label>
<p>Ausgewählt: {{ picked }}</p>
computed: { </div>
chipsData() { <div class="modal-footer">
return this.instance.chipsData; <a href="#!" class="modal-close waves-effect waves-green btn-flat" @click.stop="submitPick">Bastätigen</a>
} </div>
}, </div>
`,
watch:{ data() {
initData:{ return {
deep: true, picked: null
handler(n,o) { }
if (n !== o) { },
this.initialize(); props: ["options"],
this.$emit('init'); methods: {
} submitPick() {
} this.$emit('picked-format', this.picked);
} $('#ask-format-modal').modal('close');
}, }
props: { }
name: String,
initData: Array });
},
Vue.component('chip-input', {
methods: { template: `
initialize() { <div class="chips no-autoinit" :id="name"></div>
this.chips = this.initData.map(e => e); `,
var el = $('#'+this.name)[0]; data() {
this.instance = M.Chips.init(el, { return {
data: this.chips, instance: null,
onChipAdd: () => { chips: []
this.$emit("change",this.chipsData); }
}, },
onChipDelete: () => {
this.$emit("change",this.chipsData); computed: {
} chipsData() {
}); return this.instance.chipsData;
} }
}, },
mounted() { watch:{
this.initialize(); initData:{
} deep: true,
}); handler(n,o) {
if (n !== o) {
var app = new Vue({ this.initialize();
el: '#app', this.$emit('init');
data: { }
shifts: shiftStorage.fetch(), }
rules: ruleStorage.fetch(), }
icsFile: null, },
blob: null, props: {
dp_sheet: '', name: String,
deletedShift: '', initData: Array
remaining: shiftStorage.count(), },
selectedShift: new Shift({}),
selectedShiftIndex: -1, methods: {
selectedRule: new Rule({}), initialize() {
selectedRuleIndex: -1, this.chips = this.initData.map(e => e);
saveto: 'dienstplan.ics', var el = $('#'+this.name)[0];
uploadFileName: "", this.instance = M.Chips.init(el, {
config: { data: this.chips,
moment: { onChipAdd: () => {
parse_formats: [ this.$emit("change",this.chipsData);
"ddd, DD/ MMM. 'YY HH:mm", },
"ddd, DD/ MMM. YYYY HH:mm" onChipDelete: () => {
], this.$emit("change",this.chipsData);
parse_language: 'en', }
display_language: 'de' });
}, }
toast: { },
displayLength: 3000
} mounted() {
}, this.initialize();
}, }
watch: { });
shifts: {
handler: function (shifts) { var app = new Vue({
'use strict'; el: '#app',
shiftStorage.save(shifts); data: {
this.remaining = shifts.length; shifts: shiftStorage.fetch(),
this.makeToast("Änderungen gespeichert."); rules: ruleStorage.fetch(),
}, icsFile: null,
deep: true blob: null,
}, dp_sheet: '',
rules: { deletedShift: '',
handler: function (rules) { remaining: shiftStorage.count(),
'use strict'; selectedShift: new Shift({}),
ruleStorage.save(rules); selectedShiftIndex: -1,
this.makeToast("Änderungen gespeichert."); selectedRule: new Rule({}),
}, selectedRuleIndex: -1,
deep: true saveto: 'dienstplan.ics',
} uploadFileName: "",
}, availableFormats: ["Erfurt","Stuttgart","X"],
config: {
computed: { moment: {
groupedTermine() { parse_formats: [
return _.groupBy(this.shifts, e => e.Datum); "ddd, DD/ MMM. 'YY HH:mm",
} "ddd, DD/ MMM. YYYY HH:mm"
}, ],
parse_language: 'en',
methods: { display_language: 'de'
updateArten(value){ },
this.selectedRule.Arten = value; toast: {
}, displayLength: 3000
}
updateTitel(value){ },
this.selectedRule.Titel = value; workbook: null,
}, },
watch: {
makeToast(message) { shifts: {
var toastOptions = this.config.toast; handler: function (shifts) {
toastOptions.html = message; 'use strict';
M.toast(toastOptions); shiftStorage.save(shifts);
}, this.remaining = shifts.length;
this.makeToast("Änderungen gespeichert.");
onFileChange: function (event) { },
var files = event.target.files || event.dataTransfer.files; deep: true
this.handleInputFile(files[0]); },
this.uploadFileName = files[0].name + " [" + Math.round(files[0].size / 1024) + " kB]"; rules: {
this.makeToast(this.uploadFileName + " ausgewählt"); handler: function (rules) {
}, 'use strict';
ruleStorage.save(rules);
handleInputFile: function (file) { this.makeToast("Änderungen gespeichert.");
var reader = new FileReader(); },
var vm = this; deep: true
reader.onload = (e) => { }
var data = !e ? reader.content : e.target.result; },
var workbook = XLSX.read(data, {
type: 'binary', computed: {
cellDates: true, groupedTermine() {
}); return _.groupBy(this.shifts, e => e.Datum);
var isErfurterDienstplan = workbook.SheetNames.indexOf("Dienstplan") > -1; }
},
if(isErfurterDienstplan){
vm.parseForErfurt(workbook.Sheets["Dienstplan"]); methods: {
} else { updateArten(value){
var sheetName = workbook.SheetNames[0]; this.selectedRule.Arten = value;
vm.parseForStuttgart(workbook.Sheets[sheetName]); },
}
}; updateTitel(value){
reader.readAsBinaryString(file); this.selectedRule.Titel = value;
}, },
parseForErfurt: function (dp) { makeToast(message) {
var arr = XLSX.utils.sheet_to_row_object_array(dp, { var toastOptions = this.config.toast;
range: 1 toastOptions.html = message;
}); M.toast(toastOptions);
var vm = this; },
var day;
onFileChange: function (event) {
this.makeToast("Erfurter Dienstplan erkannt."); var files = event.target.files || event.dataTransfer.files;
this.handleInputFile(files[0]);
arr.forEach(element => { this.uploadFileName = files[0].name + " [" + Math.round(files[0].size / 1024) + " kB]";
moment.locale(vm.config.moment.parse_language); this.makeToast(this.uploadFileName + " ausgewählt");
if (element.hasOwnProperty('Datum')) { },
day = moment(element.Datum);
} handleInputFile: function (file) {
if (element.hasOwnProperty('Dienst')) { var reader = new FileReader();
var time = element.Zeit ? moment(element.Zeit) : day; var vm = this;
var termin = { reader.onload = (e) => {
datum: day.clone().hour(time.hour()).minute(time.minute()), var data = !e ? reader.content : e.target.result;
art: element.Dienst ? element.Dienst.trim() : "", var workbook = XLSX.read(data, {
beschreibung: element.Bemerkung ? element.Bemerkung.trim(): "", type: 'binary',
name: element.__EMPTY ? element.__EMPTY.trim() : "" cellDates: true,
} });
vm.addShift(new Shift(termin)); var isErfurterDienstplan = workbook.SheetNames.indexOf("Dienstplan") > -1;
}
}); if(isErfurterDienstplan){
}, vm.parseForErfurt(workbook.Sheets["Dienstplan"]);
} else {
parseForStuttgart: function (dp) { this.workbook = workbook;
var arr = XLSX.utils.sheet_to_json(dp, { this.askForDienstplanFormat();
header: "A", }
blankrows: false, };
}); reader.readAsBinaryString(file);
var vm = this; },
var day;
askForDienstplanFormat: function() {
this.makeToast("Stuttgarter Dienstplan erkannt."); this.makeToast("Dienstplanformat nicht erkannt.");
$('#ask-format-modal').modal("open");
arr.forEach(element => { },
moment.locale(vm.config.moment.parse_language);
if (element.hasOwnProperty('C')) { parseForErfurt: function (dp) {
day = moment(element.C); var arr = XLSX.utils.sheet_to_row_object_array(dp, {
} range: 1
if (element.hasOwnProperty('D') && moment(element.D, "HH:mm").isValid()) { });
var termin = { var vm = this;
ort: element.H ? element.H.trim() : "", var day;
art: element.E ? element.E.trim() : "",
beschreibung: element.H ? element.H.trim(): "", this.makeToast("Erfurter Dienstplan erkannt.");
name: element.F ? element.F.trim() : ""
} arr.forEach(element => {
var time = day.clone(); moment.locale(vm.config.moment.parse_language);
if (typeof(element.D) === "object") { if (element.hasOwnProperty('Datum')) {
time = moment(element.D); day = moment(element.Datum);
termin.datum = day.clone().hour(time.hour()).minute(time.minute()); }
} else if(element.D.indexOf("-") > -1 ) { if (element.hasOwnProperty('Dienst')) {
var tArray = element.D.split(" - "); var time = element.Zeit ? moment(element.Zeit) : day;
time = moment(tArray[0],"HH:mm"); var termin = {
termin.datum = day.clone().hour(time.hour()).minute(time.minute()); datum: day.clone().hour(time.hour()).minute(time.minute()),
termin.ende = moment(tArray[1], "HH:mm").format("HH:mm"); art: element.Dienst ? element.Dienst.trim() : "",
} beschreibung: element.Bemerkung ? element.Bemerkung.trim(): "",
vm.addShift(new Shift(termin)); name: element.__EMPTY ? element.__EMPTY.trim() : ""
}
} vm.addShift(new Shift(termin));
}); }
}, });
},
addShift: function (shift) {
this.shifts.push(shift); parseForStuttgart: function (dp) {
}, var arr = XLSX.utils.sheet_to_json(dp, {
header: "A",
removeShift: function (shift) { blankrows: false,
this.shifts.splice(this.shifts.indexOf(shift), 1); });
this.makeToast(shift.VEventTitle + " gelöscht"); var vm = this;
}, var day;
cleanStorage: function () { this.makeToast("Stuttgarter Dienstplan erkannt.");
this.shifts = [];
this.makeToast("Alle Einträge gelöscht"); arr.forEach(element => {
}, moment.locale(vm.config.moment.parse_language);
if (element.hasOwnProperty('C')) {
createDownloadFile: function () { day = moment(element.C);
var vCal = new VCalendar("Dienstplan Kalender"); }
this.shifts.forEach(function (shift) { if (element.hasOwnProperty('D') && moment(element.D, "HH:mm").isValid()) {
vCal.add(shift.toVEvent()); var termin = {
}); ort: element.H ? element.H.trim() : "",
var calString = vCal.toString(); art: element.E ? element.E.trim() : "",
this.blob = new Blob([calString], { beschreibung: element.H ? element.H.trim(): "",
type: 'text/plain' name: element.F ? element.F.trim() : ""
}); }
var time = day.clone();
if (this.icsFile !== null) { if (typeof(element.D) === "object") {
window.URL.revokeObjectURL(this.icsFile); time = moment(element.D);
} termin.datum = day.clone().hour(time.hour()).minute(time.minute());
} else if(element.D.indexOf("-") > -1 ) {
this.icsFile = window.URL.createObjectURL(this.blob); var tArray = element.D.split(" - ");
this.makeToast(this.saveto + " erstellt."); time = moment(tArray[0],"HH:mm");
}, termin.datum = day.clone().hour(time.hour()).minute(time.minute());
termin.ende = moment(tArray[1], "HH:mm").format("HH:mm");
downloadFile: function () { }
if (window.navigator.msSaveOrOpenBlob) { vm.addShift(new Shift(termin));
window.navigator.msSaveOrOpenBlob(this.blob, this.saveto);
} }
}, });
},
selectShift: function (shift) {
this.selectedShift = Shift.thaw(shift); parseForX: function (dp, sheetName) {
this.keepShift = shift; moment.locale(this.config.moment.parse_language);
M.updateTextFields(); var arr = XLSX.utils.sheet_to_json(dp, {
$('#shiftModal').modal('open'); header: "A",
}, blankrows: false,
});
saveChanges: function (changedShift) { var vm = this;
this.shifts.splice(this.shifts.indexOf(this.keepShift), 1, changedShift); var month = moment(sheetName.substr(sheetName.indexOf(" ")+1), "MMMM YYYY", "de");
var day;
$('#shiftModal').modal('close');
this.keepShift = ''; arr.forEach(element => {
this.selectedShift = ''; var art, name, beschreibung = null;
}, var time = moment().hour(0).minute(0);
try {
discardChanges: function (changedShift) { if (element.hasOwnProperty('A')) {
$('#shiftModal').modal('close'); day = moment(element.A, "D.","de");
this.keepShift = ''; day = Number(element.A);
this.selectedShift = ''; }
}, if ((!element.F && !element.H && !element.J) || element.I == "Spielort/ Extras" || element.J == "Spielort/Extras"){
editRule: function (rule) { } else {
this.selectedRule = Rule.thaw(rule);
this.selectedRuleIndex = this.rules.indexOf(rule);
$('#ruleModal').modal('open'); if (element.hasOwnProperty("D") && element.hasOwnProperty("E")) {
}, // Probe
time = moment(element.D);
saveRule: function () { art = element.E.trim();
this.rules[this.selectedRuleIndex] = this.selectedRule; name = element.F ? element.F.trim() : "";
beschreibung = typeof(element.I) != 'undefined' ? element.I.trim()
$('#ruleModal').modal('close'); : typeof(element.J) != 'undefined' ? element.J.trim()
this.selectedRule = new Rule(); : "";
}, }
else if (element.hasOwnProperty("G") && element.hasOwnProperty("H"))
discardRule: function () { {
$('#ruleModal').modal('close'); // Vorstellung
time = moment(element.G);
this.selectedRule = new Rule(); art = "VS";
} name = element.H.trim();
beschreibung = typeof(element.I) != 'undefined' ? element.I.trim()
}, : typeof(element.J) != 'undefined' ? element.J.trim()
directives: { : "";
'edit-focus': function (el, value) { }
if (value) { else if (element.hasOwnProperty("I"))
el.focus(); {
} // Spielort/Extras
} beschreibung = element.I.trim();
} name = name ? name : "Spielort/Extras";
}) }
else if (element.hasOwnProperty("J"))
{
// Spielort/Extras Fallback
beschreibung = element.J.trim();
name = name ? name : "Spielort/Extras";
}
var datumStr = day + '.' + month.clone().format("MM.YY") + time.format(" HH:mm");
var termin = {
ort: "",
art: art,
beschreibung: beschreibung,
name: name,
datum: moment(datumStr, "D.MM.YY HH:mm")
}
vm.addShift(new Shift(termin));
}
} catch (error) {
console.error("Fehler beim Parsen", element, error);
}
});
},
parsePickedFormat: function (format) {
this.makeToast(`Versuche ${format} zu lesen.`);
switch (format) {
case "Erfurt":
this.parseForErfurt(this.workbook.Sheets["Dienstplan"]);
break;
case "Stuttgart":
var sheetName = this.workbook.SheetNames[0];
this.parseForStuttgart(this.workbook.Sheets[sheetName]);
break;
case "X":
var sheetName = this.workbook.SheetNames[0];
this.parseForX(this.workbook.Sheets[sheetName], sheetName);
break;
default:
break;
}
this.workbook = null;
},
addShift: function (shift) {
this.shifts.push(shift);
},
removeShift: function (shift) {
this.shifts.splice(this.shifts.indexOf(shift), 1);
this.makeToast(shift.VEventTitle + " gelöscht");
},
cleanStorage: function () {
this.shifts = [];
this.makeToast("Alle Einträge gelöscht");
},
createDownloadFile: function () {
var vCal = new VCalendar("Dienstplan Kalender");
this.shifts.forEach(function (shift) {
vCal.add(shift.toVEvent());
});
var calString = vCal.toString();
this.blob = new Blob([calString], {
type: 'text/plain'
});
if (this.icsFile !== null) {
window.URL.revokeObjectURL(this.icsFile);
}
this.icsFile = window.URL.createObjectURL(this.blob);
this.makeToast(this.saveto + " erstellt.");
},
downloadFile: function () {
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(this.blob, this.saveto);
}
},
selectShift: function (shift) {
this.selectedShift = Shift.thaw(shift);
this.keepShift = shift;
M.updateTextFields();
$('#shiftModal').modal('open');
},
saveChanges: function (changedShift) {
this.shifts.splice(this.shifts.indexOf(this.keepShift), 1, changedShift);
$('#shiftModal').modal('close');
this.keepShift = '';
this.selectedShift = '';
},
discardChanges: function (changedShift) {
$('#shiftModal').modal('close');
this.keepShift = '';
this.selectedShift = '';
},
editRule: function (rule) {
this.selectedRule = Rule.thaw(rule);
this.selectedRuleIndex = this.rules.indexOf(rule);
$('#ruleModal').modal('open');
},
saveRule: function () {
this.rules[this.selectedRuleIndex] = this.selectedRule;
$('#ruleModal').modal('close');
this.selectedRule = new Rule();
},
discardRule: function () {
$('#ruleModal').modal('close');
this.selectedRule = new Rule();
}
},
directives: {
'edit-focus': function (el, value) {
if (value) {
el.focus();
}
}
}
})