[Mpuls-commits] r262 - in wasko/trunk: . formed waskaweb/controllers waskaweb/lib waskaweb/model waskaweb/templates

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Mon Feb 16 18:15:02 CET 2009


Author: torsten
Date: 2009-02-16 18:15:01 +0100 (Mon, 16 Feb 2009)
New Revision: 262

Added:
   wasko/trunk/waskaweb/lib/filecache.py
   wasko/trunk/waskaweb/lib/renderer.py
   wasko/trunk/waskaweb/model/annotations.py
Removed:
   wasko/trunk/waskaweb/lib/formular_help.py
   wasko/trunk/waskaweb/lib/renderer.py
Modified:
   wasko/trunk/ChangeLog.txt
   wasko/trunk/formed/annotations.xhtml
   wasko/trunk/waskaweb/controllers/CaseBase.py
   wasko/trunk/waskaweb/controllers/case.py
   wasko/trunk/waskaweb/lib/app_globals.py
   wasko/trunk/waskaweb/lib/helpers.py
   wasko/trunk/waskaweb/model/datapage.py
   wasko/trunk/waskaweb/model/semantic.py
   wasko/trunk/waskaweb/templates/main.mako
Log:
Ported rendering and help handlich from offline client


Modified: wasko/trunk/ChangeLog.txt
===================================================================
--- wasko/trunk/ChangeLog.txt	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/ChangeLog.txt	2009-02-16 17:15:01 UTC (rev 262)
@@ -1,3 +1,29 @@
+2009-02-16	Torsten Irlaender  <torsten.irlaender at intevation.de>
+
+	Implemented renderer and help-handling from the offline client
+
+	* waskaweb/model/annotations.py, 
+	  waskaweb/model/semantic.py,
+	  waskaweb/lib/app_globals.py,
+	  formed/annotations.xhtml: New. Cominded Handling of help,
+	  and annotations. Ported from offline client
+
+	* waskaweb/model/datapage.py,
+	  waskaweb/controllers/CaseBase.py,
+	  waskaweb/controllers/case.py,
+	  waskaweb/lib/helpers.py,
+	  waskaweb/lib/renderer.py,
+	  waskaweb/lib/filecache.py,
+	  waskaweb/templates/main.mako: Ported pagerendering from
+	  offlineclient (radio-matrix-support) to the webclient from the
+	  offline client.
+
+	 * waskaweb/lib/formular_help.py: Deleted
+
+2009-02-13	Torsten Irlaender  <torsten.irlaender at intevation.de>
+
+	* waskaweb/templates/main.mako: Changed pagetitle to WASKO
+
 2009-02-11	Sascha L. Teichmann	<teichmann at intevation.de>
 
 	* waskaweb/model/navigation.py: removed needless prints

Modified: wasko/trunk/formed/annotations.xhtml
===================================================================
--- wasko/trunk/formed/annotations.xhtml	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/formed/annotations.xhtml	2009-02-16 17:15:01 UTC (rev 262)
@@ -1,117 +1,172 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-    <head>
-        <title>Anmerkungen</title>
-    </head>
-    <body>
-        <div
-	id="beratung_start:required:erstgespraech">
-		<h2>Pflichtfelder für den Beginn der Beratungstätigkeit</h2>
-		Um die Vollständigkeit der ESF-Stammdaten, sowie
-		definierte Start und Endpunkte der Beratungsphase
-		zu gewährleisten, muss dieses Feld ausgefüllt werden.
-	</div>
-        <div
-	id="beratung_ende:required:geburtsdatum,arbeitslosigkeit_1,geschlecht,dauer_nach_letzter_beschaeftigung,bezug_alg2,bezug_alg1,erwerbstaetigkeit_job,muttersprache,vater,mutter,staatsangehoerigkeit,schulabschluss_1,abschluss_ausserbetriebliche_ausbildung,abschluss_betriebliche_ausbildung,beieintrittistderjugendlichemehrfachantwortenmoeglich,datum_feststellung,datum_ende_beratung">
-		<h2>Pflichtfelder für den Abschluss der Beratungstätigkeit</h2>
-		Um die Vollständigkeit der ESF-Stammdaten, sowie
-		definierte Start und Endpunkte der Beratungsphase
-		zu gewährleisten, muss dieses Feld ausgefüllt werden.
-        </div>
-        <div
-	id="cm_start:required:datum_feststellung">
-		<h2>Pflichtfelder für den Beginn des Case-Managemet</h2>
-		Um die Vollständigkeit der ESF-Stammdaten, sowie
-		definierte Start und Endpunkte des Case-Management
-		zu gewährleisten, muss dieses Feld ausgefüllt werden.
-        </div>
-        <div
-	id="cm_ende:required:beendigung_5,art_beendigung">
-		<h2>Pflichtfelder für den Abschluss des Case-Management</h2>
-		Um die Vollständigkeit der ESF-Stammdaten, sowie
-		definierte Start und Endpunkte des Case-Management
-		zu gewährleisten, muss dieses Feld ausgefüllt werden.
-        </div>
-        <div
-	id="nachbetreuung_start:required:beendigung_5">
-		<h2>Pflichtfelder für den Beginn der Nachbetreuung</h2>
-		Um die Vollständigkeit der ESF-Stammdaten, sowie
-		definierte Start und Endpunkte der Nachbetreuung
-		zu gewährleisten, muss dieses Feld ausgefüllt werden.
-        </div>
-        <div
-	id="nachbetreuung_ende:required:beendigung_nachbetreuung">
-		<h2>Pflichtfelder für den Abschluss der Nachbetruung</h2>
-		Um die Vollständigkeit der ESF-Stammdaten, sowie
-		definierte Start und Endpunkte der Nachbetreuung
-		zu gewährleisten, muss dieses Feld ausgefüllt werden.
-        </div>
-	<!-- Auswertungen -->
-        <div
-	id="hilfe_auswertung:evaluation:kompetenzfeststellung">
-		<h2>Verhältnis Beratungskunden/Aufnahmen in das Case-Management (CM)</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-        <div
-	id="hilfe_auswertung:evaluation:beieintrittistderjugendlichemehrfachantwortenmoeglich,rechtlicher_kontext,schnittstelle_zu">
-		<h2>Zugang zur Kompetenzagentur</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-        <div
-	id="hilfe_auswertung:evaluation:schulabschluss_1">
-		<h2>Höchster erreichte Schulabschluss</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-        <div
-	id="hilfe_auswertung:evaluation:zeitraum_fallmanagement,art_beendigung">
-		<h2>Verweildauer im CM</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-        <div
-	id="hilfe_auswertung:evaluation:geschlecht,geburtsdatum">
-		<h2>Alters- und Geschlechterstruktur in %</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-        <div
-	id="hilfe_auswertung:evaluation:staatsangehoerigkeit,muttersprache,vater,mutter,">
-		<h2>Migrationshintergrund</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-        <div
-	id="hilfe_auswertung:evaluation:foerderbedarf">
-		<h2>Förderbedarf</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-        <div
-	id="hilfe_auswertung:evaluation:vermittlung,veraenderungen_rechtskreis,">
-		<h2>Vermittlung</h2>
-		Dieses Feld wird in der oben genannten Auswertung ausgelesen.
-		Um möglichst aussagekräftige Ergebnisse in der internen
-		Auswertung zu erhalten, empfehlen wird dieses Feld
-		auszufüllen.
-	</div>
-    </body>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Help text</title>
+  </head>
+  <body>
+    <div id="0:help:fn">
+      <p>Jeder Fall sollte eine eindeutig identifizierbare Fallnummer haben. Diese wird intern von den Koordinierungsstellen nach einem einheitlichen Muster vergeben. Wir empfehlen folgende Durchnummerierung: F0001, F0002 und so weiter.</p>
+    </div>
+    <div id="1:help:kinder_allein">
+      <p>Ist der/die Jugendliche alleinerziehend?</p>
+    </div>
+    <div id="2:help:mutter_land">
+      <p>Dieses Item dient als Indikator für einen Migrationshintergrund. Mit dieser Angabe soll zudem der These nachgegangen werden, inwieweit der Bildungserfolg von der familiären und sozialen Herkunft determiniert wird.</p>
+    </div>
+    <div id="3:help:datum_erklaerung">
+      <p>Geben Sie das Datum an, an welchem die Erklärung vom Jugendlichen/Personensorgeberechtigten unterzeichnet wurde. Diese Einwilligungserklärung ist die Grundlage zur Erfassung und Speicherung von personenbezogenen Daten und Voraussetzung für die Aufnahme in das Case Management (Phase B).</p>
+    </div>
+    <div id="4:help:group-23">
+      <p>Machen Sie hier bitte Angaben zu den Fehlzeiten des/der Jugendlichen. Die Daten sollten ggf. in Absprache mit der Schule erhoben werden. Ist es im Ausnahmefall nicht möglich, Angaben zu machen, tragen Sie „unbekannt“ ein.</p>
+    </div>
+    <div id="5:help:group-15">
+      <p>Bitte benennen Sie wichtige Bezugspersonen des/der Jugendlichen. Die Kontaktdaten dienen bei Bedarf als Arbeitshilfe in der Koordinierungsstelle und werden nicht durch das zentrale Datenmonitoring eingelesen und ausgewertet. Im unteren Teil können Sie ggf. Adressen wichtiger Bezugspersonen und deren Funktion festhalten.</p>
+    </div>
+    <div id="6:help:cm_ziel">
+      <p>Bitte beurteilen Sie zusammenfassend, inwieweit Sie die im Case Management vereinbarten Ziele insgesamt erreicht haben. Hier geht es eher um eine allgemeine „globale“ Einschätzung.</p>
+    </div>
+    <div id="7:help:group-21">
+      <p>Beurteilen Sie jeweils die Ausprägung der Schulverweigerungshaltung des/der Schülerin. Eine aktive Schulverweigerung liegt vor, wenn der/die Jugendliche wiederholt und über einen längeren Zeitraum hinweg unentschuldigt der Schule fern geblieben ist bzw. noch fern bleibt oder zwar physisch anwesend ist, den Unterricht jedoch durch Störungen aktiv verweigert. Eine passive Schulverweigerung ist einerseits dadurch gekennzeichnet, dass die Schülerin/der Schüler zwar im Unterricht anwesend sind, sich jedoch nicht am Unterrichtsgeschehen beteiligt und kein Interesse zeigt. Von passiver Verweigerung spricht man auch, wenn die Kinder und Jugendlichen der Schule entschuldigt fernbleiben, jedoch in einem Maße, welches inhaltlich nicht nachvollziehbar ist. Die passive Verweigerungshaltung ist nicht nach außen gekehrt, verläuft in der Regel schulkonform und ist daher oft schwer bzw. erst spät erkennbar.</p>
+    </div>
+    <div id="8:help:cm_end_verm">
+      <p>Angaben zur Vermittlung geben in der Auswertung Auskunft über die Vermittlungssituation der Jugendlichen. Nach der Vermittlung beginnt die Phase der Nachbetreuung.</p>
+    </div>
+    <div id="9:help:group-9">
+      <p>Zuverlässige Aussagen zur Situation von Menschen mit Migrationshintergrund lassen sich nur dann treffen, wenn, über dem Kriterium der Staatsangehörigkeit hinaus weitere Faktoren zugrunde gelegt werden. Die Staatsangehörigkeit sagt nur bedingt etwas über den tatsächlichen Migrationshintergrund aus. Um diesen zuverlässig zu erfassen, wird neben der Staatsangehörigkeit und dem Geburtsland des/r Jugendlichen auch nach der zu Hause vorrangig gesprochenen Sprache gefragt. Angaben zum Migrationshintergrund und der damit evtl. verbundenen Förderplanung sind sowohl für spezifische Angebote der Koordinierungsstelle (Cultural Mainstreaming) relevant, aber auch um Aussagen über die Klientenstruktur der Koordinierungsstelle treffen zu können und die in den Förderrichtlinien des Programms vorgesehene Zielgruppen- und Zugangssteuerung der Koordinierungsstelle zu ermöglichen.</p>
+    </div>
+    <div id="10:help:komp_schule_e_1">
+      <p>Hier geht es um die allgemeine Fähigkeit, Konzepte, Gedanken, Gefühle, Tatsachen und Meinungen im Alltag sprachlich ausdrücken zu können.</p>
+    </div>
+    <div id="11:help:group-105">
+      <p>Benennen Sie den Notendurchschnitt des Schülers/der Schülerin im letzten Halbjahr über alle Fächer hinweg bzw. für die Fächer Deutsch, Mathematik und 1. Fremdsprache.</p>
+    </div>
+    <div id="12:help:kontakt">
+      <p>Geben Sie bitte an, wann die Koordinierungsstelle bzw. dessen Mitarbeiter/Innen erstmals Kontakt zum/zur Jugendlichen hatte.</p>
+    </div>
+    <div id="13:help:group-81">
+      <p>Beurteilung der Problembereiche durch den/die Case Manager/in. Diese Einschätzungen können/sollten ggf. Ausgangsbasis für die Elternarbeit sein.</p>
+    </div>
+    <div id="14:help:nat_staat">
+      <p>Die Staatsangehörigkeit des/der Jugendlichen dient als Indikator für einen Migrationshintergrund</p>
+    </div>
+    <div id="15:help:group-43">
+      <p>Die periodische Erfassung der Fehlstunden und Fehltage im Längsschnitt dient zur Darstellung der Entwicklung der aktiven Schulverweigerungshaltung innerhalb der Betreuungsdauer. Die Daten sollten ggf. in Absprache mit der Schule erhoben werden.</p>
+    </div>
+    <div id="16:help:sv_monate">
+      <p>Wenn bekannt: seit wann zeigt der/die Schüler/in schulverweigerndes Verhalten (ggf. Schätzung)?</p>
+    </div>
+    <div id="17:help:zugang">
+      <p>Geben Sie bitte den Zugangsweg an, über den der/die Jugendliche in das Programm „Schulverweigerung – Die 2. Chance“ gekommen ist.</p>
+    </div>
+    <div id="18:help:nat_status">
+      <p>Der Aufenthaltsstatus ist gesetzlich geregelt (Aufenthaltstitel) und entscheidet darüber, wie lange ein behördlich registrierter Immigrant in einem Land verbleiben kann (vgl. §7 AufenthG - Gesetz über den Aufenthalt, die Erwerbstätigkeit und die Integration von Ausländern im Bundesgebiet).</p>
+    </div>
+    <div id="19:help:group-79">
+      <p>Beurteilen Sie, wie sich die Schulverweigerungshaltung des/der Schüler/in im Einzelnen äußert.</p>
+    </div>
+    <div id="20:help:page-1">
+      <p>Die Kontaktdaten dienen bei Bedarf als Arbeitshilfe in der Koordinierungsstelle und werden nicht durch das zentrale Datenmonitoring eingelesen und ausgewertet.</p>
+    </div>
+    <div id="21:help:group-85">
+      <p>Sprachkompetenzen und die damit verbundenen mündlichen und schriftlichen Fertigkeiten sind wesentliche Schlüsselkompetenzen zur Teilhabe am sozialen und gesellschaftlichen Leben und eine wichtige Voraussetzung für den beruflichen Übergang. Sprachkompetenzen werden bei Kompetenzfeststellungsverfahren häufig in den Dimensionen Verstehen, Sprechen, Lesen und Schreiben erfasst. Diese Frage bezieht sich nicht nur auf die Jugendlichen mit Migrationshintergrund, sondern auch auf deutsche Jugendliche.</p>
+    </div>
+    <div id="22:help:schulform">
+      <p>Aufgrund der verschiedenen Bezeichnungen in den einzelnen Bundesländern sind hier die zentralen Schulformen und -stufen aufgeführt. Wählen Sie bitte die Bezeichnung, die am ehesten der derzeit vom Jugendlichen besuchten Schule entspricht.</p>
+    </div>
+    <div id="23:help:group-28">
+      <p>Im Abschnitt „Kompetenzfeststellung“ werden zunächst Angaben zu den durchgeführten Verfahren gemacht und danach die Ergebnisse der Kompetenzfeststellung dokumentiert. Da in den Koordinierungsstellen unterschiedliche Kompetenzfeststellungsverfahren mit unterschiedlichen Schwerpunktsetzungen zum Einsatz kommen, sieht dieser Abschnitt eine differenzierte Zusammenfassung der Ergebnisse der eingesetzten Kompetenzerfassung vor. Es kann sein, dass nicht zu allen Feldern auch tatsächlich Ergebnisse vorliegen. Deshalb bitten wir die Case Managerin bzw. den Case Manager, auf der Grundlage der vorliegenden Dokumente zur Kompetenzfeststellung (Gutachten u.a.) Angaben nur zu den Kompetenzen des Jugendlichen zu machen, die tatsächlich im Kompetenzfeststellungsverfahren gemessen wurden. Felder, zu denen keine Informationen vorliegen, bitte nicht ausfüllen! Um möglichst objektive Auskunft zum Entwicklungsstand der einzelnen Kompetenzbereiche des Jugendlichen zu bekommen, wurde bei der Kompetenzfeststellung eine weitgehende Kategorisierung und Standardisierung der Kompetenzen vorgenommen. Dabei handelt es sich um folgende Kompetenzbereiche: schulbezogene Kompetenzen, soziale und Selbstkompetenzen sowie  psychische und physische Kompetenzen. Die Ausprägungen der einzelnen Kompetenzen werden nach einem Antwortschema von „sehr gut entwickelt“ bis „sehr schwach entwickelt“ beschrieben. „Sehr gut entwickelt“ meint – die Kompetenzen kommen in vielen Situationen zum Ausdruck und sind intensiv ausgeprägt, „sehr schwach entwickelt meint“ – die Kompetenzen sind nicht vorhanden bzw. haben eine sehr geringe Ausprägung.</p>
+    </div>
+    <div id="24:help:group-13,group-104">
+      <p>Mit dieser Angabe soll der These nachgegangen werden, inwieweit der Bildungserfolg von der familiären und sozialen Herkunft determiniert wird.</p>
+    </div>
+    <div id="25:help:komp_anm">
+      <p>Fassen Sie ggf. in dieser Dokumentation bitte die Ergebnisse der Kompetenzfeststellung als Basis für die Förderplanung zusammen. Die freie Dokumentation wird im Rahmen der Anonymisierung der Akte gelöscht, da die datenschutzrechtliche Relevanz der Inhalte nicht automatisch bewertet werden kann.</p>
+    </div>
+    <div id="26:help:page-0">
+      <p>Angaben über die jeweiligen Zuständigkeiten sind für die bessere Einordnung und Nutzung der Klientendaten in der Koordinierungsstelle wichtig. Darüber hinaus werden im Rahmen des programmweiten Datenmonitorings z.B. unterschiedliche Zugangswege der Jugendlichen ermittelt. Durch die Erfassung dieser können informelle Zugänge (unmittelbares Umfeld) sowie institutionelle Zugänge abgebildet werden. Ausgehend von diesen Informationen kann der Koordinationsbedarf und -aufwand der Koordinierungsstellen und der einzelnen Institutionen abgestimmt werden. Falls im Laufe des Case-Managements die zuständige Mitarbeiterin bzw. der zuständige Mitarbeiter in der Koordinierungsstelle wechselt, so wird das Feld „Name der zuständigen Mitarbeiterin/des zuständigen Mitarbeiters in der Koordinierungsstelle“ automatisch angepasst.</p>
+    </div>
+    <div id="27:help:cm_end_art">
+      <p>Die Art der Beendigung des Case Managements wird im Sinne einer „regulären Beendigung“ oder eines „Abbruchs“ dokumentiert. Die Angabemöglichkeit „sonstige Beendigung“ versteht sich dabei nicht im Sinne einer „negativen“ Beendigung. Bei der Angabe in diesem Feld handelt sich um eine formale Beendigung des Case Managements, da die Nachbetreuungsphase dabei nicht berücksichtigt ist. In Abhängigkeit Ihrer Angabe sind ggf. Angaben zur „erfolgreichen Reintegration“, zur „sonstigen Beendigung“ oder zum „Abbruch“ zu machen.</p>
+    </div>
+    <div id="28:help:nbtr">
+      <p>Für welchen Zeitraum planen Sie für den Jugendlichen nach der Beendigung des Case Management eine Nachbetreuung einzurichten?</p>
+    </div>
+    <div id="29:help:erstgespraech">
+      <p>Geben Sie bitte an, wann im Rahmen der aktuellen Arbeit mit dem/der Jugendlichen das Erstgespräch stattfand.</p>
+    </div>
+    <div id="30:help:group-84">
+      <p>Kompetenzen sind die Summe des Wissens, der Fertigkeiten und Einstellungen, die es einem Menschen erlauben, Handlungen auszuführen. Hier geht es nicht um eine Einstufung der Kompetenzen anhand von vergebenen Schulnoten, sondern um eine Einschätzung anwendungsbezogener Kompetenzen anhand der ggf. verwendeten Kompetenzfeststellungsverfahren.</p>
+    </div>
+    <div id="31:help:group-51">
+      <p>Benennen Sie hier die aus Ihrer Sicht wichtigsten drei Unterstützungsangebote.</p>
+    </div>
+    <div id="32:help:hilfm_zeit_1_1,hilfm_zeit_1_2,hilfm_zeit_1_3,hilfm_zeit_2_1,hilfm_zeit_2_2,hilfm_zeit_2_3,hilfm_zeit_3_1,hilfm_zeit_3_2,hilfm_zeit_3_3">
+      <p>Schätzen Sie hier bitte grob ein, ob es sich um ein lang-, mittel-, oder kurzfristiges Ziel handelt.</p>
+    </div>
+    <div id="33:help:kf_verfahren_1">
+      <p>Unter „Entwicklung und Durchführung des Verfahrens zur Kompetenzfeststellung" kann nur eine Angabe gemacht werden - hier ist das, bezogen auf den Fall, bedeutsamste Verfahren anzuklicken.</p>
+    </div>
+    <div id="34:help:group-63">
+      <p>In der Abschlussbewertung werden zum einen Daten zum Verlauf bzw. zur Beendigung des Case Managements erfasst. Zum anderen wird die Zielerreichung im Hinblick auf die schulische und soziale Integration des/ der Jugendlichen dokumentiert.</p>
+    </div>
+    <div id="35:help:nat_land,nat_muspra,nat_haspra,vater_land">
+      <p>Dieses Item dient als Indikator für einen Migrationshintergrund.</p>
+    </div>
+    <div id="36:help:entw_beh">
+      <p>Anerkannte Behinderung: Behindertenausweis bzw. Feststellungsbescheid mit Grad der Behinderung (GdB) von mind. 20 Prozent</p>
+    </div>
+    <div id="37:help:einwilligung">
+      <p>Diese Einwilligungserklärung ist die Grundlage zur Erfassung und Speicherung von personenbezogenen Daten und Voraussetzung für die Aufnahme in das Case Management.</p>
+    </div>
+    <div id="38:help:group-53">
+      <p>Beurteilen Sie bitte die aktuelle Relevanz folgender Zielstellungen und geben Sie an, inwieweit die Zielstellungen im Laufe der Betreuung erreicht wurden. Bitte aktualisieren Sie diese Angaben anlassbezogen, so dass spätestens zum Abschluss der Betreuung die Angaben zur Zielerreichung aktuell sind.</p>
+    </div>
+    <div id="39:help:nat_wohn">
+      <p>Geben Sie hier das Datum an seit dem der/die Jugendliche in Deutschland wohnhaft ist. Sollte keine genaue Angabe möglich sein, so geben Sie die Jahreszahl an. Aus Konsistenzgründen der Datumsangaben wird das Datum als der 01.01 des jeweiligen Jahres gespeichert.</p>
+    </div>
+    <div id="40:help:group-80">
+      <p>Beurteilung der erzieherischen Kompetenzen durch den/die Case Manager/in. Diese Einschätzungen können/sollten ggf. Ausgangsbasis für die Elternarbeit sein.</p>
+    </div>
+    <div id="41:help:hilfm_1,hilfm_2,hilfm_3">
+      <p>Geben Sie bitte an, ob es ein Unterstützungsangebot gab. Falls ja, füllen Sie bitte die nachfolgenden Felder aus.</p>
+    </div>
+    <div id="42:help:page-6">
+      <p>Da der/die Jugendliche Teil der Familie ist, darf er hierzu Auskünfte geben. Die sozioökonomische Lage der jungen Menschen und das Phänomen Benachteiligung werden nach gegenwärtigen Untersuchungen in Verbindung gebracht.</p>
+    </div>
+    <div id="43:help:group-39">
+      <p>Hier handelt es sich teilweise um eine besondere Art personenbezogener Daten (§ 3, Abs. 9 BDSG), auf die in der Einwilligungserklärung ausdrücklich hingewiesen wird (§ 4a, Abs. 3 BDSG).</p>
+    </div>
+    <div id="44:help:hilfm_erreicht_1_1,hilfm_erreicht_1_2,hilfm_erreicht_1_3,hilfm_erreicht_2_1,hilfm_erreicht_2_2,hilfm_erreicht_2_3,hilfm_erreicht_3_1,hilfm_erreicht_3_2,hilfm_erreicht_3_3">
+      <p>Beurteilen Sie bitte aktuell, inwieweit die Zielstellung erreicht wurde. Bitte aktualisieren Sie ihre Angabe anlassbezogen.</p>
+    </div>
+    <div id="45:help:cm2c">
+      <p>Wurde der/die Jugendliche in der Vergangenheit schon einmal im Projekt „Schulverweigerung – Die 2. Chance“ betreut?</p>
+    </div>
+    <div id="46:help:dok_eingang,dok_angaben,dok_biograf,dok_entw,dok_cm,dok_abschl">
+      <p>Die freie Dokumentation dient bei Bedarf als Arbeitshilfe in der Koordinierungsstelle und wird nicht durch das zentrale Datenmonitoring eingelesen und ausgewertet.</p>
+    </div>
+    <div id="47:help:page-2">
+      <p>In diesem Abschnitt werden zunächst persönliche Daten der Jugendlichen erfasst. Danach werden Angaben zum Migrationshintergrund, zur familiären Situation und zu relevanten Bezugspersonen im sozialen Umfeld des/der Jugendlichen erhoben. Diese Angaben bilden eine Ausgangsbasis für die weitere Beratung des/der Jugendlichen.</p>
+    </div>
+    <div id="1:required:hilfm_erreicht_3_1,hilfm_erreicht_3_2,hilfm_erreicht_3_3">
+      <p>Im Rahmen der ESF-Berichterstattung sind teilnahmebezogene Daten in aggregierter Form bereitzuhalten. Dazu sind relevante Felder verpflichtend auszufüllen. Die Pflichtfelder sind einzelnen Phasen zugeordnet und entsprechend markiert. Ein Eintritt in eine neue Phase setzt voraus, dass alle Pflichtfelder ausgefüllt wurden.</p>
+    </div>
+    <div id="1:required:group-98">
+      <p>Im Rahmen der ESF-Berichterstattung sind teilnahmebezogene Daten in aggregierter Form bereitzuhalten. Dazu sind relevante Felder verpflichtend auszufüllen. Die Pflichtfelder sind einzelnen Phasen zugeordnet und entsprechend markiert. Ein Eintritt in eine neue Phase setzt voraus, dass alle Pflichtfelder ausgefüllt wurden. Bitte beachten Sie, dass die Option "keine Angabe" in diesen Feldern keine gültige Auswahl ist, sondern das Feld als nicht ausgefüllt markiert.</p>
+    </div>
+    <div id="1:required:fkz,einwilligung,ks,fn,kontakt,cm2c,zugang,erstgespraech,datum_erklaerung,datum_cm_start,name,vorname,geschlecht,geburtsdatum,wohnort,nat_land,nat_staat,nat_muspra,nat_haspra,mutter_land,mutter_staat,vater_land,vater_staat,schulform,klassenstufe,klassenwdh,sv_aktiv,sv_passiv,group-79,sv_a,sv_b,sv_c,sv_d,sv_e,sv_f,sv_g,group-23,fehltg_sj,fehltg_sj_ue,fehltg_m,fehltg_m_ue,fehlst_sj,fehlst_sj_ue,fehlst_m,fehlst_m_ue,sv_monate,abschl_gef,abschl_pr,uleistung_beginn,uleistung_verg,datum_kf_1,entw_beh,group-41,datum_bfplan,beschul_a,beschul_b,beschul_c,beschul_d,beschul_e,beschul_f,beschul_g,reint_ziel,group-92,zielcm_schul_a,zielcm_schul_b,zielcm_schul_c,zielcm_schul_d,zielcm_schul_e,zielcm_schul_f,zielcm_schul_g,zielcm_schul_h,group-93,zielcm_sozial_a,zielcm_sozial_b,zielcm_sozial_c,zielcm_sozial_d,zielcm_sozial_e,zielcm_sozial_f,zielcm_sozial_g,zielcm_sozial_h,hilfm_1,hilfm_2,hilfm_3,hilfm_art_1,hilfm_art_2,hilfm_art_3,hilfm_start_1,hilfm_start_2,hilfm_start_3,hilfm_ende_1,hilfm_ende_2,hilfm_ende_3,hilfm_erreicht_1_1,hilfm_erreicht_1_2,hilfm_erreicht_1_3,hilfm_erreicht_2_1,hilfm_erreicht_2_2,hilfm_erreicht_2_3,elt_mn_a,elt_mn_b,elt_mn_c,elt_mn_d,elt_mn_e,elt_mn_f,elt_mn_g,elt_mn_h,elt_mn_i,elt_mn_j,datum_cm_ende,cm_kontakt,group-94,beschul_end_a,beschul_end_b,beschul_end_c,beschul_end_d,beschul_end_e,beschul_end_f,beschul_end_g,beschul_end_h,uleistung_ende,uleistung_ende_a,uleistung_ende_b,uleistung_ende_c,uleistung_ende_d,uleistung_ende_e,uleistung_ende_f,uleistung_ende_g,uleistung_ende_h,uleistung_ende_i,uleistung_ende_j,cm_end_art,cm_end_verm,cm_end_schul,cm_end_jg,cm_end_sonst,cm_end_ab,cm_end_abgr,cm_ziel,cm_schulbes_1,cm_schulbes_2,group-96,cm_zielsch_a,cm_zielsch_b,cm_zielsch_c,cm_zielsch_d,cm_zielsch_e,cm_zielsch_f,cm_zielsch_g,bool-363,group-97,cm_zielsoz_a,cm_zielsoz_b,cm_zielsoz_c,cm_zielsoz_d,cm_zielsoz_e,cm_zielsoz_f,cm_zielsoz_g,cm_zielsoz_h,nbtr,nbetr_ende,nbetr_verbl">
+      <p>Im Rahmen der ESF-Berichterstattung sind teilnahmebezogene Daten in aggregierter Form bereitzuhalten. Dazu sind relevante Felder verpflichtend auszufüllen. Die Pflichtfelder sind einzelnen Phasen zugeordnet und entsprechend markiert. Ein Eintritt in eine neue Phase setzt voraus, dass alle Pflichtfelder ausgefüllt wurden. Bitte beachten Sie, dass die Option "keine Angabe" in diesen Feldern keine gültige Auswahl ist, sondern das Feld als nicht ausgefüllt markiert. Sollte in Ausnahmefällen keine Angabe möglich sein, tragen Sie bitte bis zur zulässigen Länge den Wert "unbekannt" ein.</p>
+    </div>
+    <div id="1:required:ID">
+      <p>PF</p>
+    </div>
+    <div id="1:evaluation:ID">
+      <p>AR</p>
+    </div>
+    <div id="1:evaluation:kontakt,geschlecht,geburtsdatum,nat_land,nat_staat,nat_muspra,mutter_bildung,mutter_beruf,vater_bildung,vater_beruf,fehlj_1,fehlj_2,fehlj_3,fehlj_4,fehlj_5,fehlj_6,fehlj_7,fehlj_8,fehlj_9,fehlj_10,fehlj_11,fehlj_12,fehlm_1,fehlm_2,fehlm_3,fehlm_4,fehlm_5,fehlm_6,fehlm_7,fehlm_8,fehlm_9,fehlm_10,fehlm_11,fehlm_12,fehltg_1,fehltg_2,fehltg_3,fehltg_4,fehltg_5,fehltg_6,fehltg_7,fehltg_8,fehltg_9,fehltg_10,fehltg_11,fehltg_12,fehlutg_1,fehlutg_2,fehlutg_3,fehlutg_4,fehlutg_5,fehlutg_6,fehlutg_7,fehlutg_8,fehlutg_9,fehlutg_10,fehlutg_11,fehlutg_12,fehlst_1,fehlst_2,fehlst_3,fehlst_4,fehlst_5,fehlst_6,fehlst_7,fehlst_8,fehlst_9,fehlst_10,fehlst_11,fehlst_12,fehlfust_1,fehlfust_2,fehlfust_3,fehlfust_4,fehlfust_5,fehlfust_6,fehlfust_7,fehlfust_8,fehlfust_9,fehlfust_10,fehlfust_11,fehlfust_12,datum_cm_ende,cm_end_art">
+      <p>Für das Berichtswesen sind wir gegenüber dem BMFSFJ verpflichtet, Angaben über den Status des Programms zu machen. Um an dieser Stelle die Transparenz zu erhöhen, sind die entsprechenden Felder markiert.</p>
+    </div>
+  </body>
 </html>

Modified: wasko/trunk/waskaweb/controllers/CaseBase.py
===================================================================
--- wasko/trunk/waskaweb/controllers/CaseBase.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/controllers/CaseBase.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -33,7 +33,8 @@
 
 from paste.httpexceptions           import HTTPNotFound
 from waskaweb.lib.base              import *
-from waskaweb.lib.renderer          import FormDataImpl, ViewRenderer, AidListRenderer
+from waskaweb.lib.renderer          import FormDataImpl, ViewRenderer
+from waskaweb.lib.filecache         import FileCache 
 from waskaweb.model.case            import CaseFactory, LoadCaseNotExistsError 
 from waskaweb.model.repeatgroup     import AidList 
 
@@ -74,8 +75,7 @@
             vr                = ViewRenderer(formdata, ro_mode)
             content           = vr.renderView(
                 str(ti.key), 
-                ds_id,  
-                "/case/save", 
+                ds_id,
                 showNext = not ti.nextKey() is None)
         return (content, navigation)
 
@@ -106,6 +106,7 @@
 
         form_errors = case.getFormErrors()
         formdata = FormDataImpl(
+            FileCache(),
             help=help, 
             page=page, 
             errors=form_errors, 

Modified: wasko/trunk/waskaweb/controllers/case.py
===================================================================
--- wasko/trunk/waskaweb/controllers/case.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/controllers/case.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -404,25 +404,26 @@
     def showHelp(self, help_id):
         help = g.helpData.getHelp(help_id)
         if help is None: raise HTTPNotFound()
-        c.helpdata  = unicode(help, 'utf-8')
+        c.helpdata  = help
         return render('/casemanagement/help.mako')
 
     @checkRole(('admin_ka', 'cm_ka', 'pb_ka'))
     def showRequired(self, help_id):
         required = g.helpData.getAnnotations(help_id)
         if not required: raise HTTPNotFound()
-        c.required  = unicode(required, 'utf-8')
+        c.required  = required
         return render('/casemanagement/required.mako')
 
     @checkRole(('admin_ka', 'cm_ka', 'pb_ka'))
     def showHelpEvaluation(self, help_id):
-        required = g.helpData.getAnnotations(help_id, ('evaluation',))
+        required = g.helpData.getEvaluation(help_id)
         if not required: raise HTTPNotFound()
-        c.required  = unicode(required, 'utf-8')
+        c.required  = required
         return render('/casemanagement/required.mako')
 
     @checkRole('cm_ka')
     def save(self):
+        print request.params
         case_session = session.get('case')
         try:
             ti = self.getNavigation().getTreeItem(request.params["page"])

Modified: wasko/trunk/waskaweb/lib/app_globals.py
===================================================================
--- wasko/trunk/waskaweb/lib/app_globals.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/lib/app_globals.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -26,7 +26,7 @@
 
 from waskaweb.model.io.document import openDocument 
 import waskaweb.model.data as data 
-from waskaweb.lib.formular_help import HelpProvider 
+from waskaweb.model.annotations import AnnotationsProvider 
 
 import waskaweb.lib.security    as security
 
@@ -47,7 +47,6 @@
         # XXX: Dead ugly!
         path = os.path.join(os.path.dirname(__file__), '..', '..', 'formed')
         treeFile  = os.path.join(path, 'formedtree_web.xml')
-        helpFile  = os.path.join(path, 'waska-hilfetexte.xhtml')
         annonFile = os.path.join(path, 'annotations.xhtml')
 
         # XXX: Check why the following throws key exceptions. Is config var not
@@ -55,13 +54,7 @@
         #treeFile = os.path.join(os.path.dirname(__file__), config['formed_tree'])
         self.formedTree = openDocument(treeFile)
         try:
-            f = open(helpFile, "rb")
-            help_xhtml = f.read()
-            f.close()
-            f = open(annonFile)
-            annon_xhtml = f.read()
-            f.close()
-            self.helpData = HelpProvider(help_xhtml, annon_xhtml) 
+            self.helpData = AnnotationsProvider(annonFile) 
         except:
             traceback.print_exc(file=sys.stderr)
             print >> sys.stderr, "Could not open Helpfile"

Added: wasko/trunk/waskaweb/lib/filecache.py
===================================================================
--- wasko/trunk/waskaweb/lib/filecache.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/lib/filecache.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -0,0 +1,38 @@
+# -*- coding: UTF-8 -*-
+# Copyright 2007, 2008 Intevation GmbH, Germany, <info at intevation.de>
+# 
+# This file is part of mpuls WASKA (CoMPUter-based case fiLeS - 
+# Web-Anwendungs-Server fuer Kompetenzagenturen).
+# 
+# mpuls WASKA is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+# 
+# mpuls WASKA is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
+# License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public
+# License along with mpuls WASKA. If not, see <http://www.gnu.org/licenses/>.
+# 
+# mpuls WASKA has been developed on behalf of the 
+# Projekttraeger im Deutschen Zentrum fuer Luft- und Raumfahrt e.V. (PT-DLR)
+# within the programme Kompetenzagenturen (Durchfuehrungsphase) funded by
+# the Bundesministerium fuer Familie, Senioren, Frauen und Jugend and 
+# European Social Fund resources.
+
+class FileCache(object):
+
+    def __init__(self, base=None):
+        self.base  = base
+        self.cache = {}
+
+    def get_image(self, name, attributes = ""):
+        return '<img src="%s" %s>' % (name, attributes)
+
+    def get_base(self):
+        return self.base
+
+# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:

Deleted: wasko/trunk/waskaweb/lib/formular_help.py
===================================================================
--- wasko/trunk/waskaweb/lib/formular_help.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/lib/formular_help.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -1,128 +0,0 @@
-# -*- coding: latin1 -*-
-#
-# Copyright 2007, 2008 Intevation GmbH, Germany, <info at intevation.de>
-# 
-# This file is part of mpuls WASKA (CoMPUter-based case fiLeS - 
-# Web-Anwendungs-Server fuer Kompetenzagenturen).
-# 
-# mpuls WASKA is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Affero General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-# 
-# mpuls WASKA is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
-# License for more details.
-# 
-# You should have received a copy of the GNU Affero General Public
-# License along with mpuls WASKA. If not, see <http://www.gnu.org/licenses/>.
-# 
-# mpuls WASKA has been developed on behalf of the 
-# Projekttraeger im Deutschen Zentrum fuer Luft- und Raumfahrt e.V. (PT-DLR)
-# within the programme Kompetenzagenturen (Durchfuehrungsphase) funded by
-# the Bundesministerium fuer Familie, Senioren, Frauen und Jugend and 
-# European Social Fund resources.
-#
-# Authors:
-# Sascha L. Teichmann <teichmann at intevation.de>
-#
-
-from xml.dom.minidom import parseString
-from xml.dom.ext     import Print
-from cStringIO       import StringIO
-
-import re
-
-ANNOTATION_DIV_ID = re.compile("([a-zA-Z0-9_]+):([a-zA-Z]+):([a-zA-Z0-9_,]+)")
-
-class Annotation:
-
-    def __init__(self, kind, msg):
-        self.kind = kind
-        self.msg  = msg
-
-    def __str__(self):
-        return repr(self.kind) + ": " + repr(self.msg)
-
-    def __repr__(self):
-        return self.__str__()
-
-def buildHelps(help_xhtml):
-    helps = {}
-    dom = None
-    try:
-        dom = parseString(help_xhtml)
-        for div in dom.getElementsByTagName("div"):
-            id = div.getAttribute("id")
-            if not id: continue
-            out = StringIO()
-            Print(div, out, 'UTF-8')
-            helps[id] = out.getvalue()
-            out.close()
-    finally:
-        if dom: dom.unlink()
-
-    return helps
-
-def buildAnnotations(annotation_xhtml):
-    annotations = {}
-    if not annotation_xhtml:
-        return annotations
-
-    dom = None
-    try:
-        dom = parseString(annotation_xhtml)
-        for div in dom.getElementsByTagName("div"):
-            id = div.getAttribute("id")
-            if not id: continue
-            m = ANNOTATION_DIV_ID.match(id)
-            if not m: continue
-            id    = m.group(1)
-            kind  = m.group(2)
-            items = [item for item in m.group(3).split(",") if item]
-            if not items: continue
-            div.setAttribute("id", id)
-
-            out = StringIO()
-            Print(div, out, 'UTF-8')
-            msg = out.getvalue()
-            out.close()
-
-            for item in items:
-                annons = annotations.setdefault(item, [])
-                annons.append(Annotation(kind, msg))
-    finally:
-        if dom: dom.unlink()
-
-    return annotations
-
-class HelpProvider:
-
-    def __init__(self, help_xhtml, annotation_xhtml = None):
-
-        self.helps       = buildHelps(help_xhtml)
-        self.annotations = buildAnnotations(annotation_xhtml)
-
-    def hasHelp(self, id):
-        return self.helps.has_key(id)
-
-    def getHelp(self, id, default=None):
-        return self.helps.get(id, default)
-
-    def hasAnnotations(self, id, kinds = ("required", )):
-        annotations = self.annotations.get(id)
-        if not annotations: return False
-        for annotation in annotations:
-            if annotation.kind in kinds:
-                return True
-        return False
-
-    def getAnnotations(self, id, kinds = ("required", )):
-        try:
-            x = [a.msg for a in self.annotations[id] if a.kind in kinds]
-        except KeyError:
-            x = []
-        return ''.join(x)
-
-# vim:set ts=4 sw=4 si et sta sts=4:

Modified: wasko/trunk/waskaweb/lib/helpers.py
===================================================================
--- wasko/trunk/waskaweb/lib/helpers.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/lib/helpers.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -34,12 +34,15 @@
 from webhelpers import *
 from waskaweb.lib.adelexml import EVAL_NAMES, EVAL_DESCRIPTIONS
 from pylons.i18n import _
+from string import printable
 
 import datetime
 
 VALID_DATE = re.compile(r'^([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{4})$')
 VALID_TIME = re.compile(r'^([0-9]{1,2}):([0-9]{2})')
 
+PRINTABLE = frozenset(printable)
+
 def get_adele_name(id=None):
     return EVAL_NAMES.get(str(id), 'Auswertung Nr: %s' % id) 
 
@@ -238,3 +241,8 @@
     except:
         print >> sys.stderr, "Could not fetch KA-name from client certificate"
     return ''
+    
+def safe_unicode(s):
+    return u''.join([c for c in s if ord(c) > 127 or c in PRINTABLE])
+
+

Deleted: wasko/trunk/waskaweb/lib/renderer.py
===================================================================
--- wasko/trunk/waskaweb/lib/renderer.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/lib/renderer.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -1,853 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright 2007, 2008 Intevation GmbH, Germany, <info at intevation.de>
-# 
-# This file is part of mpuls WASKA (CoMPUter-based case fiLeS - 
-# Web-Anwendungs-Server fuer Kompetenzagenturen).
-# 
-# mpuls WASKA is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Affero General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-# 
-# mpuls WASKA is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
-# License for more details.
-# 
-# You should have received a copy of the GNU Affero General Public
-# License along with mpuls WASKA. If not, see <http://www.gnu.org/licenses/>.
-# 
-# mpuls WASKA has been developed on behalf of the 
-# Projekttraeger im Deutschen Zentrum fuer Luft- und Raumfahrt e.V. (PT-DLR)
-# within the programme Kompetenzagenturen (Durchfuehrungsphase) funded by
-# the Bundesministerium fuer Familie, Senioren, Frauen und Jugend and 
-# European Social Fund resources.
-#
-# Authors:
-# Sascha L. Teichmann <teichmann at intevation.de>
-#
-
-from waskaweb.model.nodecomponents import Node
-import waskaweb.lib.helpers as h
-import waskaweb.lib.filters as F
-
-
-import waskaweb.model.data as data
-
-from cgi import escape
-
-import re
-from datetime import date 
-
-TARGET=re.compile(r"(\w+)(?:|:(.+))$")
-
-HELP = \
-'''<span class="help"> <a href="%s" target="_blank">
-<img src="/images/icons/form_help.png" border="0"
-alt="Hilfesymbol"></a></span>'''
-
-REQUIRED = \
-'''<span class="help"> <a href="%s" target="_blank">
-<img src="/images/icons/required_12x12.png" border="0" alt="Pflichtfeld"></a></span>'''
-
-EVALUATION = \
-'''<span class="help"> <a href="%s" target="_blank">
-<img src="/images/icons/needed_12x12.png" border="0" alt="Auswertungsrelevant"></a></span>'''
-
-WEIGHTS = (15, 25, 30, 40, 45, 50, 55, 60, 75)
-
-SET_MODIFICATION = ' onchange="setModification();" '
-
-class FormData:
-   
-    def __init__(self, help=None):
-        self.help = help 
-
-    def getData(self, dataID):
-        return ""
-
-    def getPrintableData(self, dataID):
-        return self.getData(dataID) 
-
-    def getHelp(self, dataID):
-        if not self.help: return ''
-
-        out = []
-
-        if self.help.hasAnnotations(dataID):
-            out.append(REQUIRED % h.url_for('required', help_id=escape(dataID)))
-
-        if self.help.hasAnnotations(dataID, ('evaluation',)):
-            out.append(EVALUATION % h.url_for('showevalhelp', help_id=escape(dataID)))
-
-        if self.help.hasHelp(dataID):
-            out.append(HELP % h.url_for('formhelp', help_id=escape(dataID)))
-
-        return ''.join(out)
-
-    def getError(self, dataID):
-        return ""
-
-def digits(n):
-    d, c = 10, 1
-    while n >= d: d *= 10; c += 1
-    return c
-
-def visibleDepth(c):
-    depth = -1
-    while c:
-        if not(isinstance(c, data.GroupNode) and not c.isInvisible()):
-            depth += 1
-        c = c.parent
-    return depth
-
-def pretty(x):
-    if isinstance(x, date):
-        return "%02d.%02d.%04d" % (x.day, x.month, x.year)
-    if isinstance(x, (type(""), type(u""))):
-        return u"%s" % x
-        #return unicode(str(x), 'utf-8')
-    if isinstance(x, type(False)):
-        return x and "on" or ""
-    return str(x)
-
-class ErrorItem:
-
-    def __init__(self, page=None, bad=None, name=None):
-        self.page     = page
-        self.bad      = bad
-        self.name     = name
-        self.messages = []
-
-    def addMessage(self, msg):
-        self.messages.append(msg)
-
-    def getName(self):
-        return self.name
-
-    def getMessages(self):
-        return self.messages
-
-
-class FormDataImpl(FormData):
-
-    def __init__(self, help=None, page=None, errors=None, nc=None):
-        FormData.__init__(self, help)
-        self.page   = page
-        self.errors = errors
-        if nc:
-            self.nc     = nc #NodeComponent List []
-            self.widgets = self.nc[0].allWidgets()
-            print self.widgets
-        else: 
-            self.nc = []
-            self.widgets = {} 
-
-    def getData(self, dataID):
-        if not self.errors is None:
-            try:
-                ei = self.errors[dataID]
-                if not ei.bad is None:
-                    return u"%s" % pretty(ei.bad)
-            except KeyError:
-                pass
-        if not self.page is None:
-            value = self.page.getData(dataID)
-            if value is not None: value = pretty(value)
-            else:     value = ""
-            return value
-        return ""
-
-    def getPrintableData(self, dataID):
-        value = self.getData(dataID)
-        try:
-            widget = self.widgets[dataID]
-        except KeyError:
-            return value 
-
-        if not isinstance(widget, ChoiceNode):
-            return value
-
-        for c in widget.children:
-            if isinstance(c, BoolLeaf):
-                v = c.getValue()
-                if v == value:
-                    return c.getDescription()
-            elif isinstance(c, ExternalChoiceListLeaf):
-                cc = c.getChildren()
-                if cc:
-                    for i in cc:
-                        if isinstance(i, BoolLeaf):
-                            v = c.getValue()
-                            if v == value:
-                                return c.getDescription()
-
-        return value 
-
-    def getError(self, dataID):
-        if self.errors:
-            try:
-                ei = self.errors[dataID]
-                return u"<br>\n".join([u"%s" % s for s in ei.messages])
-            except KeyError:
-                pass
-        return ""
-
-
-class Text:
-    def __init__(self, txt):
-        self.txt = txt
-
-    def render(self, target, weight):
-        return self.txt
-
-class Div(Text):
-    def __init__(self, txt):
-        Text.__init__(self, txt)
-
-    def render(self, target, weight):
-        return '<div class="waska_form_element w100">%s</div>\n' % self.txt
-
-class Item(Text):
-    def __init__(self, txt):
-        Text.__init__(self, txt)
-
-    def render(self, target, weight):
-        weight = int(100.0*weight+0.5)
-        weight = weight in WEIGHTS and " w%d" % weight or " w100"
-        return '<div class="waska_form_element %s">\n%s</div>\n' % (
-            weight, self.txt)
-
-class Container:
-    def __init__(self, target):
-        self.target  = target
-        self.items   = []
-
-    def append(self, item, weight=1.0):
-        self.items.append((item, weight))
-
-    def render(self, doBreak=True):
-        if self.items:
-            out = []
-            f = 1.0/reduce(lambda x, y: x + y[1], self.items, 0)
-            ws = [f*w[1] for w in self.items]
-            target = self.target
-            out = [item[0].render(target, w) for item, w in zip(self.items, ws)]
-            if doBreak: out.append('<br>')
-            return "".join(out)
-        return ""
-
-class AidListRenderer:
-    def __init__(self, aidList=None, ro_mode=True, no_data=False):
-        self.aidList = aidList
-        self.ro_mode = ro_mode
-        self.no_data = no_data
-
-    def render(self, ds_id, page_id):
-        from waskaweb.lib.base import _ 
-
-        action = self.ro_mode and "show" or "edit"
-        # Translated string here
-        s_inst     = _('cm_rg_aid_overview_table_institution')
-        s_type     = _('cm_rg_aid_overview_table_offer_type')
-        s_act      = _('cm_rg_aid_overview_table_actions')
-        #s_show     = _('cm_rg_aid_overview_table_show')
-        s_edit     = _('cm_rg_aid_overview_table_edit')
-        s_goal     = _('cm_rg_aid_overview_table_goal')
-        s_show     = '<img src="/images/icons/view_red.gif" border="0" alt="' + _('cm_overview_a_show') + \
-            '" title="' + _('cm_overview_a_show') + '">'
-        s_delete   = '<img src="/images/icons/delete_red.gif" border="0" alt="' + _('cm_overview_a_delete') + \
-            '" title="' + _('cm_overview_a_delete') + '">'
-        s_notfound = _('cm_rg_aid_overview_table_no_offer_found')
-        s_legend   = _('cm_rg_aid_overview_legend_legend')
-        s_bb       = _('cm_rg_aid_overview_legend_bb')
-        s_bb_long  = _('cm_rg_aid_overview_legend_common_educational_area')
-        s_bv       = _('cm_rg_aid_overview_legend_bv')
-        s_bv_long  = _('cm_rg_aid_overview_legend_vocational_preparation')
-        s_bq       = _('cm_rg_aid_overview_legend_bq')
-        s_bq_long  = _('cm_rg_aid_overview_legend_qualifications')
-        s_lb       = _('cm_rg_aid_overview_legend_lb')
-        s_lb_long  = _('cm_rg_aid_overview_legend_life_skills')
-        s_newoffer = _('cm_rg_aid_overview_legend_new_offer')
-
-        out = []
-        # Build table header
-        out.append(u"""<div class="waska_form_element  w100"><table>
-            <tr>
-                <th class="table_header_h table_date">Von</th>
-                <th class="table_header_h table_date">Bis</th>
-                <th class="table_header_h">%s</th>
-                <th class="table_header_h">Art der Unterstützung</th>
-                <th class="table_header_h table_width_small">%s</th>
-                <th class="table_header_h table_action">%s</th>
-            </tr>""" % (s_inst, s_type, s_act))
-        idset = True
-        if not self.no_data:
-            for aid in self.aidList.getDatasets():
-                class_str = ""
-                if idset:
-                    out.append(u"""<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>""" % (F.NA(aid.start_date), F.NA(aid.end_date), F.NA(aid.institution), F.NA(aid.type), F.NA(aid.category)))
-                    out.append(u"""<td class="table_action"><a href="/rg_aid/%s/%s/%s">%s</a>""" % (action,aid.id, aid.page_id, s_show))
-                    if not self.ro_mode and h.hasRole(['cm_ka']):
-                        out.append(u"""<a href="/rg_aid/delete/%s/%s/0">%s</a>""" % (aid.id, aid.category_id, s_delete))
-                    out.append("""</td></tr>""")
-                    out.append(u"""<tr><td colspan="6">%s: %s</td></tr>""" % (s_goal, F.NA(aid.goal)))
-                else:
-                    out.append(u"""<tr class="table_row_v"><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>""" % (F.NA(aid.start_date), F.NA(aid.end_date), F.NA(aid.institution), F.NA(aid.type), F.NA(aid.category)))
-                    out.append(u"""<td class="table_action"><a href="/rg_aid/%s/%s/%s">%s</a>""" % (action,aid.id, aid.page_id, s_show))
-                    if not self.ro_mode and h.hasRole(['cm_ka']):
-                        out.append(u"""<a href="/rg_aid/delete/%s/%s/0">%s</a>""" % (aid.id, aid.category_id, s_delete))
-                    out.append("""</td></tr>""")
-                    out.append(u"""<tr class="table_row_v"><td colspan="6">%s: %s</td>""" % (s_goal, F.NA(aid.goal)))
-                idset = not idset
-                # out.append(u"""<td class="table_action"><a href="/rg_aid/%s/%s/%s">%s</a>""" % (action,aid.id, aid.page_id, s_show))
-                # if not self.ro_mode and h.hasRole(['cm_ka']):
-                #     out.append(u"""<a href="/rg_aid/delete/%s/%s/0">%s</a>""" % (aid.id, aid.category_id, s_delete))
-                # out.append("""</td></tr>""")
-
-                if self.aidList.numDatasets() <= 0:
-                    out.append(u"""<tr><td colspan="5">%s</td></tr>""" % (s_notfound))
-        out.append(u"""</table>""")
-        out.append(u"""<div class="legend">
-        %s <strong>%s</strong>: %s <strong>%s</strong>: %s <strong>%s</strong>: %s <strong>%s</strong>: %s 
-        </div></div>""" % (s_legend,s_bb, s_bb_long, s_bv, s_bv_long, s_bq, s_bq_long, s_lb, s_lb_long))
-
-        if not self.ro_mode and h.hasRole(['cm_ka']):
-            out.append(u"""<div class="waska_form">
-  <form name="new_unterstuetzung" action="/rg_aid/newAction" method="POST">
-  <label for="angebot">%s</label><br>""" % s_newoffer)
-            out.append(u"""<select name="aid_type" id="angebot">
-    <option value="0">%s</option>
-    <option value="1">%s</option>
-    <option value="2">%s</option>
-    <option value="3">%s</option>
-  </select>""" % (s_bb_long, s_bv_long, s_bq_long, s_lb_long))
-            out.append(u"""<input type="hidden" name="page_id" value="%s">
-  <input type="hidden" name="case_id" value="%s">
-  <input type="submit" value="Hinzufügen">""" % (page_id, ds_id))
-            out.append(u"""</form></div>""")
-        return "\n".join(out)
-
-class ViewRenderer:
-
-    def __init__(self, formdata=FormData(), ro_mode=True):
-        self.formdata        = formdata
-        self.stateStack      = None
-        self.containersStack = None
-        self.ro_mode         = ro_mode
-        self.items           = formdata.nc
-
-
-    def renderView(self, name="", case_id="", action="", showNext=True):
-
-        if not self.items:
-            return "No items given!"
-       
-        out = []
-        if not self.ro_mode:
-            out.append('<form id="waska_form" action="')
-            if action: out.append(escape(action, True))
-            out.append('" accept-charset="UTF-8" method="POST">\n')
-            # Add submitbutton at the top of the page too
-            out.append('<div class="waska_form_element w100">')
-            out.append('<input type="submit" name="__formular_save" value="Speichern">\n')
-            if showNext:
-                out.append('<input type="submit" name="__formular_next" value="Speichern und weiter">\n')
-            out.append('</div>')
-
-        self.stateStack = []
-        self.containersStack = [[Container(None) ]]
-        for item in self.items:
-            self._renderChild(item)
-
-        out.append(self.containersStack[-1][0].render())
-
-        if not self.ro_mode:
-            if action:
-                if name:
-                    out.append('<input type="hidden" name="page" value="%s">\n' \
-                        % escape(name, True))
-                if case_id:
-                    out.append('<input type="hidden" name="ds" value="%s">\n' \
-                        % escape(str(case_id), True))
-                out.append('<input type="submit" name="__formular_save" value="Speichern">\n')
-                if showNext:
-                    out.append('<input type="submit" name="__formular_next" value="Speichern und weiter">\n')
-            out.append('</form>\n')
-
-        return "".join(out)
-
-    def toTarget(self, item, target):
-        weight = 1.0
-        if target:
-            m = TARGET.match(target)
-            if m:
-                target = m.group(1)
-                try:
-                   weight = float(m.group(2))
-                except (TypeError, ValueError):
-                   pass
-
-        containersStack = self.containersStack
-        for container in containersStack[-1]:
-            if not container.target or container.target == target:
-                container.append(item, weight)
-                return
-
-    def _renderGroup(self, node):
-        description = node.getDescription()
-        name        = node.getName()
-
-        help        = self.formdata.getHelp(name)
-        data        = self.formdata.getData(name)
-        error       = self.formdata.getError(name)
-
-        out = []
-        
-        error = self.formdata.getData(name)
-        if node.isInvisible():
-            if description:
-                out.append("<!-- %s -->\n" % escape(description))
-            if error:
-                out.append('<!--<p class="errormsg">%s</p>-->' % escape(error))
-
-            out.append(self._renderRecursive(node))
-        else:
-            out.append("<fieldset>\n")
-            if description:
-                out.append(
-                    "<legend>%s %s</legend>\n" % (escape(description), help))
-            if error:
-                out.append('<!--<p class="errormsg">%s</p>-->\n' % error)
-            out.append(self._renderRecursive(node))
-            out.append("</fieldset>\n")
-
-        self.toTarget(Item("".join(out)), node.getTarget())
-
-    def _renderInfo(self, info):
-        if info.isInvisible(): return
-        text, depth = info.getValue(), visibleDepth(info)
-        if text:
-            text = "<h%s>%s</h%s>\n" % (depth, escape(text), depth)
-            self.toTarget(Div(text), info.getTarget())
-
-    def _renderRadio(self, radio):
-        description = radio.getDescription()
-        name        = radio.getName()
-
-        help        = self.formdata.getHelp(name)
-        data        = self.formdata.getData(name)
-        error       = self.formdata.getError(name)
-
-        label = self._renderLabel(description, name, help=help,)
-
-        out = [ label ]
-
-        self.stateStack.append(data)
-        out.append(self._renderRecursive(radio))
-        self.stateStack.pop()
-
-        self.toTarget(Text("".join(out)), radio.getTarget());
-
-    def _renderChoice(self, choice):
-        description = choice.getDescription()
-        name        = choice.getName()
-        size        = choice.getSize()
-        multiple    = choice.getMultiple()
-
-        help        = self.formdata.getHelp(name)
-        data        = self.formdata.getData(name)
-        error       = self.formdata.getError(name)
-        css_class   = []
-
-        label = self._renderLabel(description, name, help=help)
-
-        if name:
-            name = escape(name, True)
-            name = 'name="%s" id="%s"' % (name, name)
-        else:
-            name = ""
-
-        # Do not render choicelists in disabled mode as IE can not cope with
-        # css on disabled list elements. Instead just render the selected
-        # option within the list (ChoiceBool)
-        disabled = ""
-        #if self.ro_mode: 
-        #    disabled = 'disabled="disabled"'
-        #    css_class.append('readonly')
-        #else: disabled = ""
-
-        multiple = multiple and "multiple" or ""
-
-        if not size: size = "1"
-
-        out = [ label ]
-
-        if error:
-            css_class.append('error_box')
-
-        out.append(
-            '<select size="%s" class="%s" %s %s %s %s>\n' % (
-                size, " ".join(css_class), disabled, multiple, name, SET_MODIFICATION))
-
-        self.stateStack.append(data)
-        out.append(self._renderRecursive(choice, False))
-        self.stateStack.pop()
-
-        out.append("</select>\n")
-
-        self.toTarget(Item("".join(out)), choice.getTarget());
-
-    def _renderChoiceBool(self, bool):
-
-        description = bool.getDescription()
-        name        = bool.getName()
-        value       = bool.getValue()
-        selected    = ""
-        out         = ""
-        
-        toSelect = self.stateStack[-1]
-        selected = toSelect ==  (value or value==0) and "selected" or ""
-
-        if (value or value==0): value = 'value="%s"' % escape(value, True)
-        else:     value = ""
-
-        if description: description = escape(description)
-        else:           description = ""
-
-        if self.ro_mode:
-            if selected:
-                out = "<option %s %s>%s</option>\n" % (value, selected, description)
-        else:
-            out = "<option %s %s>%s</option>\n" % (value, selected, description)
-
-        self.toTarget(Text(out), bool.getTarget());
-
-    def _renderRadioBool(self, bool):
-
-        name        = bool.getName()
-        pname       = bool.parent.getName()
-        description = bool.getDescription()
-        value       = bool.getValue()
-        css_class   = []
-
-        toCheck = self.stateStack[-1]
-
-        checked = toCheck == value and "checked" or ""
-        
-        label = self._renderLabel(description, name, newline=False)
-
-        if value:
-            value = 'value="%s"' % escape(value, True)
-        else:
-            value = ""
-
-        if   pname: oname = 'name="%s"' % escape(pname, True)
-        elif  name: oname = 'name="%s"' % escape(name, True)
-        else:       oname = ''
-
-        if name: id = 'id="%s"' % escape(name, True)
-        else:    id = ""
-        
-        if self.ro_mode: 
-            disabled = "disabled"
-            css_class.append(disabled)
-        else: disabled = ""
-
-        out = '<input type="radio" class="%s" %s %s %s %s %s %s>%s\n' % (
-            " ".join(css_class), disabled, oname, id, value, checked, label, SET_MODIFICATION)
-
-        self.toTarget(Item(out), bool.getTarget())
-
-    def _renderPlainBool(self, bool):
-
-        name        = bool.getName()
-        description = bool.getDescription()
-        css_class   = []
-
-        data = self.formdata.getData(name)
-        if data == "1": 
-            value = 'value="%s"' % escape(data, True)
-            checked = "checked"
-        else:
-            value, checked = "", ""
-
-        label = self._renderLabel(description, name)
-
-        if name: 
-            name = escape(name, True)
-            name = 'name="%s" id="%s"' % (name, name)
-        else:
-            name = ''
-
-        if self.ro_mode: 
-            disabled = "disabled"
-            css_class.append('readonly')
-        else: disabled = ""
-
-        out = '<input type="checkbox" class="%s" %s %s %s %s %s>%s' % (
-            " ".join(css_class), disabled, name, value, checked, SET_MODIFICATION, label)
-
-        self.toTarget(Item(out), bool.getTarget())
-
-    def _renderLabel(self, description, name, help=None, newline=True):
-        error       = self.formdata.getError(name)
-        if help: helpmsg = help
-        else:    helpmsg = ""
-
-        if newline: newline = "<br>"
-        else:       newline = ""
-
-        if description:
-            if name:
-                if error:
-                    return '<label for="%s" class="error_font"><a name="f_%s">%s</a> %s</label>%s\n' % ( 
-                    escape(name, True), escape(name, True), escape(description), helpmsg, newline)
-                else:
-                    return '<label for="%s"><a name="f_%s">%s</a> %s</label>%s\n' % ( 
-                    escape(name, True), escape(name, True), escape(description), helpmsg, newline)
-                    
-            return "%s " % escape(description)
-        return ""
-
-    def _renderTextArea(self, text):
-
-        description = text.getDescription()
-        name        = text.getName()
-        rows        = text.getRows()
-        cols        = text.getCols()
-        
-        help        = self.formdata.getHelp(name)
-        data        = self.formdata.getData(name)
-        error       = self.formdata.getError(name)
-        css_class   = []
-
-        if data: value = escape(data, True)
-        else:    value = ""
-
-        if rows: rows = 'rows="%s"' % escape(rows, True)
-        else:    rows = ""
-
-        if cols: cols = 'cols="%s"' % escape(cols, True)
-        else:    cols = ""
-
-        out = [ self._renderLabel(description, name, help=help) ]
-
-        if name: 
-            name = escape(name, True)
-            name = 'name="%s" id="%s"' % (name, name)
-        else:
-            name = ""
-
-        if error:
-            css_class.append('error')    
-
-        if not self.ro_mode: 
-            out.append('<textarea class="%s" %s %s %s %s>%s</textarea><br>\n' % (
-            " ".join(css_class), name, rows, cols, SET_MODIFICATION, value))
-        else:
-            #disabled = "disabled"
-            #css_class.append('readonly')
-            value = value.replace('\r\n', '<br>')
-            value = value.replace('\n', '<br>')
-            value = value.replace('\r', '<br>')
-            out.append('<p class="readonly">%s</p>' % F.NA(value))
-
-        self.toTarget(Item("".join(out)), text.getTarget())
-
-    def _renderDate(self, date):
-
-        description = date.getDescription()
-        name        = date.getName()
-        help        = self.formdata.getHelp(name)
-
-        data        = self.formdata.getData(name)
-        error       = self.formdata.getError(name)
-        css_class   = []
-
-        if data: value = 'value="%s"' % escape(data, True)
-        else:    value = ""
-        
-        out = [ self._renderLabel(description, name, help=help) ]
-
-        if name:
-            name = escape(name, True)
-            name = 'name="%s" id="%s"' % (name, name)
-        else:
-            name = ""
-
-        if self.ro_mode: 
-            disabled = "readonly"
-            css_class.append('readonly')
-        else: disabled = ""
-
-        if error:
-            css_class.append('error_box')
-
-        out.append( '<input type="text" size="10" maxlength="10" class="%s" %s %s %s %s><br>\n' % (
-            " ".join(css_class), disabled, name, value, SET_MODIFICATION))
-
-        self.toTarget(Item("".join(out)), date.getTarget());
-
-    def _renderText(self, text):
-
-        description = text.getDescription()
-        size        = text.getSize()
-        length      = text.getMaxLength()
-        name        = text.getName()
-        help        = self.formdata.getHelp(name)
-        data        = self.formdata.getData(name)
-        error       = self.formdata.getError(name)
-        css_class   = ['field']
-
-        if data: value = 'value="%s"' % escape(data, True)
-        else:    value = ""
-
-        if size:   size = 'size="%s"' % escape(size, True)
-        else:      size = ""
-
-        if length: length = 'maxlength="%s"' % escape(length, True)
-        else:      length = ""
-
-        if self.ro_mode: 
-            css_class.append('readonly')
-            disabled = "readonly"
-        else: disabled = ""
-
-        out = [ self._renderLabel(description, name, help=help) ]
-
-        if name:
-
-            name = escape(name, True)
-            name = 'name="%s" id="%s"' % (name, name)
-        else:
-            name = ""
-
-        if error:
-            css_class.append('error_box')
-
-        out.append('<input type="text" class="%s" %s %s %s %s %s %s><br>\n' % (
-            " ".join(css_class), disabled, size, length, name, value, SET_MODIFICATION))
-
-        self.toTarget(Item("".join(out)), text.getTarget())
-
-    def _renderIntLeaf(self, integer):
-
-        description = integer.getDescription()
-        name        = integer.getName()
-        minV        = integer.getMinValue()
-        maxV        = integer.getMaxValue()
-        help        = self.formdata.getHelp(name)
-        data        = self.formdata.getData(name)
-        error       = self.formdata.getError(name)
-        css_class   = ['intfield']
-        
-        if data: value = 'value="%s"' % escape(data, True)
-        else:    value = ""
-
-        try:
-            minV, maxV = int(minV), int(maxV)
-            highest = max(abs(maxV), abs(minV))
-            size = digits(highest)
-            if minV < 0 or maxV < 0: size += 1
-            size = 'size="%d" maxlength="%d"' % (size, size)
-        except ValueError:
-            size = ""
-
-        out = [ self._renderLabel(description, name, help=help) ]
-
-        if name:
-            name = escape(name, True)
-            id   = 'id="%s"'   % name
-            name = 'name="%s"' % name
-        else:
-            id, name = "", ""
-
-        if self.ro_mode: 
-            disabled = "readonly"
-            css_class.append('readonly')
-        else: disabled = ""
-
-        if error:
-            css_class.append('error_box')
-            
-        out.append('<input type="text" class="%s" %s %s %s %s %s><br>\n' % (
-            " ".join(css_class), disabled, size, name, value, SET_MODIFICATION))
-
-        self.toTarget(Item("".join(out)), integer.getTarget())
-
-    def _renderPage(self, page):
-        out = self._renderRecursive(page)
-        self.toTarget(Text(out), page.getTarget())
-
-    def _renderBool(self, bool):
-
-        parent = bool.parent
-
-        if isinstance(parent,  data.ChoiceNode):
-            self._renderChoiceBool(bool)
-
-        elif isinstance(parent, data.RadioNode):
-            self._renderRadioBool(bool)
-
-        else:
-            self._renderPlainBool(bool)
-
-    def _renderExternalChoiceList(self, choiceList):
-        children = choiceList.getChildren()
-        if children:
-            for child in children:
-                if isinstance(child, data.BoolLeaf):
-                    self._renderBool(child)
-
-    def _renderChild(self, child):
-
-        if isinstance(child, Node):
-            if isinstance(child, data.GroupNode):
-                self._renderGroup(child)
-            elif isinstance(child, data.ChoiceNode):
-                self._renderChoice(child)
-            elif isinstance(child, data.RadioNode):
-                self._renderRadio(child)
-            elif isinstance(child, data.PageNode):
-                self._renderPage(child)
-            else:
-                self.toTarget(Text(self._renderRecursive(child)), child.getTarget())
-
-        elif isinstance(child, data.InfoLeaf):
-            self._renderInfo(child)
-        elif isinstance(child, data.BoolLeaf):
-            self._renderBool(child)
-        elif isinstance(child, data.TextLeaf):
-            self._renderText(child)
-        elif isinstance(child, data.TextAreaLeaf):
-            self._renderTextArea(child)
-        elif isinstance(child, data.IntLeaf):
-            self._renderIntLeaf(child)
-        elif isinstance(child, data.DateLeaf):
-            self._renderDate(child)
-        elif isinstance(child, data.ExternalChoiceListLeaf):
-            self._renderExternalChoiceList(child)
-        
-    def _renderRecursive(self, node, doBreak=True):
-        containers = node.getContainers()
-
-        containers = containers and [Container(c.strip()) for c in containers.split(",")] or []
-        containers.append(Container(None))
-
-        self.containersStack.append(containers)
-
-        for child in node.children:
-            self._renderChild(child)
-
-        out = []
-        for c in self.containersStack.pop():
-            txt = c.render(doBreak)
-            if txt:
-                out.append(txt)
-                #if doBreak: out.append('<br class="newline">\n')
-        return "".join(out)
-
-
-# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8:

Added: wasko/trunk/waskaweb/lib/renderer.py
===================================================================
--- wasko/trunk/waskaweb/lib/renderer.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/lib/renderer.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -0,0 +1,999 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2007, 2008 Intevation GmbH, Germany, <info at intevation.de>
+# 
+# This file is part of mpuls WASKA (CoMPUter-based case fiLeS - 
+# Web-Anwendungs-Server fuer Kompetenzagenturen).
+# 
+# mpuls WASKA is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+# 
+# mpuls WASKA is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
+# License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public
+# License along with mpuls WASKA. If not, see <http://www.gnu.org/licenses/>.
+# 
+# mpuls WASKA has been developed on behalf of the 
+# Projekttraeger im Deutschen Zentrum fuer Luft- und Raumfahrt e.V. (PT-DLR)
+# within the programme Kompetenzagenturen (Durchfuehrungsphase) funded by
+# the Bundesministerium fuer Familie, Senioren, Frauen und Jugend and 
+# European Social Fund resources.
+#
+# Authors:
+# Sascha L. Teichmann <teichmann at intevation.de>
+#
+
+from waskaweb.model.nodecomponents import Node
+
+import waskaweb.model.data as data
+
+from waskaweb.model.semantic import UNKNOWN_DATE, UNKNOWN_INT
+
+from filters import NA
+
+from cgi import escape
+
+import re
+
+from datetime import date 
+
+import traceback
+
+TARGET = re.compile(r"(\w+)(?:|:(.+))$")
+
+WEIGHTS = frozenset([15, 25, 30, 40, 45, 50, 55, 60, 75])
+
+def is_required(flags):
+    return flags.find("required:") >= 0
+
+def open_in_blank(url, image):
+    return u'<span class="help"> <a href="%s" target="_blank">%s</a></span>' % (url, image)
+
+def required_url(dataID, get_image):
+    return open_in_blank(
+        "/case/required/%s" % escape(dataID),
+        get_image("/images/icons/required_12x12.png", 'border="0" alt="Pflichtfeld"'))
+
+def eval_url(dataID, get_image):
+    return open_in_blank(
+        "/case/showHelpEvaluation/%s" % escape(dataID),
+        get_image("/images/icons/needed_12x12.png", 'border="0" alt="Auswertungsrelevant"'))
+
+def help_url(dataID, get_image):
+    return open_in_blank(
+        "/case/showhelp/%s" % escape(dataID),
+        get_image("/images/icons/form_help.png", 'border="0" alt="Hilfesymbol"'))
+
+class FormData:
+   
+    def __init__(self, get_image, help=None):
+        self.get_image = get_image
+        self.help      = help 
+
+    def getData(self, dataID):
+        return ""
+
+    def getPrintableData(self, dataID):
+        return self.getData(dataID) 
+
+    def getHelp(self, dataID):
+        if not self.help: return ''
+
+        out = []
+        print "Suche in Hilfe nach %s" % dataID
+        if self.help.hasAnnotations(dataID):
+            print "Habe Annotation"
+            out.append(required_url(dataID, self.get_image))
+
+        if self.help.hasAnnotations(dataID, ('evaluation',)):
+            out.append(eval_url(dataID, self.get_image))
+
+        if self.help.hasHelp(dataID):
+            out.append(help_url(dataID, self.get_image))
+
+        return ''.join(out)
+
+    def hasError(self, dataID):
+        return False
+
+    def getError(self, dataID):
+        return ""
+
+    def hasWarning(self, dataID):
+        return False
+
+    def getWarning(self, dataID):
+        return ""
+
+def digits(n):
+    d, c = 10, 1
+    while n >= d: d *= 10; c += 1
+    return c
+
+def visibleDepth(c):
+    depth = -1
+    while c:
+        if not(isinstance(c, data.GroupNode) and not c.isInvisible()):
+            depth += 1
+        c = c.parent
+    return depth
+
+def pretty(x):
+    if isinstance(x, date):
+        if x == UNKNOWN_DATE:
+            return "unbekannt"
+        return "%02d.%02d.%04d" % (x.day, x.month, x.year)
+
+    if isinstance(x, (type(""), type(u""))):
+        return u"%s" % x
+        #return unicode(str(x), 'utf-8')
+
+    if isinstance(x, type(0)):
+        return x == UNKNOWN_INT and "unbekannt" or str(x)
+
+    if isinstance(x, type(False)):
+        return x and "on" or ""
+
+    return str(x)
+
+class FormDataImpl(FormData):
+
+    def __init__(
+        self,
+        file_cache, 
+        help     = None, 
+        page     = None, 
+        errors   = None, 
+        warnings = None,
+        nc       = None):
+        FormData.__init__(self, file_cache.get_image, help)
+        self.page     = page
+        self.errors   = errors
+        self.warnings = warnings
+        if nc:
+            self.nc      = nc #NodeComponent List []
+            self.widgets = self.nc[0].allWidgets()
+        else: 
+            self.nc      = []
+            self.widgets = {} 
+
+    def getData(self, dataID):
+        if not self.errors is None:
+            try:
+                ei = self.errors[dataID]
+                if not ei.bad is None:
+                    return u"%s" % pretty(ei.bad)
+            except KeyError:
+                pass
+        if not self.page is None:
+            value = self.page.getData(dataID)
+            if value is not None: value = pretty(value)
+            else:     value = ""
+            return value
+        return ""
+
+    def getPrintableData(self, dataID):
+        value = self.getData(dataID)
+        try:
+            widget = self.widgets[dataID]
+        except KeyError:
+            return value 
+
+        if not isinstance(widget, data.ChoiceNode):
+            return value
+
+        for c in widget.children:
+            if isinstance(c, data.BoolLeaf):
+                v = c.getValue()
+                if v == value:
+                    return c.getDescription()
+            elif isinstance(c, data.ExternalChoiceListLeaf):
+                cc = c.getChildren()
+                if cc:
+                    for i in cc:
+                        if isinstance(i, data.BoolLeaf):
+                            v = c.getValue()
+                            if v == value:
+                                return c.getDescription()
+
+        return value 
+
+    def hasError(self, dataID):
+        return self.errors and bool(self.errors.get(dataID))
+
+    def getError(self, dataID):
+        if self.errors:
+            try:
+                ei = self.errors[dataID]
+                return u"<br>\n".join([u"%s" % s for s in ei.messages])
+            except KeyError:
+                pass
+        return ""
+
+    def hasWarning(self, dataID):
+        return self.warnings and bool(self.warnings.get(dataID))
+
+    def getWarning(self, dataID):
+        if self.warnings:
+            try:
+                t = self.warnings[dataID]
+                return u"<br>".join(t[1].join())
+            except KeyError:
+                pass
+        return u""
+
+
+class Text:
+    def __init__(self, txt):
+        self.txt = txt
+
+    def render(self, target, weight):
+        return self.txt
+
+class Div(Text):
+    def __init__(self, txt):
+        Text.__init__(self, txt)
+
+    def render(self, target, weight):
+        return '<div class="waska_form_element w100">%s</div>\n' % self.txt
+
+class Item(Text):
+    def __init__(self, txt):
+        Text.__init__(self, txt)
+
+    def render(self, target, weight):
+        weight = int(100.0*weight+0.5)
+        weight = weight in WEIGHTS and " w%d" % weight or " w100"
+        return '<div class="waska_form_element %s">\n%s</div>\n' % (
+            weight, self.txt)
+
+class Container:
+    def __init__(self, target):
+        self.target  = target
+        self.items   = []
+
+    def append(self, item, weight=1.0):
+        self.items.append((item, weight))
+
+    def render(self, doBreak=True):
+        if self.items:
+            out = []
+            f = 1.0/reduce(lambda x, y: x + y[1], self.items, 0)
+            ws = [f*w[1] for w in self.items]
+            target = self.target
+            out = [item[0].render(target, w) for item, w in zip(self.items, ws)]
+            if doBreak: out.append('<br>')
+            return "".join(out)
+        return ""
+
+class ViewRenderer:
+
+    def __init__(self, formdata=None, ro_mode=True):
+        self.formdata        = formdata
+        self.stateStack      = None
+        self.containersStack = None
+        self.ro_mode         = ro_mode
+        self.items           = formdata.nc
+
+    def renderView(self, name="", ds_id=None, showNext=True):
+
+        if not self.items:
+            return "No items given!"
+       
+        out = []
+        if not self.ro_mode:
+            out.append('<form id="waska_form" action="/case/save" accept-charset="UTF-8">\n')
+            # Add submitbutton at the top of the page too
+            out.append('<div class="waska_form_element w100">')
+            out.append('<input type="hidden" name="ds" value="%s"' % ds_id)
+            out.append('<input type="submit" value="&Uuml;bernehmen">\n')
+            if showNext:
+                out.append('<input type="submit" value="&Uuml;bernehmen und weiter">\n')
+            out.append('</div>')
+
+        self.stateStack = []
+        self.containersStack = [[Container(None) ]]
+        for item in self.items:
+            self._renderChild(item)
+
+        out.append(self.containersStack[-1][0].render())
+
+        if not self.ro_mode:
+            if name:
+                out.append('<input type="hidden" name="page" value="%s">\n' \
+                    % escape(name, True))
+            out.append('<div class="waska_form_element w100">')
+            out.append('<input type="submit" value="&Uuml;bernehmen"\n')
+            if showNext:
+                out.append('<input type="submit" value="&Uuml;bernehmen und weiter")>\n')
+            out.append('</div>')
+            out.append('</form>\n')
+
+        return "".join(out)
+
+    def toTarget(self, item, target):
+        weight = 1.0
+        if target:
+            m = TARGET.match(target)
+            if m:
+                target = m.group(1)
+                try:
+                   weight = float(m.group(2))
+                except (TypeError, ValueError):
+                   pass
+
+        containersStack = self.containersStack
+        for container in containersStack[-1]:
+            if not container.target or container.target == target:
+                container.append(item, weight)
+                return
+
+    def _renderGroup(self, node):
+        description = node.getDescription()
+        name        = node.getName()
+
+        help        = self.formdata.getHelp(name)
+        data        = self.formdata.getData(name)
+
+        out = []
+        
+        if node.isInvisible():
+            if description:
+                out.append("<!-- %s -->\n" % escape(description))
+
+            out.append(self._renderRecursive(node))
+        else:
+            out.append("<fieldset>\n")
+            if description:
+                out.append(
+                    "<legend>%s %s</legend>\n" % (escape(description), help))
+            out.append(self._renderRecursive(node))
+            out.append("</fieldset>\n")
+
+        self.toTarget(Item("".join(out)), node.getTarget())
+
+    def _renderMatrix(self, node):
+        description = node.getDescription()
+        name        = node.getName()
+
+        help        = self.formdata.getHelp(name)
+        legend      = []
+
+        out = []
+        
+        error = self.formdata.getData(name)
+        if node.isInvisible():
+            if description:
+                out.append("<!-- %s -->\n" % escape(description))
+
+            out.append(self._renderRecursive(node))
+        else:
+            try:
+                out.append('<table class="formed-radiomatrix">')
+                # write table header take
+                out.append("<tr>")
+                out.append('<th class="formed-radiomatrix-label">%s</th>' % description)
+                for hcol in node.children[0].children:
+                    alternative = hcol.getAlternative()
+                    description = hcol.getDescription()
+                    if alternative == '':
+                        header = description
+                    else:
+                        header = alternative 
+                        legend.append("<strong>%s</strong>:%s" % (alternative, description))
+                    out.append("<th>%s</th>" % header )
+                out.append("</tr>")
+                # write table body
+                fdata = self.formdata
+                for num, row in enumerate(filter(lambda x: isinstance(x, data.RadioNode), node.children)):
+                    css_row_class = num % 2 and 'odd' or 'even'
+
+                    rname = row.getName()
+
+                    if fdata.hasError(rname):
+                        label = u'<span class="error_font">%s</span>' % row.getDescription()
+                    elif fdata.hasWarning(rname):
+                        label = u'<span class="warning_font">%s</span>' % row.getDescription()
+                    else:
+                        label = row.getDescription()
+
+                    out.append(
+                        u'<tr>'
+                        u'<td class="formed-radiomatrix-label %s">'
+                        u'<a name="f_%s">%s</a>%s</td>' % (
+                            css_row_class, 
+                            rname, 
+                            label,
+                            fdata.getHelp(rname)))
+
+                    self.stateStack.append((
+                        self.formdata.getData(row.getName()),
+                        is_required(row.getFlags())))
+
+                    for col in row.children:
+                        out.append('<td class="formed-radiomatrix-data %s">%s</td>' % (
+                            css_row_class, self._renderRadioMatrixBool(col)))
+
+                    self.stateStack.pop()
+                    out.append("</tr>")
+                # write legend
+                if legend:
+                    out.append(
+                        u'<tr><td class="formed-radiomatrix-legend" colspan=%s>'
+                        u'<span class="formed-radiomatrix-legend">%s</span></td></tr>' % (
+                            len(node.children[0].children)+1, ", ".join(legend)))
+                out.append("</table>")
+            except:
+                pass
+                #traceback.print_exc()
+        self.toTarget(Item("".join(out)), node.getTarget())
+
+
+    def _renderInfo(self, info):
+        if info.isInvisible(): return
+        text, depth = info.getValue(), visibleDepth(info)
+        if text:
+            text = "<h%s>%s</h%s>\n" % (depth, escape(text), depth)
+            self.toTarget(Div(text), info.getTarget())
+
+    def _renderRadio(self, radio):
+        description = radio.getDescription()
+        name        = radio.getName()
+
+        help        = self.formdata.getHelp(name)
+        data        = self.formdata.getData(name)
+        #error       = self.formdata.hasError(name)
+
+        label = self._renderLabel(description, name, help=help,)
+
+        out = [ label ]
+
+        self.stateStack.append(data)
+        out.append(self._renderRecursive(radio))
+        self.stateStack.pop()
+
+        self.toTarget(Text("".join(out)), radio.getTarget());
+
+    def _renderChoice(self, choice):
+        description = choice.getDescription()
+        name        = choice.getName()
+        size        = choice.getSize()
+        multiple    = choice.getMultiple()
+        flags       = choice.getFlags()
+
+        help        = self.formdata.getHelp(name)
+        data        = self.formdata.getData(name)
+        error       = self.formdata.hasError(name)
+        warn        = self.formdata.hasWarning(name)
+
+        css_class   = []
+
+        label = self._renderLabel(description, name, help=help)
+
+        if name:
+            name = escape(name, True)
+            name = 'name="%s" id="%s"' % (name, name)
+        else:
+            name = ""
+
+        # Do not render choicelists in disabled mode as IE can not cope with
+        # css on disabled list elements. Instead just render the selected
+        # option within the list (ChoiceBool)
+        disabled = ""
+        #if self.ro_mode: 
+        #    disabled = 'disabled="disabled"'
+        #    css_class.append('readonly')
+        #else: disabled = ""
+
+        multiple = multiple and "multiple" or ""
+
+        if not size: size = "1"
+
+        out = [ label ]
+
+        if is_required(flags):
+            css_class.append('required')
+
+        if  error: css_class.append('error_box')    
+        elif warn: css_class.append('warning_box')    
+
+        out.append(
+            '<select size="%s" class="%s" %s %s %s>\n' % (
+                size, " ".join(css_class), disabled, multiple, name))
+
+        self.stateStack.append(data)
+        out.append(self._renderRecursive(choice, False))
+        self.stateStack.pop()
+
+        out.append("</select>\n")
+
+        self.toTarget(Item("".join(out)), choice.getTarget());
+
+    def _renderChoiceBool(self, bool):
+
+        description = bool.getDescription()
+        name        = bool.getName()
+        value       = bool.getValue()
+        selected    = ""
+        out         = ""
+        
+        toSelect = self.stateStack[-1]
+        selected = toSelect ==  (value or value==0) and "selected" or ""
+
+        if (value or value==0): value = 'value="%s"' % escape(value, True)
+        else:     value = ""
+
+        if description: description = escape(description)
+        else:           description = ""
+
+        if self.ro_mode:
+            if selected:
+                out = "<option %s %s>%s</option>\n" % (value, selected, description)
+        else:
+            out = "<option %s %s>%s</option>\n" % (value, selected, description)
+
+        self.toTarget(Text(out), bool.getTarget());
+
+    def _renderRadioMatrixBool(self, bool):
+        '''Radio bool without any label'''
+
+        name        = bool.getName()
+        pname       = bool.parent.getName()
+        description = bool.getDescription()
+        value       = bool.getValue()
+
+        toCheck, required = self.stateStack[-1]
+
+        checked = toCheck == value and "checked" or ""
+
+        required = required and 'class="required"' or ''
+        
+        if value:
+            value = u'value="%s"' % escape(value, True)
+        else:
+            value = ""
+
+        if   pname: oname = u'name="%s"' % escape(pname, True)
+        elif  name: oname = u'name="%s"' % escape(name, True)
+        else:       oname = u''
+
+        if name: id = u'id="%s"' % escape(name, True)
+        else:    id = ""
+        
+        out = u'<input type="radio" %s %s %s %s %s>\n' % (
+            oname, id, value, required, checked)
+
+        return out
+
+    def _renderRadioBool(self, bool):
+
+        name        = bool.getName()
+        pname       = bool.parent.getName()
+        description = bool.getDescription()
+        value       = bool.getValue()
+        css_class   = []
+
+        toCheck = self.stateStack[-1]
+
+        checked = toCheck == value and "checked" or ""
+        
+        label = self._renderLabel(description, name, newline=False)
+
+        if value:
+            value = 'value="%s"' % escape(value, True)
+        else:
+            value = ""
+
+        if   pname: oname = u'name="%s"' % escape(pname, True)
+        elif  name: oname = u'name="%s"' % escape(name, True)
+        else:       oname = u''
+
+        if name: id = u'id="%s"' % escape(name, True)
+        else:    id = ""
+        
+        if self.ro_mode: 
+            disabled = "disabled"
+            css_class.append(disabled)
+        else: disabled = ""
+
+        out = u'<input type="radio" class="%s" %s %s %s %s %s>%s\n' % (
+            u" ".join(css_class), disabled, oname, id, value, checked, label)
+
+        self.toTarget(Item(out), bool.getTarget())
+
+    def _renderPlainBool(self, bool):
+
+        name        = bool.getName()
+        description = bool.getDescription()
+        css_class   = []
+
+        data = self.formdata.getData(name)
+        if data == "1": 
+            value = 'value="%s"' % escape(data, True)
+            checked = "checked"
+        else:
+            value, checked = "", ""
+
+        label = self._renderLabel(description, name)
+
+        if name: 
+            name = escape(name, True)
+            name = 'name="%s" id="%s"' % (name, name)
+        else:
+            name = ''
+
+        if self.ro_mode: 
+            disabled = "disabled"
+            css_class.append('readonly')
+        else: disabled = ""
+
+        out = '<input type="checkbox" class="%s" %s %s %s %s>%s' % (
+            " ".join(css_class), disabled, name, value, checked, label)
+
+        self.toTarget(Item(out), bool.getTarget())
+
+    def _renderLabel(self, description, name, help=None, newline=True):
+
+        error = self.formdata.hasError(name)
+        warn  = self.formdata.hasWarning(name)
+
+        if help: helpmsg = help
+        else:    helpmsg = ""
+
+        if newline: newline = "<br>"
+        else:       newline = ""
+
+        if description:
+            if name:
+                if error:
+                    return u'<label for="%s" class="error_font"><a name="f_%s">%s</a> %s</label>%s\n' % ( 
+                    escape(name, True), escape(name, True), escape(description), helpmsg, newline)
+
+                if warn:
+                    return u'<label for="%s" class="warning_font"><a name="f_%s">%s</a> %s</label>%s\n' % ( 
+                    escape(name, True), escape(name, True), escape(description), helpmsg, newline)
+
+                return u'<label for="%s"><a name="f_%s">%s</a> %s</label>%s\n' % ( 
+                    escape(name, True), escape(name, True), escape(description), helpmsg, newline)
+                    
+            return u"%s " % escape(description)
+        return ""
+
+    def _renderTextArea(self, text):
+
+        description = text.getDescription()
+        name        = text.getName()
+        rows        = text.getRows()
+        cols        = text.getCols()
+        flags       = text.getFlags()
+        
+        help        = self.formdata.getHelp(name)
+        data        = self.formdata.getData(name)
+        error       = self.formdata.hasError(name)
+        warn        = self.formdata.hasWarning(name)
+
+        css_class   = []
+
+        if data: value = escape(data, True)
+        else:    value = ""
+
+        if rows: rows = 'rows="%s"' % escape(rows, True)
+        else:    rows = ""
+
+        if cols: cols = 'cols="%s"' % escape(cols, True)
+        else:    cols = ""
+
+        out = [ self._renderLabel(description, name, help=help) ]
+
+        if name: 
+            name = escape(name, True)
+            name = 'name="%s" id="%s"' % (name, name)
+        else:
+            name = ""
+
+        if is_required(flags):
+            css_class.append('required')
+
+        if  error: css_class.append('error_box')    
+        elif warn: css_class.append('warning_box')    
+
+        if not self.ro_mode: 
+            out.append('<textarea class="%s" %s %s %s>%s</textarea><br>\n' % (
+            " ".join(css_class), name, rows, cols, value))
+        else:
+            #disabled = "disabled"
+            #css_class.append('readonly')
+            value = value.replace('\r\n', '<br>')
+            value = value.replace('\n', '<br>')
+            value = value.replace('\r', '<br>')
+            out.append('<p class="readonly">%s</p>' % NA(value))
+
+        self.toTarget(Item("".join(out)), text.getTarget())
+
+    def _renderDate(self, date):
+
+        description = date.getDescription()
+        name        = date.getName()
+        flags       = date.getFlags()
+        help        = self.formdata.getHelp(name)
+
+        data        = self.formdata.getData(name)
+        error       = self.formdata.hasError(name)
+        warn        = self.formdata.hasWarning(name)
+        css_class   = []
+
+        if data: value = 'value="%s"' % escape(data, True)
+        else:    value = ""
+        
+        out = [ self._renderLabel(description, name, help=help) ]
+
+        if name:
+            name = escape(name, True)
+            name = 'name="%s" id="%s"' % (name, name)
+        else:
+            name = ""
+
+        if self.ro_mode: 
+            disabled = "readonly"
+            css_class.append('readonly')
+        else: disabled = ""
+
+        if is_required(flags):
+            css_class.append('required')
+
+        if  error: css_class.append('error_box')    
+        elif warn: css_class.append('warning_box')    
+
+        out.append( '<input type="text" size="10" maxlength="10" class="%s" %s %s %s><br>\n' % (
+            " ".join(css_class), disabled, name, value))
+
+        self.toTarget(Item("".join(out)), date.getTarget());
+
+    def _renderText(self, text):
+
+        description = text.getDescription()
+        size        = text.getSize()
+        flags       = text.getFlags()
+        length      = text.getMaxLength()
+        name        = text.getName()
+        help        = self.formdata.getHelp(name)
+        data        = self.formdata.getData(name)
+        error       = self.formdata.hasError(name)
+        warn        = self.formdata.hasWarning(name)
+        css_class   = ['field']
+
+        if data: value = 'value="%s"' % escape(data, True)
+        else:    value = ""
+
+        if size:   size = 'size="%s"' % escape(size, True)
+        else:      size = ""
+
+        if length: length = 'maxlength="%s"' % escape(length, True)
+        else:      length = ""
+
+        if self.ro_mode: 
+            css_class.append('readonly')
+            disabled = "readonly"
+        else: disabled = ""
+
+        out = [ self._renderLabel(description, name, help=help) ]
+
+        if name:
+
+            name = escape(name, True)
+            name = 'name="%s" id="%s"' % (name, name)
+        else:
+            name = ""
+
+        if is_required(flags):
+            css_class.append('required')
+
+        if  error: css_class.append('error_box')    
+        elif warn: css_class.append('warning_box')    
+
+        out.append(u'<input type="text" class="%s" %s %s %s %s %s><br>\n' % (
+            " ".join(css_class), disabled, size, length, name, value))
+
+        self.toTarget(Item("".join(out)), text.getTarget())
+
+    def _renderIntLeaf(self, integer):
+
+        description = integer.getDescription()
+        name        = integer.getName()
+        flags       = integer.getFlags()
+        minV        = integer.getMinValue()
+        maxV        = integer.getMaxValue()
+        help        = self.formdata.getHelp(name)
+        data        = self.formdata.getData(name)
+        error       = self.formdata.hasError(name)
+        warn        = self.formdata.hasWarning(name)
+        css_class   = ['intfield']
+        
+        if data: value = 'value="%s"' % escape(data, True)
+        else:    value = ""
+
+        try:
+            minV, maxV = int(minV), int(maxV)
+            highest = max(abs(maxV), abs(minV))
+            size = digits(highest)
+            if minV < 0 or maxV < 0: size += 1
+            size = 'size="%d" maxlength="%d"' % (size, size)
+        except ValueError:
+            size = ""
+
+        out = [ self._renderLabel(description, name, help=help) ]
+
+        if name:
+            name = escape(name, True)
+            id   = 'id="%s"'   % name
+            name = 'name="%s"' % name
+        else:
+            id, name = "", ""
+
+        if self.ro_mode: 
+            disabled = "readonly"
+            css_class.append('readonly')
+        else: disabled = ""
+
+        if is_required(flags):
+            css_class.append('required')
+
+        if  error: css_class.append('error_box')    
+        elif warn: css_class.append('warning_box')    
+
+        out.append(u'<input type="text" class="%s" %s %s %s %s %s><br>\n' % (
+            " ".join(css_class), disabled, size, name, value, id))
+
+        self.toTarget(Item("".join(out)), integer.getTarget())
+
+    def _renderPage(self, page):
+        out = self._renderRecursive(page)
+        self.toTarget(Text(out), page.getTarget())
+
+    def _renderBool(self, bool):
+
+        parent = bool.parent
+
+        if isinstance(parent,  data.ChoiceNode):
+            self._renderChoiceBool(bool)
+
+        elif isinstance(parent, data.RadioNode):
+            self._renderRadioBool(bool)
+
+        else:
+            self._renderPlainBool(bool)
+
+    def _renderExternalChoiceList(self, choiceList):
+        children = choiceList.getChildren()
+        if children:
+            for child in children:
+                if isinstance(child, data.BoolLeaf):
+                    self._renderBool(child)
+
+    def _renderChild(self, child):
+
+        if isinstance(child, Node):
+            if isinstance(child, data.MatrixNode):
+                self._renderMatrix(child)
+            elif isinstance(child, data.GroupNode):
+                self._renderGroup(child)
+            elif isinstance(child, data.ChoiceNode):
+                self._renderChoice(child)
+            elif isinstance(child, data.RadioNode):
+                self._renderRadio(child)
+            elif isinstance(child, data.PageNode):
+                self._renderPage(child)
+            else:
+                self.toTarget(Text(self._renderRecursive(child)), child.getTarget())
+
+        elif isinstance(child, data.InfoLeaf):
+            self._renderInfo(child)
+        elif isinstance(child, data.BoolLeaf):
+            self._renderBool(child)
+        elif isinstance(child, data.TextLeaf):
+            self._renderText(child)
+        elif isinstance(child, data.TextAreaLeaf):
+            self._renderTextArea(child)
+        elif isinstance(child, data.IntLeaf):
+            self._renderIntLeaf(child)
+        elif isinstance(child, data.DateLeaf):
+            self._renderDate(child)
+        elif isinstance(child, data.ExternalChoiceListLeaf):
+            self._renderExternalChoiceList(child)
+        
+    def _renderRecursive(self, node, doBreak=True):
+        containers = node.getContainers()
+
+        containers = containers and [Container(c.strip()) for c in containers.split(",")] or []
+        containers.append(Container(None))
+
+        self.containersStack.append(containers)
+
+        for child in node.children:
+            self._renderChild(child)
+
+        out = []
+        for c in self.containersStack.pop():
+            txt = c.render(doBreak)
+            if txt:
+                out.append(txt)
+                #if doBreak: out.append('<br class="newline">\n')
+        return "".join(out)
+
+class ErrorRenderer(object):
+    
+    def __init__(self, error_items, warnings, file_cache):
+        self.error_items = error_items
+        self.warnings    = warnings
+        self.get_image   = file_cache.get_image
+
+    def render(self, page):
+
+        page_name = page.getName()
+
+        errors_list = [kv for kv in self.error_items.iteritems() if kv[1].page == page_name]
+        order = dict([(w.getName(), idx) for idx, w in enumerate(page.allWidgets())])
+
+        out = []
+
+        get_image = self.get_image
+        if errors_list:
+            out.append(
+                u'<div class="form_errors">'
+                u'<h1><a name="error_list">Fehlerhafte Formulareingabe</a></h1>'
+                u'<ul>')
+
+            errors_list.sort(lambda a, b: cmp(order[a[0]], order[b[0]]))
+
+            for k, v in errors_list:
+                out.append(u'<li><a href="javascript:undo_error(\'%s\',\'%s\');">' % (k, page_name))
+                out.append(get_image("images/icons/undo.png",
+                    u'border="0" alt="undo" title="Letzten gültigen Wert wieder herstellen."'))
+                out.append(
+                    u'</a> '
+                    u'<a href="#f_%s">%s: %s</a></li>'% (k, v.name, u", ".join(v.messages)))
+
+            out.append(u'</ul></div>')
+
+        if self.warnings:
+            # hash by warning message
+            warns = {}
+            warnings_list = self.warnings.items()
+            warnings_list.sort(lambda a, b: cmp(order[a[0]], order[b[0]]))
+
+            for k, v in warnings_list:
+                msg = u", ".join(v[1])
+                items = warns.setdefault(msg, [])
+                items.append((k, v[0].getDescription()))
+
+            out.append(
+                u'<div class="form_warnings">'
+                u'<h1><a name="warning_list">%s</a></h1>'
+                u'<ul>' % (len(warns) > 1 and u"Hinweise" or u"Hinweis"))
+
+            counter = (len(self.warnings) > 5) and 1 or None
+
+            for k, v in warns.iteritems():
+                out.append(u'<li>')
+                links = []
+                if counter:
+                    for item in v:
+                        links.append(u'<a href="#f_%s" title="%s">%d</a>' % (
+                            item[0], item[1], counter))
+                        counter += 1
+                else:
+                    for item in v:
+                        links.append(u'<a href="#f_%s">%s</a>' % item)
+
+                out.append(u"%s: %s</li>" % (k, u", ".join(links)))
+
+            out.append(u'</ul></div>')
+
+        return u"".join(out)
+
+# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8:

Added: wasko/trunk/waskaweb/model/annotations.py
===================================================================
--- wasko/trunk/waskaweb/model/annotations.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/model/annotations.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+#
+# (c) 2008 by Intevation GmbH
+# This is Free software under the GPLv3. 
+# See LICENSE comming with the source of 'mpuls offline'
+# for details.
+#
+# author: Sascha L. Teichmann <sascha.teichmann at intevation.de>
+#
+
+from xml.dom.minidom import parse
+
+import re
+import os
+
+REQUIRED   = ("required", )
+HELP       = ("help", )
+EVALUATION = ("evaluation", )
+
+ANNOTATION_DIV_ID = re.compile("([a-zA-Z0-9_\-]+):([a-zA-Z0-9_\-]+):([a-zA-Z0-9_\-,]+)")
+
+class Annotation:
+
+    def __init__(self, kind, msg):
+        self.kind = kind
+        self.msg  = msg
+
+    def __str__(self):
+        return repr(self.kind) + ": " + repr(self.msg)
+
+    def __repr__(self):
+        return self.__str__()
+
+def build_annotation(annotations_file):
+
+    annotations = {}
+    dom = None
+    try:
+        dom = parse(annotations_file)
+        for div in dom.getElementsByTagName("div"):
+            id = div.getAttribute("id")
+            if not id: continue
+            m = ANNOTATION_DIV_ID.match(id)
+            if not m: continue
+            id, kind = m.group(1), m.group(2)
+            items = [item for item in m.group(3).split(",") if item]
+            if not items: continue
+            div.setAttribute("id", id)
+
+            msg = unicode(div.toxml())
+
+            for item in items:
+                annons = annotations.setdefault(item, [])
+                annons.append(Annotation(kind, msg))
+    finally:
+        if dom: dom.unlink()
+
+    return annotations
+
+
+class AnnotationsProvider:
+
+    def __init__(self, annotations_file):
+        if os.path.isfile(annotations_file):
+            self.annotations = build_annotation(annotations_file)
+        else:
+            self.annotations = {}
+
+    def hasHelp(self, id):
+        return self.hasAnnotations(id, HELP)
+
+    def getHelp(self, id):
+        return self.getAnnotations(id, HELP)
+        
+    def hasEvaluation(self, id):
+        return self.hasAnnotations(id, EVALUATION)
+
+    def getEvaluation(self, id):
+        return self.getAnnotations(id, EVALUATION)
+        
+    def hasAnnotations(self, id, kinds = REQUIRED):
+        annotations = self.annotations.get(id)
+        if not annotations: return False
+        for annotation in annotations:
+            if annotation.kind in kinds:
+                return True
+        return False
+
+    def getAnnotations(self, id, kinds = REQUIRED):
+        try:
+            x = [a.msg for a in self.annotations[id] if a.kind in kinds]
+        except KeyError:
+            x = []
+        return u''.join(x)
+
+# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

Modified: wasko/trunk/waskaweb/model/datapage.py
===================================================================
--- wasko/trunk/waskaweb/model/datapage.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/model/datapage.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -28,10 +28,9 @@
 # Sascha L. Teichmann <teichmann at intevation.de>
 #
 
-from waskaweb.model.semantic import SematicError, checkAndConvert
+from waskaweb.model.semantic import ErrorItem, SematicError, checkAndConvert
 from waskaweb.model.data     import RuleLeaf
 
-from waskaweb.lib.renderer   import ErrorItem
 from waskaweb.lib.db         import db
 
 import sys

Modified: wasko/trunk/waskaweb/model/semantic.py
===================================================================
--- wasko/trunk/waskaweb/model/semantic.py	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/model/semantic.py	2009-02-16 17:15:01 UTC (rev 262)
@@ -30,16 +30,50 @@
 
 import re
 
-#import waskaweb.model.data as data
-import waskaweb
+import data
 
 from datetime import date
 
 from sys import maxint
 
+from waskaweb.lib.helpers import safe_unicode
+
+
+UNKNOWN_INT  = -9999999
+UNKNOWN_DATE = date(1, 1, 1)
+
+
+class ErrorItem:
+
+    def __init__(self, page=None, bad=None, name=None):
+        self.page     = page
+        self.bad      = bad
+        self.name     = name
+        self.messages = []
+
+    def addMessage(self, msg):
+        self.messages.append(msg)
+
+    def getName(self):
+        return self.name
+
+    def getMessages(self):
+        return self.messages
+
+    def __repr__(self):
+        return self.__str__()
+
+    def __str__(self):
+        r = ["page: %s" % repr(self.page)]
+        r.append("bad: %s" % repr(self.bad))
+        r.append("name: %s" % repr(self.name))
+        r.append("messages: [%s]" % ", ".join([repr(s) for s in self.messages]))
+        return ", ".join(r)
+
 class SematicError(Exception):
 
     def __init__(self, value):
+        Exception.__init__(self)
         self.value = value
 
     def __str__(self):
@@ -65,7 +99,7 @@
             try:
                 value = value[:int(ml)]
                 try:
-                    return unicode(str(value), 'utf-8')
+                    return safe_unicode(unicode(str(value), 'utf-8'))
                 except (TypeError, UnicodeError):
                     return value
             except ValueError:
@@ -79,7 +113,7 @@
 
     def checkAndConvert(self, value, nc):
         try:
-            return unicode(str(value), 'utf-8')
+            return safe_unicode(unicode(str(value), 'utf-8'))
         except (TypeError, UnicodeError):
             pass
         return value
@@ -91,6 +125,10 @@
 
     def checkAndConvert(self, value, nc):
         value = value.strip()
+
+        if value[:2].lower() == u"un":
+            return UNKNOWN_INT
+
         try:
             value = int(float(value))
         except ValueError:
@@ -129,6 +167,7 @@
     DATE_IDX     = (1, 2, 3)
     DATE_ISO     = re.compile(r"([0-9]{2,4})-([0-9]{1,2})-([0-9]{1,2})")
     DATE_ISO_IDX = (3, 2, 1)
+    YEAR         = re.compile(r"([0-9]{1,4})")
 
     def convert(self, value, nc):
         return self.checkAndConvert(value, nc)
@@ -136,6 +175,9 @@
     def checkAndConvert(self, value, nc):
         value = value.strip()
 
+        if value[:2].lower() == u"un":
+            return UNKNOWN_DATE
+
         for reg, idx in ((DateChecker.DATE, DateChecker.DATE_IDX), (DateChecker.DATE_ISO, DateChecker.DATE_ISO_IDX)):
             m = reg.match(value)
             if not m: continue
@@ -152,6 +194,19 @@
             except ValueError:
                 raise SematicError(u"'%s' ist kein gültiges Datum." % value)
 
+        m = DateChecker.YEAR.match(value)
+        if m:
+            year = int(m.group(1))
+
+            if year < 100:
+                if year < 70: year += 2000
+                else:         year += 1900
+
+            try: 
+                return date(year, 1, 1)
+            except ValueError:
+                raise SematicError(u"'%s' ist kein gültiges Datum." % value)
+
         raise SematicError("'%s' ist kein Datum." % value)
 
 class ChoiceChecker(ValueChecker):
@@ -160,18 +215,18 @@
 
         value = value.strip()
         for c in nc.children:
-            if isinstance(c, waskaweb.model.data.BoolLeaf):
+            if isinstance(c, data.BoolLeaf):
                 v = c.getValue()
                 if v == value: 
                     try:
                         return int(v)
                     except ValueError:
                         return v
-            elif isinstance(c, waskaweb.model.data.ExternalChoiceListLeaf):
+            elif isinstance(c, data.ExternalChoiceListLeaf):
                 cc = c.getChildren()
                 if cc:
                     for i in cc:
-                        if isinstance(i, waskaweb.model.data.BoolLeaf):
+                        if isinstance(i, data.BoolLeaf):
                             v = i.getValue()
                             if v == value:
                                 try:
@@ -200,14 +255,14 @@
 
         value = value.strip().lower()
         for c in nc.children:
-            if isinstance(c, waskaweb.model.data.BoolLeaf):
+            if isinstance(c, data.BoolLeaf):
                 v = _checkBool(c, value)
                 if not v is None: return v
-            elif isinstance(c, waskaweb.model.data.ExternalChoiceListLeaf):
+            elif isinstance(c, data.ExternalChoiceListLeaf):
                 cc = c.getChildren()
                 if cc:
                     for i in cc:
-                        if isinstance(i, waskaweb.model.data.BoolLeaf):
+                        if isinstance(i, data.BoolLeaf):
                             v = _checkBool(i, value)
                             if not v is None: return v
 
@@ -219,10 +274,16 @@
     def checkAndConvert(self, value, nc):
         value = value.strip()
         for c in nc.children:
-            if isinstance(c, waskaweb.model.data.BoolLeaf):
+            if isinstance(c, data.BoolLeaf):
                 v = c.getValue()
-                if v == value: return v
-        raise SematicError("'%s' is not a valid value for '%s'" % (
+                if v == value:
+                    try:
+                        return int(v)
+                    except ValueError:
+                        raise SematicError(u"'%s' ist kein valider wert für '%s'" % (
+                            value, nc.getName()))
+
+        raise SematicError(u"'%s' ist kein valider wert für '%s'" % (
             value, nc.getName()))
 
 class RadioConverter(Converter):
@@ -230,7 +291,7 @@
     def convert(self, value, nc):
         value = value.strip()
         for c in nc.children:
-            if isinstance(c, waskaweb.model.data.BoolLeaf):
+            if isinstance(c, data.BoolLeaf):
                 v = c.getDescription().strip().lower()
                 if v == value:
                     return c.getValue()
@@ -251,23 +312,23 @@
         return (value == "ja" or value == '1') and 1 or 0 
 
 TYPES = {
-    waskaweb.model.data.IntLeaf :     IntChecker(),
-    waskaweb.model.data.DateLeaf:     DateChecker(),
-    waskaweb.model.data.TextLeaf:     TextChecker(),
-    waskaweb.model.data.TextAreaLeaf: TextAreaChecker(),
-    waskaweb.model.data.ChoiceNode:   ChoiceChecker(),
-    waskaweb.model.data.BoolLeaf:     BoolChecker(),
-    waskaweb.model.data.RadioNode:    RadioChecker()
+    data.IntLeaf :     IntChecker(),
+    data.DateLeaf:     DateChecker(),
+    data.TextLeaf:     TextChecker(),
+    data.TextAreaLeaf: TextAreaChecker(),
+    data.ChoiceNode:   ChoiceChecker(),
+    data.BoolLeaf:     BoolChecker(),
+    data.RadioNode:    RadioChecker()
 }
 
 CONVERTERS = {
-    waskaweb.model.data.IntLeaf :     IntChecker(),
-    waskaweb.model.data.DateLeaf:     DateChecker(),
-    waskaweb.model.data.TextLeaf:     TextChecker(),
-    waskaweb.model.data.TextAreaLeaf: TextAreaChecker(),
-    waskaweb.model.data.ChoiceNode:   ChoiceConverter(),
-    waskaweb.model.data.BoolLeaf:     BoolConverter(),
-    waskaweb.model.data.RadioNode:    RadioConverter()
+    data.IntLeaf :     IntChecker(),
+    data.DateLeaf:     DateChecker(),
+    data.TextLeaf:     TextChecker(),
+    data.TextAreaLeaf: TextAreaChecker(),
+    data.ChoiceNode:   ChoiceConverter(),
+    data.BoolLeaf:     BoolConverter(),
+    data.RadioNode:    RadioConverter()
 }
 
 def checkAndConvert(nc, value):

Modified: wasko/trunk/waskaweb/templates/main.mako
===================================================================
--- wasko/trunk/waskaweb/templates/main.mako	2009-02-13 15:07:32 UTC (rev 261)
+++ wasko/trunk/waskaweb/templates/main.mako	2009-02-16 17:15:01 UTC (rev 262)
@@ -140,7 +140,7 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
   <head>
-    <title>WASKA</title>
+    <title>WASKO</title>
     <meta name="generator" content="vim">
     <meta name="author" content="Torsten Irlaender">
     <meta name="date" content="2007-08-07">



More information about the Mpuls-commits mailing list