Browse Source

erster commit

hmt 6 years ago
commit
6906f79a3a

+ 45 - 0
Eine Liste.html

@@ -0,0 +1,45 @@
+{#each _.chunk(schueler, 10) as slice, i}
+  <div class="page grid" orientation="portrait" size="A4">
+    <div class="header">
+      <b>Klassenliste der {schueler[0].Klasse} – Stand {heute}</b>
+    </div>
+    <div class="main">
+      <Voffset v=1/>
+      <table>
+        {#each slice as s, ii}
+          <tr>
+            <td><b>{i*10+ii+1}</b></td>
+            <td>{s.anrede} <b>{s.Name}, {s.Vorname}</b></td>
+          </tr>
+          <tr>
+            <td></td>
+            <td>{s.Strasse}, {s.PLZ} {s.OrtAbk}</td>
+          </tr>
+          <tr><td></td><td>Telefon: {s.Telefon} – eMail: {s.EMail}</td></tr>
+          <tr>
+            <td></td>
+            <td>geboren am {datum(s.Geburtsdatum)} in {s.Geburtsort}<br></td>
+          </tr>
+        {/each}
+      </table>
+    </div>
+    <div class="footer">Seite {i+1}/{Math.ceil(schueler.length/10)}</div>
+  </div>
+{/each}
+
+<script>
+  import { datum }  from './helfer'
+  const _  = R.lodash
+
+	export default {
+    data () { return { heute: new Date().toLocaleDateString('de', {day: '2-digit', month: '2-digit', year: 'numeric'}) } },
+    helpers: { datum, _ },
+    components: {
+      Voffset: './partials/Voffset.html'
+    }
+  }
+</script>
+
+<style>
+  @import 'css/main.css';
+</style>

+ 85 - 0
Jahreszeugnis.html

@@ -0,0 +1,85 @@
+{#each schueler as s (s.ID)}
+  {#each s.abschnitte.filter(aktHalbjahr) as hj (hj.ID)}
+    <div class="page grid" orientation="portrait" size="A4">
+      <!-- <Seitenlogo/> -->
+      <Pageheader logo=""/>
+      <div class="main">
+        <div style="font-size: 15pt; line-height: 1.5em">
+        </div>
+        <div style="font-size: 12pt; line-height: 1.2em">
+        </div>
+        <Voffset v="1"/>
+        <b>{s.anrede} {s.Vorname} {s.Zusatz || ''} {s.Name},</b>
+        <br />geboren am {datum(s.Geburtsdatum)} in {s.Geburtsort},<br />
+        besuchte im {hj.Abschnitt}. Halbjahr des Schuljahres {hj.schuljahr} die Klasse {hj.Klasse}
+        <Voffset v="2"/>
+        Leistungen laut Konferenzbeschluss vom {datum(hj.Konferenzdatum)}:
+        <Noten noten={hj.noten}></Noten>
+        <hr />
+        Fehlstunden: {Number(hj.SumFehlStd) || 0}{#if Number(hj.SumFehlStdU) > 0}, davon unentschuldigt: {hj.SumFehlStdU}<Voffset v="1"/>{/if}
+        <Voffset v="1"/>
+        <b>{versetzungsvermerk(s,hj)}</b>
+        <Voffset v="1"/>
+        <b>Bemerkungen</b><br />{@html bemerkungen(hj)}
+      </div>
+      <div class="footer">
+        <div class="flex-grid">
+          <div class="col-2">
+            {schule.Ort}, den {datum(hj.ZeugnisDatum)}
+          </div>
+          <div class="col text-center klein">
+          <Voffset v="3"/>
+            <hr />
+            {schule.SchulleiterVorname} {schule.SchulleiterName} <br />{schule.schulleiter_in}
+          </div>
+          <div class="col text-center klein">
+          <Voffset v="3"/>
+            Siegel
+          </div>
+          <div class="col text-center klein">
+          <Voffset v="3"/>
+            <hr />
+            {hj.v_name_klassenlehrer}<br />{hj.klassenlehrer_in}
+          </div>
+        </div>
+        <div class="flex-grid">
+          <div class="col-2"></div>
+          <div class="col-2">
+            <Voffset v="1"/>
+            Die Kenntnisnahme wird bestätigt:
+          </div>
+          <div class="col">
+            <Voffset v="3"/>
+            <hr />
+            <div class="klein text-center">
+              {volljaehrigBei(s, hj.ZeugnisDatum) ? s.schueler_in : 'Erziehungsberechtigte'}
+            </div>
+          </div>
+        </div>
+        <Fussnote schule={schule}></Fussnote>
+      </div>
+    </div>
+  {/each}
+{/each}
+
+<script>
+  import { datum, versetzungsvermerk, volljaehrigBei, bemerkungen }  from './helfer'
+
+  export default {
+    components: {
+      Seitenlogo: './partials/Seitenlogo.html',
+      Pageheader: './partials/Pageheader.html',
+      Noten: './partials/Noten.html',
+      Fussnote: './partials/Fussnoten.html',
+      Voffset: './partials/Voffset.html'
+    },
+    helpers: { datum, versetzungsvermerk, volljaehrigBei, bemerkungen },
+    computed: {
+      aktHalbjahr: ({jahr, abschnitt}) => { return a => a.Jahr === jahr && a.Abschnitt === abschnitt }
+    }
+  }
+</script>
+
+<style>
+  @import 'css/main.css';
+</style>

+ 70 - 0
Notenliste.html

@@ -0,0 +1,70 @@
+{#each _.chunk(schueler, anzahl) as slice, i}
+  <div class="page grid" orientation="landscape" size="A4">
+    <div class="header">
+      <b>Notenliste der {schueler[0].Klasse} – Stand {heute}</b>
+    </div>
+    <div class="main">
+      <Voffset v=1/>
+      <table class="table-bordered">
+        <tr>
+          <td><b>Nr</b></td><td><b>Name</b></td>
+          {#each Array.from(faecher) as f}
+            <td class="text-center"><b>{f}</b></td>
+          {/each}
+        </tr>
+        {#each slice as s, ii}
+          <tr>
+            <td><b>{i*10+ii+1}</b></td>
+            <td><b>{s.Name}, {s.Vorname}</b></td>
+            {#each Object.entries(noten) as [fach, noten]}
+              <td class="text-center" style={parseInt(noten[i*anzahl+ii]) > 4 ? 'background:tomato;':''}>{noten[i*anzahl+ii]}</td>
+            {/each}
+          </tr>
+        {/each}
+      </table>
+    </div>
+    <div class="footer">Seite {i+1}/{Math.ceil(schueler.length/anzahl)}</div>
+  </div>
+{/each}
+
+<script>
+  const _  = R.lodash
+
+	export default {
+    data () {
+      return {
+        anzahl: 20,
+        heute: new Date().toLocaleDateString('de', {day: '2-digit', month: '2-digit', year: 'numeric'})
+      }
+    },
+    computed: {
+      aktHalbjahr: ({jahr, abschnitt}) => { return a => a.Jahr === jahr && a.Abschnitt === abschnitt },
+      faecher: ({schueler, aktHalbjahr}) => {
+        let faecher = new Set
+        schueler.forEach(s => s.abschnitte.find(aktHalbjahr).noten.forEach(n => {faecher.add(n.fach.FachKrz)}))
+        return faecher
+      },
+      noten: ({schueler, faecher, aktHalbjahr}) => {
+        let noten = {}
+        faecher.forEach(f => {noten[f] = []})
+        schueler.forEach(s => s.abschnitte.find(aktHalbjahr).noten.forEach(n => noten[n.fach.FachKrz].push(n.NotenKrz || "–")))
+        return noten
+      }
+    },
+    helpers: { _ },
+    components: {
+      Voffset: './partials/Voffset.html'
+    }
+  }
+</script>
+
+<style>
+  @import 'css/main.css';
+  @import 'css/a4-landscape.css';
+  .table-bordered {
+    border-collapse: collapse; }
+  .table-bordered tr td {
+    border: thin solid #ddd;
+    line-height: 1.3rem }
+</style>
+

+ 3 - 0
css/a3-landscape.css

@@ -0,0 +1,3 @@
+@page {
+  size: A3 landscape;
+}

+ 3 - 0
css/a3-portrait.css

@@ -0,0 +1,3 @@
+@page {
+  size: A3 portrait;
+}

+ 3 - 0
css/a4-landscape.css

@@ -0,0 +1,3 @@
+@page {
+  size: A4 landscape;
+}

+ 3 - 0
css/a4-portrait.css

@@ -0,0 +1,3 @@
+@page {
+  size: A4 portrait;
+}

+ 296 - 0
css/main.css

@@ -0,0 +1,296 @@
+@charset "UTF-8";
+@import url(normalize.css);
+@font-face {
+  font-family: 'TeX Gyre Heros';
+  src: url("../daten/texgyreheros-regular-webfont.woff") format("woff");
+  font-style: normal;
+}
+@font-face {
+  font-family: 'TeX Gyre Heros';
+  src: url("../daten/texgyreheros-bold-webfont.woff") format("woff");
+  font-style: normal;
+  font-weight: bold;
+}
+@font-face {
+  font-family: 'TeX Gyre Heros';
+  src: url("../daten/texgyreheros-italic-webfont.woff") format("woff");
+  font-style: italic;
+}
+@font-face {
+  font-family: 'TeX Gyre Heros';
+  src: url("../daten/texgyreheros-bolditalic-webfont.woff") format("woff");
+  font-style: italic;
+  font-weight: bold;
+}
+
+@media screen {
+  .page {
+    border: 1px solid black;
+    box-shadow: 5px 5px 4px 0 silver;
+  }
+}
+
+@media print {
+  * {
+    -webkit-print-color-adjust: exact;
+  }
+
+  .page {
+    page-break-after: always;
+    page-break-inside: avoid;
+    margin: 0;
+  }
+}
+
+@page {
+  margin: 0;
+  size: auto;
+}
+
+html {
+  font-size: 10pt;
+}
+
+.page {
+  line-height: 1.5;
+  font-weight: normal;
+  font-family: "Tex Gyre Heros";
+  color: #333;
+  position: relative;
+  padding: 10mm 25mm 10mm 25mm;
+  display: block;
+}
+
+/*
+Da *named pages*, also z.B. @page a3landscape noch nicht von Chrome
+unterstützt werden, gilt vorerst die Standard-Einstellung von A4 Portrait.
+Um andere Formate zu erzeugen, muss die Größe in den svelte-Koponenten
+als setup-Einstellung hinterlegt werden. Oder man verwendet ein besonderes CSS
+mit der anderen Einstellung.
+Dazu bitte ein @import 'nicht_a4_portrait.css' verwenden. Svelte ignoriert sonst
+die in den <style> tags hinterlegten @... Anweisungen.
+Link: https://www.w3.org/TR/css3-page/#using-named-pages
+*/
+
+.page[size="A4"][orientation="portrait"] {
+  /* page: a4portrait; */
+  width: 210mm;
+  height: 296.8mm;
+}
+/* @page a4portrait {
+  margin: 0;
+  size: A4 portrait;
+} */
+
+.page[size="A4"][orientation="landscape"] {
+  /* page: a4landscape; */
+  width: 296.8mm;
+  height: 209mm;
+}
+/* @page a4landscape {
+  margin: 0;
+  size: A4 landscape;
+} */
+
+.page[size="A3"][orientation="portrait"] {
+  /* page: a3portrait; */
+  width: 296.8mm;
+  height: 420mm;
+}
+/* @page a3portrait {
+  margin: 0;
+  size: A3 portrait;
+} */
+
+.page[size="A3"][orientation="landscape"] {
+  /* page: a3landscape; */
+  width: 420mm;
+  height: 296.8mm;
+}
+/* @page a3landscape {
+  margin: 0;
+  size: A3 landscape;
+} */
+
+.page[size="A5"][orientation="portrait"] {
+  /* page: a5portrait; */
+  width: 148mm;
+  height: 210mm;
+}
+/* @page a5portrait {
+  margin: 0;
+  size: A5 portrait;
+} */
+
+.page[size="A5"][orientation="landscape"] {
+  /* page: a5landscape; */
+  width: 210mm;
+  height: 148mm;
+}
+/* @page a5landscape {
+  margin: 0;
+  size: A5 landscape;
+} */
+
+/* Typography */
+h1, h2, h3, h4, h5, h6 {
+  font-weight: 300;
+  letter-spacing: -.1rem;
+  margin-bottom: 2.0rem;
+  margin-top: 0;
+}
+h1 {
+  font-size: 4.6rem;
+  line-height: 1.2;
+}
+h2 {
+  font-size: 3.6rem;
+  line-height: 1.25;
+}
+h3 {
+  font-size: 2.8rem;
+  line-height: 1.3;
+}
+h4 {
+  font-size: 2.2rem;
+  letter-spacing: -.08rem;
+  line-height: 1.35;
+}
+h5 {
+  font-size: 1.8rem;
+  letter-spacing: -.05rem;
+  line-height: 1.5;
+}
+h6 {
+  font-size: 1.6rem;
+  letter-spacing: 0;
+  line-height: 1.4;
+}
+p {
+  margin-top: 0;
+}
+
+.grid {
+  display: grid;
+  grid-gap: 0;
+  gap: 0;
+  /* align-content: start; */
+  grid-template-columns: auto;
+  grid-template-rows: auto 1fr auto;
+  grid-template-areas: "header" "main" "footer";
+}
+
+.header {
+  grid-area: header;
+  /* align-self: start; */
+}
+
+.main {
+  grid-area: main;
+  /* align-self: start; */
+}
+
+.footer {
+  grid-area: footer;
+  align-self: end;
+}
+
+.main-grid {
+  grid-area: main;
+  display: grid;
+  grid-column-gap: 25mm;
+  grid-template-columns: 1fr 1fr;
+  grid-template-rows: auto 1fr auto;
+  grid-template-areas: "header-left header-right" "main-left main-right" "footer-left footer-right";
+}
+
+.main-left {
+  grid-area: main-left;
+}
+
+.main-right {
+  grid-area: main-right;
+}
+
+.footer-grid {
+  grid-area: footer;
+  display: grid;
+  grid-column-gap: 25mm;
+  grid-template-columns: 1fr 1fr;
+  grid-template-rows: auto;
+  grid-template-areas: "footer-left footer-right";
+}
+
+.footer-left {
+  grid-area: footer-left;
+}
+
+.footer-right {
+  grid-area: footer-right;
+}
+
+.flex-grid {
+  display: flex;
+}
+
+.col {
+  flex: 1;
+}
+
+.col-2 {
+  flex: 2;
+}
+
+.text-center {
+  text-align: center !important;
+}
+
+hr {
+  border: 0;
+  border-top: 1px solid #eee;
+  border-color: #000;
+  height: 1px;
+  margin: 3px 0;
+}
+.hr-grau {
+  border-color: #646464 !important;
+}
+
+.fett {
+  font-weight: bolder;
+}
+
+.klein {
+  font-size: 0.7rem;
+}
+
+.eng {
+ line-height: 0.9rem;
+}
+
+.grau * {
+  color: #646464 !important;
+}
+
+ul.dashes {
+  margin: 0;
+  list-style-type: none;
+  padding-left: 1rem;
+}
+ul.dashes li:before {
+  content: "-";
+  position: absolute;
+  margin-left: -1rem;
+}
+
+.schulnummer {
+  text-align: left;
+}
+
+.wasserzeichen {
+  position: absolute;
+  opacity: 0.5;
+  z-index: 99;
+  color: red;
+  font-size: 96pt;
+}

+ 2 - 0
css/normalize.css

@@ -0,0 +1,2 @@
+/*! modern-normalize | MIT License | https://github.com/sindresorhus/modern-normalize */
+html{box-sizing:border-box}*,::after,::before{box-sizing:inherit}:root{-moz-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol'}hr{height:0}abbr[title]{text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:SFMono-Regular,Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{padding:0}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}

+ 84 - 0
daten/logo-rechts.svg

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   clip-path="url(#a)"
+   height="90.267"
+   preserveAspectRatio="xMidYMid"
+   version="1.2"
+   viewBox="0 0 16003.395 2547.525"
+   width="567.049"
+   fill-rule="evenodd"
+   stroke-width="28.222"
+   stroke-linejoin="round"
+   id="svg4828"
+   sodipodi:docname="logo-rechts.svg"
+   inkscape:version="0.92.2 2405546, 2018-03-11">
+  <metadata
+     id="metadata4832">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1136"
+     id="namedview4830"
+     showgrid="false"
+     inkscape:zoom="1.1850827"
+     inkscape:cx="300.65274"
+     inkscape:cy="39.848395"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4826" />
+  <defs
+     class="ClipPathGroup"
+     id="defs4800">
+    <clipPath
+       id="a">
+      <path
+         d="M0 0h21000v29700H0z"
+         id="path4797" />
+    </clipPath>
+  </defs>
+  <g
+     class="SlideGroup"
+     id="g4826">
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:517.48406982px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000080;fill-opacity:1;stroke:none;stroke-width:45.99858093px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
+       x="8839.0332"
+       y="763.78784"
+       id="text4836"><tspan
+         sodipodi:role="line"
+         id="tspan4834"
+         x="8839.0332"
+         y="763.78784"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:981.30303955px;font-family:Sans;-inkscape-font-specification:'Sans, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:45.99858093px;fill:#000080;">Berufskolleg</tspan><tspan
+         sodipodi:role="line"
+         x="8839.0332"
+         y="1531.1272"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:981.30303955px;font-family:Sans;-inkscape-font-specification:'Sans, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:45.99858093px;fill:#000080;"
+         id="tspan4840">Entenhausen</tspan></text>
+  </g>
+</svg>

File diff suppressed because it is too large
+ 0 - 0
daten/logo_seite_dummy.svg


BIN
daten/texgyreheros-bold-webfont.woff


BIN
daten/texgyreheros-bolditalic-webfont.woff


BIN
daten/texgyreheros-italic-webfont.woff


BIN
daten/texgyreheros-regular-webfont.woff


+ 106 - 0
daten/top_dummy.svg

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   clip-path="url(#a)"
+   height="96.147"
+   preserveAspectRatio="xMidYMid"
+   version="1.2"
+   viewBox="0 0 16011.91 2713.4849"
+   width="567.351"
+   fill-rule="evenodd"
+   stroke-width="28.222"
+   stroke-linejoin="round"
+   id="svg25"
+   sodipodi:docname="top_dummy.svg"
+   inkscape:version="0.92.2 2405546, 2018-03-11">
+  <metadata
+     id="metadata29">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="640"
+     inkscape:window-height="480"
+     id="namedview27"
+     showgrid="false"
+     inkscape:zoom="0.6592039"
+     inkscape:cx="283.67551"
+     inkscape:cy="48.073502"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="g23" />
+  <defs
+     class="ClipPathGroup"
+     id="defs5">
+    <clipPath
+       id="a">
+      <path
+         d="M0 0h21000v29700H0z"
+         id="path2" />
+    </clipPath>
+  </defs>
+  <g
+     class="SlideGroup"
+     id="g23">
+    <path
+       d="M11318.338 15835.045l490.715 1212.163-1205.6-506.626-1001.196 841.276 109.28-1303.149-1109.489-692.226 1273.138-298.764 315.495-1269.096 677.563 1118.503 1304.475-92.118z"
+       transform="matrix(.43784 0 0 .46079 812.034 -5799.302)"
+       fill="none"
+       stroke="#cb0000"
+       stroke-width="100"
+       id="path11" />
+    <path
+       d="M9091.535 1235.467l-579.164 87.046-186.997 555.014-261.757-523.919-585.635-6.336 417.39-410.846-174.947-558.93 519.72 270.003 477.512-339.102-96.187 577.717z"
+       fill="none"
+       stroke="#00c3cb"
+       stroke-width="100"
+       id="path13" />
+    <path
+       d="M11686.662 1854.174l-673.757-78.734-377.677 563.478-133.322-665.111-652.608-185.068 591.36-332.328L10415 478.555l498.802 459.722 636.751-233.87-283.083 616.45z"
+       fill="none"
+       stroke="#cb0000"
+       stroke-width="100"
+       stroke-opacity=".622"
+       id="path15" />
+    <path
+       d="M13594.336 1269.84l-584.386-295.388-529.624 385.045 100.346-647.064-529.862-384.717 646.403-104.52 202.151-622.812 299.153 582.467 654.8-.202-461.517 464.503z"
+       fill="none"
+       stroke="#33008f"
+       stroke-width="100"
+       id="path17" />
+    <path
+       d="M15347.337 2215.085l-588.062-242.578-484.318 412.427 48.985-634.241-541.904-333.167 618.335-149.404 149.404-618.336 333.167 541.904 634.241-48.985-412.427 484.319z"
+       fill="none"
+       stroke="#cb6b00"
+       stroke-width="100"
+       id="path19" />
+    <path
+       d="M13164.68 1768.241l-89.37 216.654 151.945 178.434-233.667-18.048-122.746 199.649-55.043-227.808-227.808-55.043 199.648-122.746-18.047-233.667 178.434 151.946z"
+       fill="none"
+       stroke="#6cc200"
+       stroke-width="100"
+       id="path21" />
+  </g>
+</svg>

+ 94 - 0
demo.html

@@ -0,0 +1,94 @@
+<div class="page grid" orientation="portrait" size="A4">
+  <div class="main">
+    <h1>schild.report Demo-Report</h1>
+    Dieser Report verwendet ein Standard-CSS, das für die Druckausgabe optimiert
+    ist. Es kann als Grundlage für alle Reports verwendet werden.
+    Um eine Seite zu erstellen, reicht diese Vorlage:
+    <pre>{html(`
+<div class="page grid" orientation="portrait" size="A4">
+  <div class="header">
+    <h2><center>Inhalt für den Kopfteil</center></h2>
+    <hr>
+  </div>
+  <div class="main">
+    <h3>Der Hauptteil der Seite</h3>
+  </div>
+  <div class="footer">
+    <hr>
+    <h2><center>Inhalt für den Fuß</center></h2>
+  </div>
+</div>
+    `)}</pre>
+  </div>
+</div>
+<div class="page grid" orientation="portrait" size="A4">
+  <div class="header">
+    <h2><center>Inhalt für den Kopfteil</center></h2>
+    <hr>
+  </div>
+  <div class="main">
+    <h3>Der Hauptteil der Seite</h3>
+  </div>
+  <div class="footer">
+    <hr>
+    <h2><center>Inhalt für den Fuß</center></h2>
+  </div>
+</div>
+<div class="page grid" orientation="portrait" size="A4">
+  <div class="header"><h2><center>Nächste Seite</center></h2></div>
+  <div class="main">
+    <h3>Im Folgenden gibt es weitere Beispiele zur Anwendung der Reports</h3>
+    Wenn man Schülerdaten in einer Schleife ausgeben möchte, greift man auf Svelte
+    zurück, das diese Funktion komfortabel zur Verfügung stellt. Auf den nächsten Seiten
+    werden verschiedene Möglichkeiten demonstriert. Um herauszufinden, wie der Inhalt
+    erzeugt wird, bitte im Vorlagenverzeichnis das dokumentierte <code>demo.html</code> Dokument
+    öffnen und zum Testen gerne auch ändern. Die Änderungen, sofern keine Fehler
+    eingebaut werden, werden automatisch nach dem Speichern angezeigt.
+  </div>
+  <div class="footer"><h2><center>Inhalt für den Fuß</center></h2></div>
+</div>
+<div class="page grid" orientation="portrait" size="A4">
+  <!-- Pageheader ist eine Svelte-Komponente, die unter ./partials abgelegt ist und im
+  script-Bereich dieses Dokuments registriert wurde. -->
+  <Pageheader logo="daten/top_dummy.svg"/>
+  <div class="main">
+    In der Klasse sind <b>{schueler.length}</b> Schülerinnen und Schüler.
+    <br>D.h. {schueler.filter(s => s.Geschlecht === 3).length} Schüler und
+    {schueler.filter(s => s.Geschlecht === 4).length} Schülerinnen
+    <Voffset v="2"/>
+    Hier nun die ersten 15:<br>
+    <!-- Damit die Seite nicht mehr darstellt als auf ein A4-Papier passt, werden nur
+    die ersten 15 Schüler (oder weniger) angezeigt. Mit {#each schueler as s} würden
+    alle Schüler auf der Seite dargestellt werden.
+    s ist der jeweilige Schüler und i die Position in der Liste: -->
+    {#each schueler.slice(0,15) as s, i}
+      {#each s.abschnitte.filter(aktHalbjahr) as hj}
+        <b>{i+1}</b> {s.Name} ist {s.schueler_in} in der Klasse {s.Klasse} <br>
+        {s.Geschlecht === 3 ? 'Er':'Sie'} befindet sich im Schuljahr {hj.Jahr} im {hj.Abschnitt} Halbjahr.<br>
+      {/each}
+    {/each}
+  </div>
+</div>
+
+
+<script>
+	export default {
+    data () { return {
+      page: ``
+    }},
+    helpers: {
+      html: text => text
+    },
+    components: {
+      Pageheader: './partials/Pageheader.html',
+      Voffset: './partials/Voffset.html'
+    },
+    computed: {
+      aktHalbjahr: ({jahr, abschnitt}) => { return a => a.Jahr === jahr && a.Abschnitt === abschnitt }
+    }
+  }
+</script>
+
+<style>
+  @import 'css/main.css';
+</style>

+ 37 - 0
helfer.js

@@ -0,0 +1,37 @@
+export const datum = (t) => {
+  // gibt ein Datum im deutschen Format zurück
+  try {
+    return Date.parse(t).toLocaleDateString('de', {day: '2-digit', month: '2-digit', year: 'numeric'})
+  } catch (e) {console.log(e); return}
+}
+export const versetzungsvermerk = (s, hj, agz=null) => {
+  // gibt, wenn vorhanden den passenden Vermerk zurück
+  // wenn es ein agz ist und kein Konferenzdatum feststeht
+  if (agz && !hj.Konferenzdatum) return
+  let vermerk = ''
+  if (hj.Abschnitt === 2 || s.Klasse.startsWith('H')) {
+    switch (hj.VersetzungKrz) {
+      case 'N': vermerk = `Nicht versetzt laut Konferenzbeschluss vom ${datum(hj.Konferenzdatum)}`
+      case 'V': vermerk = `Versetzt laut Konferenzbeschluss vom ${datum(hj.Konferenzdatum)}`
+      default:  vermerk = 'Kein Versetzungsvermerk hinterlegt'
+    }
+  }
+  return vermerk
+}
+export const bemerkungen = (hj) => {
+  return hj.ZeugnisBem ? hj.ZeugnisBem.replace('\r\n', '<br/>') : 'keine'
+}
+export const volljaehrigBei = (s, datum) => {
+  // gibt an, ob der Schüler *s* zu einem Zeitpunkt *datum* volljährig war
+  try {
+  const g = new Date(s.Geburtsdatum)
+  const d = new Date(datum)
+  const volljaehrig = d.getFullYear() - g.getFullYear() - ((d.getMonth() > g.getMonth() || (d.getMonth() == g.getMonth() && d.getDay() >= g.getDay()) ? 0 : 1)) >= 18
+  return volljaehrig
+  } catch (e) {console.log(e); return}
+}
+export const schulform = (s) => {
+  switch (s.ASDSchulform[0]) {
+    case 'B': return 'Berufsfachschule'
+  }
+}

+ 44 - 0
partials/Fussnoten.html

@@ -0,0 +1,44 @@
+<!-- Allgemeine Fussnoten
+
+Folgende Attribute werden unterstützt:
+schule:     Hier stehen die SchulNr etc drin. Muss mitgegeben werden
+ger:        Wenn der GER für AGZ oder ASZ verwendet werden soll, mitangeben.
+            true|false Standardwert ist false -->
+
+<div class="klein eng">
+  <Voffset v="2"/>
+  <div class="schulnummer">
+    Schulnummer: {schule.SchulNr}
+  </div>
+  <hr class="hr-grau" />
+  <div class="grau">
+    Notenstufen gemäß § 48 SchulG: 1 = sehr gut, 2 = gut, 3 = befriedigend,
+    4 = ausreichend, 5 = mangelhaft, 6 = ungenügend
+    <Voffset v=".5"/>
+    {#if ger}
+      <sup>1</sup>Der Unterricht in den modernen Fremdsprachen hat auf der nach dem Fach in
+      Klammern angegebenen Niveaustufe des „Europäischen Referenzrahmens für
+      Sprachen: Lernen, Lehren, Beurteilen“ stattgefunden. Sind zwei
+      Referenzniveaus ausgewiesen, ist das niedrigere in vollem Umfang, das höhere
+      in Anteilen erreicht. Bei mindestens ausreichenden Leistungen wird der
+      sprachliche Kompetenzerwerb auf diesem Niveau bescheinigt.
+      <Voffset v=".5"/>
+    {/if}
+    Dem Zeugnis liegen zugrunde: Die Verordnung über die Ausbildung und Prüfung
+    in den Bildungsgängen des Berufskollegs (Ausbildungs- und Prüfungsordnung
+    Berufskolleg, APO-BK) vom 26. Mai 1999 (SGV. NRW. 223/BASS 13 – 33 Nr. 1.1).
+    <Voffset v=".5"/>
+    Rechtsbehelfsbelehrung: Gegen dieses Zeugnis kann innerhalb eines Monats
+    nach Bekanntgabe des Zeugnisses Widerspruch eingelegt werden. Der Widerspruch
+    ist beim {schule.Bezeichnung2}, {schule.Strasse}, {schule.PLZ} {schule.Ort},
+    schriftlich oder zur Niederschrift zu erheben. <br />Falls die Frist durch
+    das Verschulden einer/eines Bevollmächtigten versäumt wird, wird dies
+    Verschulden der Widerspruchsführerin/dem Widerspruchsführer zugerechnet.
+  </div>
+</div>
+
+<script>
+  export default {
+    components: { Voffset: './Voffset.html' }
+  }
+</script>

+ 138 - 0
partials/Noten.html

@@ -0,0 +1,138 @@
+<!--
+Allgemeine Notentabelle für Berufskollegs, die z.B. die etwas langtextigen
+Lernfeldfächer unterstützt oder verschiedene Abschlussnoten.
+
+Folgende Attribute werden unterstützt:
+lernfeld: Sorgt dafür, dass die Klasse `fach-bezeichnung` nicht gesetzt wird und
+          Fächer dementsprechend nicht eingerückt werden (nur Lernfeldfächer)
+          true|false Standardwert false
+noten:    Hier können Abschnittsnoten oder Abschlussnoten gesetzt werden, z.B.
+          hj.noten oder s.bk_abschluss_faecher oder s.fhr_abschluss_faecher
+          Muss gesetzt werden
+fachGliederungen: sollten mitgegeben werden, wenn die für verschiedene Bildungsgänge
+          geänderten Werte berücksichtigt werden sollen, z.B. andere Bezeichnungen
+          in den verschiedenen Bildungsgängen
+ger:      Wenn ein Referenzniveau für die Fremdsprachen angegeben werden soll
+          true|false, Standardwert false,
+fachklasse: muss mitgegeben werden, wenn aus der Fachklassen.json Datei der Standard-
+          Wert gelesen werden soll. Ansonsten über Lernentw festlegen, was eigetragen wird
+-->
+
+<table class="table-noten">
+  {#each faechergruppen as [gruppen_id, gruppenFaecher]}
+    <tr>
+      <td class="fachgruppe-margin">
+        <b>{gruppenbezeichnung(gruppen_id)}</b>
+      </td>
+    </tr>
+    {#each lernbereicheUndFaecher(gruppenFaecher) as [lernbereich, faecher]}
+      {#if lernbereich != faecher[0].fach.Zeugnisbez}
+        <tr><td colspan="2" class="{lernfeld ? '' : 'fach-bezeichnung'}">{lernbereich}</td></tr>
+      {/if}
+      {#each faecher as f (f.ID)}
+        {#if f.AufZeugnis === '+'}
+          <tr>
+            <td class="{lernfeld ? '' : 'fach-bezeichnung'} {lernbereich != faecher[0].fach.Zeugnisbez ? 'lernfeld-lernbereich' : ''}">
+              {#if f.fach.Zeugnisbez.includes("Projekt")}
+                {f.Lernentw}
+              {:else}
+                <!-- Die Zeugnisbezeichnung hat bei gesetzten fachGliederungen einen anderen Namen... -->
+                {f.fach.ZeugnisBez || f.fach.Zeugnisbez}
+                <!-- Ist ger gesetzt, unter Lernentw nach Eintrag schauen. Wenn dort nichts steht,
+                in der Fachklassen.json nach Einträgen für Fremdsprache und Fach_ID suchen
+                Mit der Fussnote 1 muss natürlich auch tatsächlich eine Fussnote zum GER existieren … -->
+                {#if ger && f.fach.IstSprache === '+'}
+                  {fremdsprache(f, fachklasse)}<sup>1</sup>
+                {/if}
+              {/if}
+            </td>
+            <!-- Entweder Abschlussnote, wenn gesetzt oder Abschnittsnote oder nix -->
+            <td class="td-fach-note"><span>{f.NoteAbschluss || f.NotenKrz || '–––––'}</span></td>
+          </tr>
+        {/if}
+      {/each}
+    {/each}
+    <tr>
+    </tr>
+  {/each}
+</table>
+
+<script>
+  import { gruppenIds } from './gruppenIDs'
+  const _ = R.lodash
+
+  export default {
+    helpers: {
+      _,
+      gruppenbezeichnung: gruppe => gruppenIds[gruppe],
+      lernbereicheUndFaecher: gruppenFaecher => Object.entries(_.groupBy(gruppenFaecher, f => f.fach.Bezeichnung)),
+      fremdsprache: (fach, fachklasse) => {
+        try {
+          return fach.Lernentw
+        } catch (e) { return 'undefined - Sprachniveau fehlt'}
+      }
+    },
+    computed: {
+      faechergruppen: ({ noten, fachGliederungen }) => {
+        // noten werden im jeweiligen Dokument über das *noten*-Attribut mitgegeben.
+        // z.B. die Abschnittsnote oder die Noten aus *s.bk_abschluss_faecher* und co.
+        // Nutzer über Konsole auf Änderungen in den Fächern hinweisen
+        if (fachGliederungen && fachGliederungen.length > 0) console.log('Es wurden Fachgliederungswerte gesetzt: ', fachGliederungen)
+        // suche in den fachGliederungen, ob für die jeweilige Fachklasse was besonderes festgelegt wurde
+        // vorausgesetzt, fachGliederungen wurde mitgegeben
+        const faecher = fachGliederungen
+          ? noten.map(f => {return {...f, fach: Object.assign(f.fach, fachGliederungen.find(fg => fg.Fach_ID === f.Fach_ID))}})
+          : noten
+        // erstmal alle Fächer sortieren in der passenden Reihenfolge,
+        // dann nach Fachgruppe gruppieren
+        return Object.entries(_.groupBy(_.sortBy(faecher, f => f.fach.SortierungS2), f => f.fach.Fachgruppe_ID))
+      },
+      // Berufsbezogen kommt neuerdings zuerst, dann berufsübergreifend
+      // wenn aber nur eine Note vermerkt ist, dann normal zurück
+      faechergruppenUmsortiert: ({ faechergruppen }) => faechergruppen.length > 1
+        ? [faechergruppen[0], faechergruppen[1]] = [faechergruppen[1], faechergruppen[0]]
+        : faechergruppen
+    }
+  }
+</script>
+<style>
+.table-noten {
+  width: 100%;
+  border: none;
+  border-collapse: collapse;
+  margin-bottom: 1rem;
+}
+.fach-bezeichnung {
+  padding-left: 10mm;
+}
+.td-fach-note {
+  padding-bottom: .2rem;
+  padding-top: .2rem;
+  width: 12rem;
+}
+.td-fach-note span {
+  background-color: #dcdcdc !important;
+  text-align: center;
+  width: 10rem;
+  display: block;
+  margin: 0 auto;
+}
+/*.td-fach-note span.e-note {
+  background-color: white !important;
+  width: 17rem;
+  margin: 0;
+}*/
+
+.fachgruppe-margin {
+  padding-top: 0.3rem;
+}
+
+.lernfeld-lernbereich {
+  padding-left: 2rem;
+  padding-bottom: 0;
+}
+
+.lernbereich {
+  padding-left: 20mm;
+}
+</style>

+ 15 - 0
partials/Pageheader.html

@@ -0,0 +1,15 @@
+<div class="header">
+  {#if logo}
+    <img src="{logo}" style="width: -webkit-fill-available" alt="zeugnis_logo"/>
+  {:else}
+    <h1><center>Zeugnis</center></h1>
+  {/if}
+  <hr class="hr-rot"/>
+</div>
+
+<style>
+  .hr-rot {
+    border-color: #ff2700;
+    margin: 8px 0;
+  }
+</style>

+ 15 - 0
partials/Seitenlogo.html

@@ -0,0 +1,15 @@
+<div class="seitenlogo">
+  {#if logo}
+    <img src="{logo}" height="160px" alt="traeger_logo"/>
+  {:else}
+    <center>Logo</center>
+  {/if}
+</div>
+
+<style>
+  .seitenlogo {
+    position: absolute;
+    bottom: 10mm;
+    left: 10mm;
+  }
+</style>

+ 1 - 0
partials/Voffset.html

@@ -0,0 +1 @@
+<div style="margin-top: {v}em"></div>

+ 32 - 0
partials/gruppenIDs.js

@@ -0,0 +1,32 @@
+export const gruppenIds = {
+    10:'Berufsübergreifender Lernbereich',
+    11:'Aufgabenbereich I',
+    20:'Berufsbezogener Lernbereich',
+    21:'Aufgabenbereich II',
+    25:'Berufsbezogener Bereich (Schwerpunkt)',
+    30:'Differenzierungsbereich',
+    31:'Aufgabenbereich III.1',
+    32:'Aufgabenbereich III.2',
+    33:'Aufgabenbereich III.3',
+    40:'Berufspraktikum',
+    60:'besondere Lernleistung',
+    100:'Fremdsprachen',
+    110:'Deutsch',
+    200:'Naturwissenschaften',
+    300:'Gesellschaftslehre',
+    400:'Arbeitslehre',
+    500:'Kunst und Musik',
+    600:'Sport',
+    700:'Mathematik',
+    800:'Wahlpflichtbereich',
+    900:'Religion',
+    1000:'Zusätzliche Unterrichtsveranstaltungen',
+    1100:'Angleichungskurse',
+    1200:'Sprache',
+    1300:'Sachunterricht',
+    1400:'Förderunterricht',
+    1500:'Vertiefungskurs',
+    1600:'Projektkurs',
+    1700:'Abschlussarbeit',
+    1800:'Projektarbeit'
+}

Some files were not shown because too many files changed in this diff