Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

households : Handling households and their members

This page assumes you have read The households plugin.

Lines starting with >>> in this document are code snippets that get tested as part of our development workflow.

>>> import lino
>>> lino.startup('lino_book.projects.avanti1.settings')
>>> from lino.api.doctest import *

Households

class lino_xl.lib.households.Household

Django model to represent a household.

type

The type of this household. See household types

create_household(cls, ar, head, partner, type)

Create a household with the given head, partner and type.

A household is a subclass of lino_xl.lib.contacts.Partner.

>>> issubclass(households.Household, contacts.Partner)
True

Household types

There are different types of households.

class lino_xl.lib.households.Type

Django model to represent a household type.

>>> rt.show(households.Types)  
==== ==================== ========================= ======================
 ID   Designation          Designation (de)          Designation (fr)
---- -------------------- ------------------------- ----------------------
 1    Married couple       Ehepaar                   Couple marié
 2    Divorced couple      Geschiedenes Paar         Couple divorcé
 3    Factual household    Faktischer Haushalt       Cohabitation de fait
 4    Legal cohabitation   Legale Wohngemeinschaft   Cohabitation légale
 5    Isolated             Getrennt                  Isolé
 6    Other                Sonstige                  Autre
==== ==================== ========================= ======================

Memberships

class lino_xl.lib.households.Member

Django model to represent a household membership.

household
person
role
dependency
primary

Whether this is the primary household of this person. Checking this field will automatically disable any other primary memberships.

start_date

Since when this membership exists. This is usually empty.

end_date

Until when this membership exists.

class lino_xl.lib.households.Members
class lino_xl.lib.households.MembersByHousehold
class lino_xl.lib.households.PopulateMembers

Populate household members from data in human links.

The default role of a new household member is “Child”. A household member can either refer to an existing person, of e.g. specify only a first name.

>>> m = households.Member(first_name="Tom")
>>> print(m)
Tom (Child)
>>> p = contacts.Person.objects.first()
>>> m = households.Member(person=p)
>>> print(m)
Mr Aábdeen Abad (Child)
>>> print(p)
Mr Aábdeen Abad

Configuration

class lino_xl.lib.households.MemberRoles

The list of allowed choices for the role of a household member.

See role.

>>> rt.show('households.MemberRoles')
======= ============ ===================
 value   name         text
------- ------------ -------------------
 01      head         Head of household
 02      spouse       Spouse
 03      partner      Partner
 04      cohabitant   Cohabitant
 05      child        Child
 06      relative     Relative
 07      adopted      Adopted child
 08      foster       Foster-child
 10      other        Other
======= ============ ===================

How to represent a household member

SiblingsByPerson

The SiblingsByPerson table shows the family composition of a person, i.e. all members of the current household of that person.

>>> SiblingsByPerson = rt.models.households.SiblingsByPerson

This works of course only when Lino can determine the “one and only” current household. If the person has only one membership (at a given date), then there is no question.

When there are several memberships, then ideally one of them should be marked as primary.

But even when a person has multiple household memberships and none of them is primary, Lino can look at the end_date.

The active household is determined as follows:

  • If the person has only one household, use this.

  • Otherwise, if one household is marked as primary, use this.

  • Otherwise, if there is exactly one membership whose end_date is either empty or in the future, take this.

If no active household can be determined, the panel just displays an appropriate message.

Let’s get a list of the candidates to inspect:

>>> Person = rt.models.contacts.Person
>>> Member = rt.models.households.Member
>>> MemberRoles = rt.models.households.MemberRoles
>>> heads = Person.objects.filter(household_members__role=MemberRoles.head).distinct()
>>> for m in heads.order_by('id'):
...     qs = Member.objects.filter(role=MemberRoles.head, person=m.person)
...     all = qs.count()
...     primary = qs.filter(primary=True).count()
...     if all > 1 and not primary:
...         print("{} ({}) is head of {} households".format(
...             m.person, m.person.pk, all))
Mr Aleksándr Alvang (178) is head of 2 households

The most interesting is 178:

>>> ses = rt.login('robin')
>>> p = Person.objects.get(pk=178)
>>> ses.show('households.MembersByPerson', master_instance=p)
Mr Aleksándr Alvang is
`☐  <javascript:window.App.runAction({ "actorId": "households.Members", "an": "set_primary", "onMain": false, "rp": null, "sr": 11, "status": {  } })>`__Head of household in `Aleksándr & Agápiiá Alvang-Bek-Murzin (Other) <…>`__
`☐  <javascript:window.App.runAction({ "actorId": "households.Members", "an": "set_primary", "onMain": false, "rp": null, "sr": 5, "status": {  } })>`__Head of household in `Aleksándr & Cátává Alvang-Maalouf (Factual household) <…>`__

**Join an existing household** or **create a new one**.
>>> ses.show('households.MembersByPerson', p, nosummary=True)
======================================================= =================== ========= ============ ============
 Household                                               Role                Primary   Start date   End date
------------------------------------------------------- ------------------- --------- ------------ ------------
 Aleksándr & Agápiiá Alvang-Bek-Murzin (Other)           Head of household   No
 Aleksándr & Cátává Alvang-Maalouf (Factual household)   Head of household   No                     04/03/2002
======================================================= =================== ========= ============ ============
>>> rt.show(SiblingsByPerson, p)
========== =================== ======================== ============ ============ ======== ============ ============= ========
 Age        Role                Person                   First name   Last name    Gender   Birth date   Nationality   School
---------- ------------------- ------------------------ ------------ ------------ -------- ------------ ------------- --------
 43 years   Partner             Mrs Agápiiá Bek-Murzin   Agápiiá      Bek-Murzin   Female   1973-09-04
 23 years   Head of household   Mr Aleksándr Alvang      Aleksándr    Alvang       Male     1993-09-09
========== =================== ======================== ============ ============ ======== ============ ============= ========

Let’s use the get_json_soup function to analyze

>>> avanti.Client.objects.get(pk=178).user.username
'romain'
>>> soup = get_json_soup('romain', 'avanti/MyClients/178', 'households.MembersByPerson')
>>> links = soup.find_all('a')
>>> len(links)
6
>>> print(links[4].string)
Joindre un ménage existant
>>> print(links[4].get('href'))
... 
javascript:window.App.runAction({ "actorId": "households.MembersByPerson", "an":
"insert", "onMain": false, "rp": null, "status": { "base_params": { "mk": 178,
"mt": ..., "person": 178 }, "data_record": { "data": { "disabled_fields": {
"birth_date": true, "first_name": true, "gender": true, "last_name": true },
"household": null, "householdHidden": null, "person": "ALVANG Aleks\u00e1ndr
(178/romain)", "personHidden": 178, "primary": false, "role": "Enfant",
"roleHidden": "05" }, "phantom": true, "title": "Ins\u00e9rer Membre de m\u00e9nage" }, "param_values": { "aged_from": null, "aged_to": null, "end_date":
null, "gender": null, "genderHidden": null, "start_date": null }, "record_id":
null } })

Don’t read on

The following covers a problem that occurred 20181023 and was detected by welfare but not yet by book.

>>> ses = rt.login('romain')
>>> print(p.id)
178
>>> test_client.force_login(ses.user)
>>> def check(uri, fieldname):
...     url = '/api/%s?fmt=json&an=detail' % uri
...     res = test_client.get(url, REMOTE_USER=ses.user.username)
...     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 = 'avanti/MyClients/{}'.format(p.id)
>>> html = check(uri, 'households.MembersByPerson')
>>> soup = beautiful_soup(html)
>>> links = soup.find_all('a')
>>> len(links)
6
class lino_xl.lib.households.MemberDependencies

The list of allowed choices for the charge of a household member.

Table reference

class lino_xl.lib.households.Households
class lino_xl.lib.households.HouseholdsByType
class lino_xl.lib.households.Types