Uploads in Lino Welfare¶
This document describes the lino_xl.lib.uploads
plugin as used by
Lino Welfare.
Side note: Code snippets (lines starting with >>>
) in this document get
tested as part of our development workflow. The following
initialization snippet tells you which demo project is being used in
this document.
>>> import lino
>>> lino.startup('lino_welfare.projects.gerd.settings.doctests')
>>> from lino.api.doctest import *
Lino Welfare uses the lino_xl.lib.uploads
plugin together with the
lino_xl.lib.coachings
plugin, which changes some aspects.
>>> print(cal.TasksByController.detail_layout.main)
start_date due_date id workflow_buttons
summary
project user delegated
owner created:20 modified:20
description #notes.NotesByTask
Configuring upload types¶
This is the list of upload types:
>>> rt.login('rolf').show(uploads.UploadTypes)
==== ============================ ======== ================ ============= ========================= ====================== ============================
ID Bezeichnung Wanted Upload-Bereich Max. number Ablaufwarnung (Einheit) Ablaufwarnung (Wert) Upload shortcut
---- ---------------------------- -------- ---------------- ------------- ------------------------- ---------------------- ----------------------------
3 Arbeitserlaubnis Ja Allgemein 1 monatlich 2
2 Aufenthaltserlaubnis Ja Allgemein 1 monatlich 2
8 Behindertenausweis Nein Allgemein -1 1
9 Diplom Ja Allgemein -1 1
4 Führerschein Ja Allgemein 1 monatlich 1
5 Identifizierendes Dokument Ja Allgemein 1 monatlich 1 Identifizierendes Dokument
10 Personalausweis Nein Allgemein -1 1
1 Source document Ja Allgemein 1 monatlich 2
6 Vertrag Nein Allgemein -1 1
7 Ärztliche Bescheinigung Nein Allgemein -1 1
**0** **13**
==== ============================ ======== ================ ============= ========================= ====================== ============================
Two clients and their uploads¶
The following newcomer has uploaded 2 identifying documents. One of these is no longer valid, and we know it: needed has been unchecked. The other is still valid but will expire in 3 days.
>>> newcomer = pcsw.Client.objects.get(pk=121)
>>> print(newcomer)
DERICUM Daniel (121)
The UploadsByProject summary shows a summary grouped by upload type.
>>> rt.show(uploads.UploadsByProject, newcomer)
Identifizierendes Dokument: `12 <…>`__, `11 <…>`__ / Diplom: `9 <…>`__ / Personalausweis: `10 <…>`__ / `❏ <javascript:Lino.pcsw.Clients.show_uploads(null,false,121,{ })>`__
>>> rt.show(uploads.UploadsByProject, newcomer, nosummary=True)
============================ ============ ======= =============================== ===================
Upload-Art Gültig bis Nötig Beschreibung Hochgeladen durch
---------------------------- ------------ ------- ------------------------------- -------------------
Identifizierendes Dokument 25.05.14 Ja Identifizierendes Dokument 12 Theresia Thelen
Identifizierendes Dokument 22.04.14 Nein Identifizierendes Dokument 11 Theresia Thelen
Personalausweis 26.06.15 Nein Personalausweis 10 Hubert Huppertz
Diplom 26.06.15 Nein Diplom 9 Hubert Huppertz
============================ ============ ======= =============================== ===================
Here is another beneficiary with three uploads:
>>> oldclient = pcsw.Client.objects.get(pk=124)
>>> print(str(oldclient))
DOBBELSTEIN Dorothée (124)
>>> rt.show(uploads.UploadsByProject, oldclient)
Aufenthaltserlaubnis: `residence_permit.pdf <…>`__ `⇲ </media/uploads/2014/05/residence_permit.pdf>`__ / Arbeitserlaubnis: `work_permit.pdf <…>`__ `⇲ </media/uploads/2014/05/work_permit.pdf>`__ / Führerschein: `driving_license.pdf <…>`__ `⇲ </media/uploads/2014/05/driving_license.pdf>`__ / `❏ <javascript:Lino.pcsw.Clients.show_uploads(null,false,124,{ })>`__
>>> rt.show(uploads.UploadsByProject, oldclient, nosummary=True)
====================== ============ ======= ============================================================================================= ===================
Upload-Art Gültig bis Nötig Beschreibung Hochgeladen durch
---------------------- ------------ ------- --------------------------------------------------------------------------------------------- -------------------
Führerschein 01.06.14 Ja `Führerschein driving_license.pdf </media/uploads/2014/05/driving_license.pdf>`__ Caroline Carnol
Arbeitserlaubnis 30.08.14 Ja `Arbeitserlaubnis work_permit.pdf </media/uploads/2014/05/work_permit.pdf>`__ Alicia Allmanns
Aufenthaltserlaubnis 18.03.15 Ja `Aufenthaltserlaubnis residence_permit.pdf </media/uploads/2014/05/residence_permit.pdf>`__ Theresia Thelen
====================== ============ ======= ============================================================================================= ===================
My uploads¶
Most users can open two tables which show “their” uploads.
>>> print(str(uploads.MyUploads.label))
Meine Upload-Dateien
>>> print(str(uploads.MyExpiringUploads.label))
Meine ablaufenden Upload-Dateien
This is the MyUploads
table for Theresia:
>>> rt.login('theresia').show(uploads.MyUploads)
==== ============================ ============================ ============ ============ ======= ============================================================================================= ======================================
ID Klient Upload-Art Gültig von Gültig bis Nötig Beschreibung Datei
---- ---------------------------- ---------------------------- ------------ ------------ ------- --------------------------------------------------------------------------------------------- --------------------------------------
13 DOBBELSTEIN Dorothée (124) Aufenthaltserlaubnis 18.03.15 Ja `Aufenthaltserlaubnis residence_permit.pdf </media/uploads/2014/05/residence_permit.pdf>`__ uploads/2014/05/residence_permit.pdf
12 DERICUM Daniel (121) Identifizierendes Dokument 25.05.14 Ja Identifizierendes Dokument 12
11 DERICUM Daniel (121) Identifizierendes Dokument 22.04.14 Nein Identifizierendes Dokument 11
==== ============================ ============================ ============ ============ ======= ============================================================================================= ======================================
And the same for Caroline:
>>> rt.login('caroline').show(uploads.MyUploads)
==== ============================ ============== ============ ============ ======= =================================================================================== =====================================
ID Klient Upload-Art Gültig von Gültig bis Nötig Beschreibung Datei
---- ---------------------------- -------------- ------------ ------------ ------- ----------------------------------------------------------------------------------- -------------------------------------
15 DOBBELSTEIN Dorothée (124) Führerschein 01.06.14 Ja `Führerschein driving_license.pdf </media/uploads/2014/05/driving_license.pdf>`__ uploads/2014/05/driving_license.pdf
==== ============================ ============== ============ ============ ======= =================================================================================== =====================================
This is the MyExpiringUploads
table for Hubert:
>>> rt.login('hubert').show(uploads.MyExpiringUploads)
========================= ====================== ======================== =================== ============ ============ =======
Klient Upload-Art Beschreibung Hochgeladen durch Gültig von Gültig bis Nötig
------------------------- ---------------------- ------------------------ ------------------- ------------ ------------ -------
AUSDEMWALD Alfons (116) Source document Source document 1 Hubert Huppertz 17.05.15 Ja
AUSDEMWALD Alfons (116) Aufenthaltserlaubnis Aufenthaltserlaubnis 2 Hubert Huppertz 17.05.15 Ja
========================= ====================== ======================== =================== ============ ============ =======
Theresia does not coach anybody, so the MyExpiringUploads
table is empty for her:
>>> rt.login('theresia').show(uploads.MyExpiringUploads)
Keine Daten anzuzeigen
Shortcut fields¶
>>> id_document = uploads.UploadType.objects.get(shortcut=uploads.Shortcuts.id_document)
>>> rt.show(uploads.UploadsByType, id_document)
=================== ========================= ============================ ======= ============ ============ ======= ===============================
Hochgeladen durch Klient Upload-Art Datei Gültig von Gültig bis Nötig Beschreibung
------------------- ------------------------- ---------------------------- ------- ------------ ------------ ------- -------------------------------
Theresia Thelen DERICUM Daniel (121) Identifizierendes Dokument 25.05.14 Ja Identifizierendes Dokument 12
Theresia Thelen DERICUM Daniel (121) Identifizierendes Dokument 22.04.14 Nein Identifizierendes Dokument 11
Hubert Huppertz COLLARD Charlotte (118) Identifizierendes Dokument 06.06.15 Ja Identifizierendes Dokument 5
=================== ========================= ============================ ======= ============ ============ ======= ===============================
Let’s have a closer look at the id_document shortcut field for some customers.
The response to this AJAX request is in JSON, and we want to inspect the id_document field using BeautifulSoup:
>>> uri = "pcsw/Clients/{0}".format(newcomer.pk)
>>> soup = get_json_soup('romain', uri, 'id_document')
This is an upload shortcut field whose target has more than one row. Which means that it has two buttons.
>>> div = soup.div
>>> len(div.contents)
3
The first button opens a detail window on the last uploaded file:
>>> div.contents[0]
<a href='javascript:Lino.uploads.Uploads.detail.run(null,{ "base_params": { },
"param_values": { ... }, "record_id": 12 })'
style="text-decoration:none">Letzte</a>
The second item is just the comma that separates the two buttons:
>>> div.contents[1]
', '
The second button opens the list of uploads. Here is the full HTML definition of that button:
>>> btn = div.contents[2]
>>> btn
<a href='javascript:Lino.uploads.UploadsByProject.grid.run(null,{ "base_params":
{ "mk": 121, "mt": 58, "type": 5 }, "param_values": {
"coached_by": null, "coached_byHidden": null, "end_date": null,
"observed_event": "Est active", "observed_eventHidden": "20", "start_date":
null, "upload_type": null, "upload_typeHidden": null, "user": null,
"userHidden": null }, "record_id": 12 })' style="text-decoration:none">Alle 2
Dateien</a>
Already its href is quite long:
>>> btn['href']
'javascript:Lino.uploads.UploadsByProject.grid.run(null,{ "base_params": { "mk": 121, "mt": 58, "type": 5 }, "param_values": { "coached_by": null, "coached_byHidden": null, "end_date": null, "observed_event": "Est active", "observed_eventHidden": "20", "start_date": null, "upload_type": null, "upload_typeHidden": null, "user": null, "userHidden": null }, "record_id": 12 })'
This code would call the run()
method of the grid
view of the
uploads.UploadsByProject
table with two positional arguments, the first
being null and the second being a big JavaScript object. Let’s inspect this
second argument of this second button.
>>> arg = btn['href'][55:-1]
>>> print(arg)
{ ... }
>>> d = AttrDict(json.loads(arg))
It has 3 keys:
>>> keys = list(d.keys())
>>> keys.sort()
>>> print(json.dumps(keys))
["base_params", "param_values", "record_id"]
>>> d.record_id
12
>>> d.base_params
{'mk': 121, 'mt': 58, 'type': 5}
>>> pprint(d.param_values)
...
{'coached_by': None,
'coached_byHidden': None,
'end_date': None,
'observed_event': 'Est active',
'observed_eventHidden': '20',
'start_date': None,
'upload_type': None,
'upload_typeHidden': None,
'user': None,
'userHidden': None}
Uploads by client¶
UploadsByProject
shows all the
uploads of a given client, but it has a customized
get_slave_summary
.
The following example is going to use client #121 as master.
>>> obj = oldclient
Here we use lino.api.doctest.get_json_soup()
to inspect what the
summary view of UploadsByProject returns for this client.
>>> soup = get_json_soup('rolf', 'pcsw/Clients/124', 'uploads.UploadsByProject')
>>> print(soup.get_text())
...
Source document: Aufenthaltserlaubnis: residence_permit.pdf ⇲Arbeitserlaubnis: work_permit.pdf ⇲Führerschein: driving_license.pdf ⇲Identifizierendes Dokument: Diplom:
The HTML fragment contains five links:
>>> links = soup.find_all('a')
>>> len(links)
9
The first link would run the insert action on UploadsByProject, with the owner set to this client
>>> btn = links[0]
>>> print(btn.string)
None
>>> print(btn.img['src'])
/static/images/mjames/add.png
>>> print(btn)
...
<a href='javascript:Lino.uploads.UploadsByProject.insert.run(null,{...})'
style="vertical-align:-30%;" title="Neue(n/s) Upload-Datei
erstellen."><img alt="add" src="/static/images/mjames/add.png"/></a>
>>> print(links[2].get('href'))
...
/media/uploads/2014/05/residence_permit.pdf
>>> print(links[3].get('href'))
...
javascript:Lino.uploads.Uploads.detail.run(null,{ ..., "record_id": 14 })
Now let’s inspect the javascript of the first button
>>> dots = btn['href'][57:-1]
>>> print(dots)
{ ... }
>>> d = AttrDict(json.loads(dots))
>>> pprint(d)
...
{'base_params': {'mk': 124, 'mt': 58, 'type_id': 1},
'data_record': {'data': {'description': '',
'disabled_fields': {'camera_stream': True,
'file_size': True,
'mimetype': True},
'end_date': None,
'file': '',
'needed': True,
'project': 'DOBBELSTEIN Dorothée (124)',
'projectHidden': 124,
'start_date': None,
'type': 'Source document',
'typeHidden': 1},
'phantom': True,
'title': 'Upload-Datei erstellen'},
'param_values': {'coached_by': None,
'coached_byHidden': None,
'end_date': None,
'observed_event': 'Ist aktiv',
'observed_eventHidden': '20',
'start_date': None,
'upload_type': None,
'upload_typeHidden': None,
'user': None,
'userHidden': None},
'record_id': None}