Formatierungsprobleme

Django, Flask, Bottle, WSGI, CGI…
Antworten
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Ich habe mir eure Hinweise zu Herzen genommen und die Zugriffe auf meine Daten umgestellt und habe jetzt zwei Fragen:
Meine Übersicht sieht jetzt so aus:
Bild
Die Einträge "True" und "False" kommen von meiner Summe über "Abbrechen", "Lösung anzeigen" und "Hilfe anzeigen". "False" wird angezeigt, wenn kein Eintrag gefunden wird, "True" bei einem und ansonsten die Summe. Gibt es eine einfache Möglichkeit, "0" und "1" anzuzeigen oder muss ich das immer mit "if" und "elseif" abfangen?

Code: Alles auswählen

            abbr_gesamt = protokoll.aggregate(Sum('abbr'))['abbr__sum']
            lsg_gesamt = protokoll.aggregate(Sum('lsg'))['lsg__sum']
            hilfe_gesamt = protokoll.aggregate(Sum('hilfe'))['hilfe__sum']
Und dann müsste ich über alle Einträge in "Protokoll" die Gesamtzeit aus den Einträgen "start" und "end" bilden.
@sparrow: Du hast mir das mal so zusammengebastelt (und das tut auch an der Stelle, wofür du es geschrieben hast.I
sparrow hat geschrieben: Sonntag 19. Februar 2023, 21:18 Das Gruppieren und Annotieren kann die Datenbank übernehmen. Und wir verwenden eine F-Expression um die Zeit auszurechnen:

Code: Alles auswählen

from django.db.models import F, Sum

protkoll_statistics = (
    Protokoll.objects
    .values("user__name", "kategorie__name")
    .annotate(richtig_sum=Sum('richtig'))
    .annotate(falsch_sum=Sum('falsch'))
    .annotate(zeit_sum=Sum(F('end') - F('start')))
    .order_by()
) 
Ich habe auch den Link zu den f-expressions geöffnet und versucht zu verstehen - hier klappt es aber nicht:

Code: Alles auswählen

            protokoll = Protokoll.objects.filter(user=user, sj=user.sj, hj=user.hj)
            zeit_gesamt = protokoll.aggregate(Sum(F('end') - F('start')))
wirft den Fehler:

Code: Alles auswählen

Complex aggregates require an alias
- was mache ich damit?
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Hallo,
falls ich dich richtig verstanden habe, könnte ein "yesno" Filter evtl. dein Problem lösen.

Die Fehlermeldung sagt, du sollst einen Alias für die Summe setzen. Evtl. so (kenne mich damit aber eher weniger aus):

Code: Alles auswählen

zeit_gesamt = protokoll.annotate(fulltime=Sum(F('end') - F('start')))
Viele Grüße
Whitie

Edit: Ist das "Sum" da nicht überflüssig?
Zuletzt geändert von Whitie am Sonntag 26. Februar 2023, 21:02, insgesamt 1-mal geändert.
geraldfo
User
Beiträge: 44
Registriert: Samstag 28. Januar 2023, 20:19
Wohnort: Nähe Wien

Die Darstellung von True/False kannst du mit diesem yesno-Dings abändern:
https://docs.djangoproject.com/en/stabl ... ins/#yesno
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

geraldfo hat geschrieben: Sonntag 26. Februar 2023, 21:02 Die Darstellung von True/False kannst du mit diesem yesno-Dings abändern:
https://docs.djangoproject.com/en/stabl ... ins/#yesno
Putzig! Wo zaubert ihr nur immer wieder die Kaninchen her? (Allerdings hatte ich das auch schon mal gelesen - aber wieder aus dem Auge/Sinn verloren).
Das funktioniert aber leider nicht, weill ich die Werte aus einer Liste auslese und dann nur noch Einsen und Nullen auftauchen:

Code: Alles auswählen

                    {% for style, item in zeile %}
                        <td class = "{{style}}">{{item}}</td>
                    {% endfor %}
... da muss ich wohl doch die if/elifs im View setzen.
Whitie hat geschrieben: Sonntag 26. Februar 2023, 21:01 Die Fehlermeldung sagt, du sollst einen Alias für die Summe setzen. Evtl. so (kenne mich damit aber eher weniger aus):

Code: Alles auswählen

zeit_gesamt = protokoll.annotate(fulltime=Sum(F('end') - F('start')))
Edit: Ist das "Sum" da nicht überflüssig?
Also so, wie er dasteht, gibt "zeit_gesamt" ein queryset mehrerer Protokollobjekte zurück und ohne "sum" kann er mit "fulltime" nix anfangen. Wie soll ich das definieren?
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Also wenn deine Nullen und Einsen Zahlen sind (keine Zeichenketten), sollte der yesno-Filter funktionieren. Sonst könntest du dir einen eigenen Filter schreiben.

Mit yesno:

Code: Alles auswählen

{% for style, item in zeile %}
    <td class = "{{ style }}">{{ item|yesno:"0,1" }}</td>
{% endfor %}
Für das Summieren musst du auf einen Experten warten, damit hab ich nur selten gearbeitet. Habe erstmal keine Idee dazu.

Viele Grüße
Whitie
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Ich bestimme ja die Bearbeitungszeit für jede einzelne Kategorie und summiere das jetzt einfach auf.

Und da ich auch Zeichenketten in meiner Liste habe, ändere ich jetzt die False/True doch per if/elif im view:

Code: Alles auswählen

                    abbr_kat = k['abbr']
                    if not abbr_kat:
                        abbr_kat = 0
                    elif abbr_kat == True:
                        abbr_kat = 1
                    lsg_kat = k['lsg'] 
                    if not lsg_kat:
                        lsg_kat = 0
                    elif lsg_kat == True:
                        lsg_kat = 1
                    hilfe_kat = k['hilfe']
                    if not hilfe_kat:
                        hilfe_kat = 0
                    elif hilfe_kat == True:
                        hilfe_kat = 1
 
Ich frage mich, warum das so nicht geht:

Code: Alles auswählen

                     for z in ["abbr_kat", "lsg_kat", "hilfe_kat"]:
                         if not z:
                             z = 0
                         elif z == True:
                             z = 1  
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Na weil keine dieser Zeichenketten leer ist und keine dieser Zeichenketten die ganze Zahl 1 oder der Wahrheitswert `True` ist. Das ist sowieso schräg das `z` mal an eine Zeichenkette und mal an die ganzen Zahlen 0 und 1 gebunden werden soll.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Keine Ahnung, wo Du dieses Code-Fragment wieder eingebaut hast. Aber das Problem liegt daran, dass die "richtig"-Spalte vom Typ Boolean ist und bei 0 bis 1 Summanden das ein Boolean bleibt, und erst bei mehreren Einträgen in ein int konvertiert wird. Deshalb mußt Du den Wert casten:

Code: Alles auswählen

protkoll_statistics = (
    Protokoll.objects
    .values("user__name", "kategorie__name")
	.annotate(
		richtg_as_int=Cast('richtig', output_field=IntegerField()),
		falsch_as_int=Cast('falsch', output_field=IntegerField())
	).annotate(
		richtig_sum=Sum('richtg_as_int'),
		falsch_sum=Sum('falsch_as_int'),
		zeit_sum=Sum(F('end') - F('start'))
	).order_by()
)
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Sirius3 hat geschrieben: Dienstag 28. Februar 2023, 16:07 Keine Ahnung, wo Du dieses Code-Fragment wieder eingebaut hast. Aber das Problem liegt daran, dass die "richtig"-Spalte vom Typ Boolean ist und bei 0 bis 1 Summanden das ein Boolean bleibt, und erst bei mehreren Einträgen in ein int konvertiert wird. Deshalb mußt Du den Wert casten:
Das Code_fragment ist von @sparrows (siehe oben).
Der Rest sieht so aus:

Code: Alles auswählen

        for kategorie in kategorien:
            index =  kategorie.zeile
            protokoll_kategorie = protokoll.filter(kategorie = kategorie.id)
            if protokoll_kategorie.count() > 0:                        # es sind Aufgaben da
                zaehler_kategorie = Zaehler.objects.get(user=user, kategorie = kategorie)
                kategorie_werte = (                                    # die Summen der einzelnen Kategoren des jeweiligen Users
                    protokoll_kategorie
                    .values("kategorie__zeile")
                    .annotate(richtig_sum=Sum('richtig'))
                    .annotate(falsch_sum=Sum('falsch'))
                    .annotate(zeit_sum=Sum(F('end') - F('start')))
                    .annotate(abbr=Sum('abbr'))
                    .annotate(lsg=Sum('lsg'))
                    .annotate(hilfe=Sum('hilfe'))
                    )
                for k in kategorie_werte:
                    zeile = [[],[]] 
                    richtig_kat = k['richtig_sum']
                    falsch_kat = k['falsch_sum']
                    qfarbe = quote_farbe(richtig_kat, falsch_kat)
                    zeit_kat = k['zeit_sum']
                    if richtig_kat+falsch_kat > 0:
                        quote = int(falsch_kat/(richtig_kat+falsch_kat)*100)
                        pro_aufg = round(zeit_kat.total_seconds()/float(richtig_kat+falsch_kat),1)
                    else:
                        quote = "-"
                        pro_aufg = "-"
                    abbr_kat = k['abbr']
                    if not abbr_kat:
                        abbr_kat = 0
                    elif abbr_kat == True:
                        abbr_kat = 1
                    lsg_kat = k['lsg'] 
                    if not lsg_kat:
                        lsg_kat = 0
                    elif lsg_kat == True:
                        lsg_kat = 1
                    hilfe_kat = k['hilfe']
                    if not hilfe_kat:
                        hilfe_kat = 0
                    elif hilfe_kat == True:
                        hilfe_kat = 1
... und liefert, wie du beschreibst 0, 1, oder die entsprechende Summe. Und ich "dachte" mal wieder, anstelle von dreimal if/elif, könnte man das abkürzen. Da oben, mit den drei if/elifs funktioniert es ja auch wie gewünscht. Aber wenn ich dich richtig verstehe, kann ich das im obigen Code schon weiter vorne einbauen (Und mal "casten" nachschlagen).
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Mann, mann, mann! Auf Entfernung gesehen ist das ja zu blöd! Ich denke das liegt am Alter und die Hirnleistung (Demenz?) lässt nach.(Ich habe das "Denken" ja auch in Anführungszeichen gesetzt.) Ist hier noch jemand im Rentenalter?
Klar "for z in ["abbr_kat", "lsg_kat", "hilfe_kat"]:" betrifft nur die Strings. Aber wie wäre es denn mit:

Code: Alles auswählen

                    for z in [abbr_kat, lsg_kat, hilfe_kat]:
                        if not z:
                            z = 0
                        elif z == True:
                            z = 1  
... oder ist das auch blödsinnig? (Funktioniert übrigens auch nicht)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie ich schon geschrieben habe, wandelt man den Datentyp bereits beim Abfragen der Daten um.
Gäbe es diese Möglichkeit nicht, würde man einfach `int` verwenden:

Code: Alles auswählen

abbr_kat = int(k['abbr'])
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Sirius3 hat geschrieben: Dienstag 28. Februar 2023, 22:58 Wie ich schon geschrieben habe, wandelt man den Datentyp bereits beim Abfragen der Daten um.
Gäbe es diese Möglichkeit nicht, würde man einfach `int` verwenden:

Code: Alles auswählen

abbr_kat = int(k['abbr'])
Das funktioniert ja auch wunderbar und würde meine ursprüngliche Fragestellung unkompliziert beantworten.
Wo ist den jetzt der Vorteil von:
Sirius3 hat geschrieben: Dienstag 28. Februar 2023, 16:07 ...Deshalb mußt Du den Wert casten:

Code: Alles auswählen

protkoll_statistics = (
    Protokoll.objects
    .values("user__name", "kategorie__name")
	.annotate(
		richtg_as_int=Cast('richtig', output_field=IntegerField()),
		falsch_as_int=Cast('falsch', output_field=IntegerField())
	).annotate(
		richtig_sum=Sum('richtg_as_int'),
		falsch_sum=Sum('falsch_as_int'),
		zeit_sum=Sum(F('end') - F('start'))
	).order_by()
)
... und dann wäre es prima, wenn du mir den Code noch genauer erklären könntest.
Warum zwei ".values("user__name", "kategorie__name")" und warum zwei annotate Einträge - der zweite scheint ja auf dem ersten aufzubauen?
Mein Code sieht zum Vergleich jetzt so aus:

Code: Alles auswählen

        for kategorie in kategorien:
            index =  kategorie.zeile
            protokoll_kategorie = protokoll.filter(kategorie = kategorie.id)
            if protokoll_kategorie.count() > 0:                        # es sind Aufgaben da
                zaehler_kategorie = Zaehler.objects.get(user=user, kategorie = kategorie)
                kategorie_werte = (                                    # die Summen der einzelnen Kategoren des jeweiligen Users
                    protokoll_kategorie
                    .values("kategorie__zeile")
                    .annotate(richtig_sum=Sum('richtig'))
                    .annotate(falsch_sum=Sum('falsch'))
                    .annotate(zeit_sum=Sum(F('end') - F('start')))
                    .annotate(abbr=Sum('abbr'))
                    .annotate(lsg=Sum('lsg'))
                    .annotate(hilfe=Sum('hilfe'))
                    )
                for k in kategorie_werte:
                    zeile = [[],[]] 
                    richtig_kat = k['richtig_sum']
                    falsch_kat = k['falsch_sum']
                    qfarbe = quote_farbe(richtig_kat, falsch_kat)
                    zeit_kat = k['zeit_sum']
                    abbr_kat = int(k['abbr'])
                    lsg_kat = int(k['lsg']) 
                    hilfe_kat = int(k['hilfe'])
... wobei ich auch noch wirklich verstanden hebe, warum ich die Werte, die "annotate" liefert, mit "for k in kategorie_werte" ausgelesen werden müssen und ich nicht direkt darauf zu greifen kann.
Antworten