Households¶
The lino_welfare.modlib.households
plugin extends
lino.modlib.households
by adding some PCSW-specific features.
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.
>>> from lino import startup
>>> startup('lino_welfare.projects.gerd.settings.doctests')
>>> from lino.api.doctest import *
>>> import datetime
Added features¶
- class lino_welfare.modlib.households.RefundsByPerson¶
Shows the members of the primary household of this person together with an amount which depends on whether that member is adult or not.
This is a special form of
lino_xl.lib.households.SiblingsByPerson
, used e.g. inaids/Confirmation/clothes_bank.body.html
.It has two configurable attributes:
- child_tariff¶
The amount to refund for children (household members less than
lino_xl.lib.households.Plugin.adult_age
years old).
- adult_tariff¶
The amount to refund for children (household members who are
lino_xl.lib.households.Plugin.adult_age
years or older).
The values of these attributes are hard-coded and defaul to 10 and 20:
>>> rt.models.households.RefundsByPerson.child_tariff Decimal('10') >>> rt.models.households.RefundsByPerson.adult_tariff Decimal('20')
You might change them locally e.g. in the
lino.core.site.Site.custom_layouts
module.- person_info¶
The full name of the household member.
- amount¶
The amount to pay. This is either
child_tariff
oradult_tarif
depending on the age of the household member.
See the examples below.
- lino_welfare.modlib.households.get_household_summary(person, today=None, adult_age=None)¶
Return a string which expresses the household composition in a few words. See The household summary.
Note that members without birth_date are considered children.
Paul Frisch¶
Mr. Paul Frisch is a fictive client for which the demo database contains fictive family links.
>>> paul = contacts.Person.objects.get(first_name="Paul", last_name="Frisch")
>>> print(paul.id)
240
>>> print(paul)
Herr Paul FRISCH
His primary household consists of 2 adults and 3 children:
>>> print(rt.models.households.get_household_summary(paul))
2 Erwachsene und 3 Kinder
>>> test_client.force_login(rt.login('rolf').user)
>>> def check(uri, fieldname):
... url = '/api/%s?fmt=json&an=detail' % uri
... res = test_client.get(url, REMOTE_USER='rolf')
... assert res.status_code == 200
... d = json.loads(res.content)
... if not fieldname in d['data']:
... raise Exception("20181023 '{}' not in {}".format(
... fieldname, d['data'].keys()))
... return d['data'][fieldname]
>>> uri = 'contacts/Persons/{}'.format(paul.id)
>>> html = check(uri, 'humanlinks.LinksByHuman')
>>> soup = BeautifulSoup(html, 'lxml')
>>> links = soup.find_all('a')
>>> len(links)
23
>>> print(links[1].get('href'))
...
javascript:Lino.contacts.Persons.detail.run(null,{ "base_params": { "mk": 240,
"mt": 8 }, "record_id": 238 })
These are the family relationships of Paul Frisch:
>>> print(soup.get_text(' ', strip=True))
...
Paul ist Sohn von Gaby FROGEMUTH (79 Jahre), Hubert (80 Jahre) Vater von Dennis
(13 Jahre), Clara (14 Jahre), Philippe (17 Jahre), Peter (26 Jahre) Ehemann von
Petra ZWEITH (45 Jahre) Beziehung erstellen als Vater / Sohn Adoptivvater /
Adoptivsohn Pflegevater / Pflegesohn Ehemann Partner Stiefvater / Stiefsohn
Bruder Vetter Onkel / Neffe Verwandter Sonstiger
The previous AJAX call caused Django’s translation machine to switch to German. Switch back to English.
>>> translation.activate('en')
Paul’s father Hubert is married with Gaby and they live together. Their children have moved out.
>>> obj = contacts.Person.objects.get(name="Frisch Hubert")
>>> print(obj)
Mr Hubert FRISCH
>>> ses = rt.login('rolf')
>>> ses.show(households.SiblingsByPerson, master_instance=obj)
========== =================== =============== ==================== ============ =========== ============ ========
Age Role Dependency Person First name Last name Birth date Gender
---------- ------------------- --------------- -------------------- ------------ ----------- ------------ --------
80 years Head of household Not at charge Mr Hubert FRISCH Hubert Frisch 1933-07-21 Male
79 years Partner Not at charge Mrs Gaby FROGEMUTH Gaby Frogemuth 1934-08-04 Female
========== =================== =============== ==================== ============ =========== ============ ========
Paul Frisch is married with Petra Zweith and has a child from divorced marriage with Paula Einzig.
>>> obj = contacts.Person.objects.get(name="Frisch Paul")
>>> print(obj)
Mr Paul FRISCH
>>> ses.show(households.SiblingsByPerson, master_instance=obj)
========== =================== ================ ==================== ============ =========== ============ ========
Age Role Dependency Person First name Last name Birth date Gender
---------- ------------------- ---------------- -------------------- ------------ ----------- ------------ --------
46 years Head of household Not at charge Mr Paul FRISCH Paul Frisch 1967-06-19 Male
45 years Partner Not at charge Mrs Petra ZWEITH Petra Zweith 1968-12-31 Female
17 years Child At full charge Mr Philippe FRISCH Philippe Frisch 1997-01-01 Male
14 years Child At full charge Mrs Clara FRISCH Clara Frisch 1999-06-19 Female
13 years Child At full charge Mr Dennis FRISCH Dennis Frisch 2001-01-03 Male
========== =================== ================ ==================== ============ =========== ============ ========
>>> ses.show(households.RefundsByPerson, master_instance=obj)
========== ======== ==================== ===========
Age Gender Person Amount
---------- -------- -------------------- -----------
46 years Male Paul FRISCH 20,00
45 years Female Petra ZWEITH 20,00
17 years Male Philippe FRISCH 10,00
14 years Female Clara FRISCH 10,00
13 years Male Dennis FRISCH 10,00
**Total (5 rows)** **70,00**
========== ======== ==================== ===========
Ludwig Frisch is married with Laura Loslever and they live together with their two children.
>>> obj = contacts.Person.objects.get(name="Frisch Ludwig")
>>> print(obj)
Mr Ludwig FRISCH
>>> ses.show(households.SiblingsByPerson, master_instance=obj)
========== =================== ================ ==================== ============ =========== ============ ========
Age Role Dependency Person First name Last name Birth date Gender
---------- ------------------- ---------------- -------------------- ------------ ----------- ------------ --------
46 years Partner Not at charge Mrs Laura LOSLEVER Laura Loslever 1968-04-27 Female
45 years Head of household Not at charge Mr Ludwig FRISCH Ludwig Frisch 1968-06-01 Male
12 years Child At full charge Mrs Melba FRISCH Melba Frisch 2002-04-05 Female
6 years Child At full charge Mrs Irma FRISCH Irma Frisch 2008-03-24 Female
========== =================== ================ ==================== ============ =========== ============ ========
Here is what Ludwig’s LinksByHuman panel shows:
>>> ses.show(humanlinks.LinksByHuman, master_instance=obj)
Ludwig is
Son of `Gaby FROGEMUTH <…>`__ (79 years), `Hubert <…>`__ (80 years)
Father of `Irma <…>`__ (6 years), `Melba <…>`__ (12 years)
Husband of `Laura LOSLEVER <…>`__ (46 years)
Create relationship as **Father**/**Son** **Adoptive father**/**Adopted son** **Foster father**/**Foster son** **Husband** **Partner** **Stepfather**/**Stepson** **Brother** **Cousin** **Uncle**/**Nephew** **Relative** **Other**
Here is his RefundsByPerson
table:
>>> ses.show(households.RefundsByPerson, master_instance=obj)
========== ======== ==================== ===========
Age Gender Person Amount
---------- -------- -------------------- -----------
46 years Female Laura LOSLEVER 20,00
45 years Male Ludwig FRISCH 20,00
12 years Female Melba FRISCH 10,00
6 years Female Irma FRISCH 10,00
**Total (4 rows)** **60,00**
========== ======== ==================== ===========
An edge case¶
The following edge case failed before 20170206:
>>> settings.SITE.strict_master_check = True
>>> ses.show(humanlinks.LinksByHuman)
Traceback (most recent call last):
...
django.core.exceptions.BadRequest: Request on slave table humanlinks.LinksByHuman has no master instance (...)
>>> settings.SITE.strict_master_check = False
Melba getting adult¶
The RefundsByPerson
table depends on the current date, which is
2014-05-22 for this demo:
>>> dd.today()
datetime.date(2014, 5, 22)
The demo date is stored in a site attribute the_demo_date
:
>>> settings.SITE.the_demo_date
datetime.date(2014, 5, 22)
Let’s have a look at Ludwig’s RefundsByPerson
table when Melba gets
adult. The default configuration says that you get adult at 18:
>>> dd.plugins.households.adult_age
18
In 2019 Melba is 17, still a child:
>>> settings.SITE.the_demo_date = datetime.date(2019, 5, 22)
>>> ses.show(households.RefundsByPerson, master_instance=obj)
========== ======== ==================== ===========
Age Gender Person Amount
---------- -------- -------------------- -----------
51 years Female Laura LOSLEVER 20,00
50 years Male Ludwig FRISCH 20,00
17 years Female Melba FRISCH 10,00
11 years Female Irma FRISCH 10,00
**Total (4 rows)** **60,00**
========== ======== ==================== ===========
But in 2020 she is 18, now her refunded amount is that of an adult:
>>> settings.SITE.the_demo_date = datetime.date(2020, 5, 22)
>>> ses.show(households.RefundsByPerson, master_instance=obj)
========== ======== ==================== ===========
Age Gender Person Amount
---------- -------- -------------------- -----------
52 years Female Laura LOSLEVER 20,00
51 years Male Ludwig FRISCH 20,00
18 years Female Melba FRISCH 20,00
12 years Female Irma FRISCH 10,00
**Total (4 rows)** **70,00**
========== ======== ==================== ===========
Note: Before 20190326 above table gave an amount of 10,00 for Melba because of
a bug (you got adult only one year after the age specified in
lino_xl.lib.households.Plugin.adult_age
).
For the following tests we set the_demo_date
back to its original value:
>>> settings.SITE.the_demo_date = datetime.date(2014, 5, 22)
Inspecting the MembersByPerson panel¶
The following code caused an exception “ParameterStore of LayoutHandle for ParamsLayout on pcsw.Clients expects a list of 12 values but got 16” on 2014-04-29.
We need a client who is member of multiple households.
>> ses = rt.login(“hubert”) >> cli = ses.spawn(integ.Clients)[1]
>>> cli = pcsw.Client.objects.get(pk=181)
>>> cli
Client #181 ('JEANÉMART Jérôme (181)')
>>> print(len(integ.Clients.params_layout.get_layout_handle()._data_elems))
16
>>> url = '/api/integ/Clients/{}?'.format(cli.pk)
>>> url += 'pv=30&pv=&pv=&pv=&pv=&pv=&pv=&pv=&pv=&pv=&pv=false&pv=&pv=&pv=&pv=false&pv=false'
>>> url += '&an=detail&rp=ext-comp-1351&fmt=json'
>>> res = test_client.get(url, REMOTE_USER='rolf')
>>> print(res.status_code)
200
The response to this request is in JSON:
>>> d = json.loads(res.content)
We test the MembersByPerson panel. It contains a summary:
>>> print(d['data']['households.MembersByPerson'])
...
<div class="htmlText">JEANÉMART Jérôme (181) ist<ul><li>...einen neuen erstellen</a>.</div>
<div>JEANÉMART Jérôme (181) ist<ul><li><a …href=”javascript:Lino.households.Members.set_primary(…)…</div>
Since this is not very human-readable, we are going to analyze it with BeautifulSoup.
>>> soup = BeautifulSoup(d['data']['households.MembersByPerson'], 'lxml')
>>> print(soup.get_text(' ', strip=True))
...
JEANÉMART Jérôme (181) ist
☐ Vorstand in Jérôme & Marie-Louise Jeanémart-Vandenmeulenbos (Sonstige)
☐ Vorstand in Jérôme & Theresia Jeanémart-Thelen (Faktischer Haushalt)
Bestehendem Haushalt beitreten oder einen neuen erstellen .
>>> links = soup.find_all('a')
It contains 6 links:
>>> len(links)
6
The first link is the disabled checkbox for the primary
field:
>>> print(links[0].string)
...
☐
Clicking on this would run the following JavaScript:
>>> print(links[0].get('href'))
javascript:Lino.households.Members.set_primary("ext-comp-1351",false,11,{ })
The next link is the name of the household, and clicking on it would equally execute some JavaScript code:
>>> print(links[1].string)
Jérôme & Marie-Louise Jeanémart-Vandenmeulenbos (Sonstige)
>>> print(links[1].get('href'))
...
javascript:Lino.households.Households.detail.run("ext-comp-1351",{
"base_params": { "mk": 181, "mt": 58 }, "param_values": { "aged_from": null,
"aged_to": null, "end_date": null, "gender": null, "genderHidden": null,
"start_date": null }, "record_id": 237 })
The second last link is:
>>> print(links[-2].string)
Bestehendem Haushalt beitreten
>>> print(links[-2].get('href'))
...
javascript:Lino.households.MembersByPerson.insert.run("ext-comp-1351",{...})
The lino.api.doctest.get_json_soup()
automates this trick:
>>> url = 'integ/Clients/{}'.format(cli.pk)
>>> soup = get_json_soup('rolf', url, 'households.MembersByPerson',
... pv=[30,'','','','','','','','','','false','','','','false','false'])
>>> links = soup.find_all('a')
>>> len(links)
6
The household summary¶
The utility function get_household_summary is used for printing certain aid confirmations. Some examples:
>>> for cli in pcsw.Client.objects.order_by('id'):
... s = households.get_household_summary(cli)
... if not s.endswith("ist in keinem Haushalt"):
... print(u"{} : {}".format(cli, s))
VANDENMEULENBOS Marie-Louise (174) : 2 Erwachsene
LAHM Lisa (176) : 2 Erwachsene
DUBOIS Robin (179) : 2 Erwachsene
DENON Denis (180*) : 1 Erwachsener und 1 Kind
JEANÉMART Jérôme (181) : 2 Erwachsene
KASENNOVA Tatjana (213) : 1 Erwachsener und 1 Kind
FRISCH Paul (240) : 2 Erwachsene und 3 Kinder
BRAUN Bruno (259) : BRAUN Bruno (259) ist in mehreren Haushalten zugleich
Member objects¶
The following code snippet was used to reproduce #844:
>>> translation.activate('en')
>>> Member = households.Member
>>> print(Member())
(Child)
>>> person = contacts.Person.objects.get(pk=259)
>>> print(Member(person=person))
Mr Bruno BRAUN (Child)
>>> print(Member(person=person, role=households.MemberRoles.head))
Mr Bruno BRAUN (Head of household)