20101001 Notizen einfügen

(3.17 Uhr) lino.utils.jsgen.py2js() hatte einen Bug: Datumswerte wurden um einen Monat verschoben. (Denn ich hatte noch nicht mitbekommen, dass der Parameter month bei 0 anfängt, wenn man in Javascript ein Datum mit new Date(year, month, day) instanziert.)

(4.38 Uhr) Tilt! Ich hab endlich verstanden, weshalb die Standardwerte im Insert-Fenster fehlten: Die kann ich natürlich nicht schon beim Generieren der site.js kennen, also müssen sie dynamisch bei jedem Insert-Request ermittelt werden. Konkret: lino.ui.extjs.ext_windows.InsertWrapper macht jetzt record_id=-99999, und lino.ui.extjs.ext_ui.ExtUI.api_element_view() fängt diesen Fall ab und macht dann ReportRequest.create_instance.

Jetzt fehlt noch das Datum, das ja standardmäßig ja auf heute stehen sollte. Das ist im Model, in der Felddefinition von Note.date. Ein auto_now_add=True ist hier nicht das Richtige, weil das erst in Aktion tritt, wenn das Object gespeichert wird. Aber default=datetime.date.today ist unser Kandidat.

Schön. Jetzt sind alle Standardwerte vorbelegt im Insert-Fenster (Ausnahme: der Master bei Insert in einem Slave-Fenster. Siehe später.)

Aber vorher haben wir noch ein neues Problem, wenn ich eine neu erstellte notes.Note speichern will:

ValueError: invalid literal for int() with base 10: 'root'

Das liegt wahrscheinlich daran, dass username zwar unique ist, aber nicht der primary key ist. Das hieße, dass man allgemein einen ForeignKey nach users.User nicht speichern kann. Aber dsbe.Person.user kann man bearbeiten und speichern. Da funktioniert es. (Upps: in Lino.submit_detail war noch ein kleiner Bug.)

Der Unterschied ist: das PUT beim Speichern einer dsbe.Person sendet korrekterweise:

user=staff,userHidden=2

Aber das POST des submit_insert einer notes.Note sendet:

user=staff,userHidden=root

Wie kommt er darauf, userHidden auf root zu setzen?

GET /choices/contacts/Persons/user antwortet korrekt:

{ count: 3, rows: [ { text: “user”, value: 1 }, { text: “staff”, value: 2 }, { text: “root”, value: 3 } ], title: “Choices for user” }

Und auch GET /api/notes/NotesByPerson/-99999 oder GET /api/notes/MyNotes/-99999 antworten korrekt:

{ navinfo: { msg: “Row 0 of 2”, next: null, prev: null, last: 10, first: 1 }, data: { body: “”, language: “Deutsch”, languageHidden: “de”, company: null, typeHidden: null, personHidden: null, companyHidden: null, person: null, url: null, userHidden: 3, user: “root”, date: new Date(2010,9,1), type: null, id: null, subject: null }, id: null, title: “(root 2010-10-01)” }

Aber jetzt ist der Fehler von alleine verschwunden oder zumindest nicht mehr reproduzierbar. Also lassen wir das.

Jetzt fehlt vor allem noch der Master bei Insert in einem Slave-Fenster.

Also ich geh z.B. nach http://127.0.0.1:8000/api/contacts/Persons/79?fmt=detail&tab=4 und klicke aufs Insert der NotesByPerson. Und das Feld Person ist dann nicht ausgefüllt. Und das ist logisch, weil der InsertWrapper scheinbar keine base_params hat, denn er schickt ein GET api/notes/NotesByPerson/-99999 (es fehlt der Master, mk=79). Genau. Deshalb brauchen wir die neue Lino.show_insert_handler().

Fertig! Also die schlimmsten Bugs beim Einfügen sind behoben:

  • Insert in notes.Note : Datum sollte par défaut auf heute stehen, Sprache auf Deutsch.
  • Beim Einfügen in einem Slave wird der Master nicht gesetzt.

Jetzt zuerst ein Check-In und eine Pause.

(15.30 Uhr) Jetzt nehme ich das Problem docs/tickets/5 noch mal in die Mangel (SlaveGrid-Elemente zeigen beim ersten Aufruf “Nix gefunden”).

Es kommt z.B. wenn ich folgende URL aufrufe:

Das Bild wird dann nicht angezeigt, und in der Console steht cmp is rendered but not visible: and now?.

Wie kann ein Ext.Component einerseits rendered sein und andererseits nicht visible? Okay, wenn er z.B. in einem Tab ist, das momentan versteckt ist. Ist hier aber nicht der Fall, und außerdem würde der Component dann aufs show-Event reagieren. Er reagiert auf überhaupt keines der dokumentierten Events.

Ext.Component.isVisible() ist einfach:

isVisible : function(){
    return this.rendered && this.getVisibilityEl().isVisible();
},

getVisibilityEl() ist eine private Funktion:

getVisibilityEl : function(){
    return this.hideParent ? this.container : this.getActionEl();
},

getActionEl : function(){
    return this[this.actionMode];
},

Nee, das bringt alles keine Lösung. Ja, das visibilityEl is offensichtlich noch nicht visible. Aber das ist kein Component mehr und hat also kein render-Event, an das ich mich ran hängen könnte.

Tilt! Idee: ich probiers einfach eine Zehntelsekunde später nochmal:

Lino.do_when_visible = function(cmp,todo) {
  if (cmp.isVisible()) {
    todo();
  } else {
    if (cmp.rendered) {
      Lino.do_when_visible.defer(100,this,[cmp,todo]);
    } else {
      cmp.on('afterrender',todo,cmp,{single:true});
    }
  }
};

Das ist zwar eine Frickelslösung, aber es funktioniert! Hurra!

N.B. Anfangs funktionierte es nur fürs Bild, weil für die Slave Grids noch ein anderes Problem war. Preisfrage: Wo ist der Bug in folgendem Code?

on_master_changed : function() {
  cmp = this;
  var todo = function() {
    var p = cmp.ww.get_master_params();
    for (k in p) cmp.getStore().setBaseParam(k,p[k]);
    cmp.getStore().load();
  };
  Lino.do_when_visible(this,todo);
}

Lösung: die zweite Zeile im obigen Code muss natürlich

var cmp = this;

sein. Ich alter Python-Programmierer habe eine knappe Stunde gebraucht, um das fehlende var zu finden. Ohne das var ist cmp eine globale Variable, und dann ruft er Lino.do_when_visible zwar brav auf jeder Grid, aber wenn die sichtbar wird, wird die todo immer nur auf der letzten Grid (NotesByPerson) aufgerufen.

Ich selber habs jetzt übrigens mit createDelegate statt einem this-Ersatz gemacht, weil ich das eleganter finde:

on_master_changed : function() {
  var todo = function() {
    var p = this.ww.get_master_params();
    for (k in p) this.getStore().setBaseParam(k,p[k]);
    this.getStore().load();
  };
  Lino.do_when_visible(this,todo.createDelegate(this));
}

Fazit: Ich verstehe weiterhin nicht, wie ein Component im beschriebenen Fall rendered und trotzdem nicht visible sein kann, ich kriegs auch nicht reproduziert in einem einfachen showcase, aber jetzt habe ich immerhin eine funktionierende Lösung, die bis auf weiteres vollkommen reicht. Also Problem docs/tickets/5 ist für mich abgeschlossen.

Check-In und Wochenende.

(22 Uhr) Vor dem Schlafengehen noch schnell einen Punkt aus der /todo abgearbeitet:

  • Neue Tabelle “Ansprechpartner pro Person” mit einem Feld “Rolle” oder “Eigenschaft”, dessen Auswahlliste konfigurierbar ist (‘Hauptkontakt’, ‘DSBE’, ‘allgemeiner Sozialdienst’, ‘Schulderberatung’, ‘Energieberatung’). Das Feld Person.user kann dann raus, und PAR->IdUsr muss in diese Tabelle importiert werden. Im Layout2 (“Person”) muss dann user ersetzt werden durch eine Tabelle von Ansprechpartnern. Konkret also vor allem 2 neue Tabellen:
    • CoachType : (id,name)
    • Coach : (user,type,person,company)

Eine Vereinfachung: das Feld Person.user (Hauptansprechpartner) bleibt dennoch drin. Vor allem weil es sonst kompliziert und untransparent wäre, diese eine Zeile bei importierten Personen schreibgeschützt zu machen.

Interessant ist zu bemerken, dass die ganze Aktion nur 45 Minuten gedauert hat, inklusive fixtures und Reorganisierung der Eingabebildschirme (aber ohne Blogschreiben).

Wobei die letzten 15 Minuten eigentlich nicht zählen, denn die habe ich gebraucht um rauszufinden, dass einige Layout-Bugs doch noch dringend behoben werden sollten. Im Moment sind die meisten Bugs sichtbar im Detail von dsbe.Persons. Da kriege ich Montag noch was zu tun:

  • Im Tab “Kontakt” fehlen die flags, die ich noch zwischen remarks und coaching knallen will. Zumindest will ich mal sehen, wie sich das macht. Aber wenn ich das tue, ist die ganze untere hbox nicht mehr da.
  • Im Tab “Kontakt”, box “coaching” nimmt die Grid zu viel Platz ein. Da hat Lino unnötigerweise einen VBorderPanel benutzt. Das ist nicht nötig, wenn nur ein Element vflex ist.
  • Im Tab “Person” nimmt die Grid zu viel Höhe.
  • Im Detail-Tab “Profil 1” nimmt das GridElement anfangs den ganzen Raum ein, die Felder im oberen Teil werden erst nach einem resize sichtbar.
  • Wieso lässt “Studien & Erfahrungen” im Tab “Person” sich höhenverstellen, aber “AG-Sperren” in “Profil 1” nicht?