Welcome | Get started | Dive into Lino | Contribute | Reference
Clients in Lino Avanti¶
This document describes the lino_avanti.lib.avanti
plugin.
This is a tested document. The following instructions are used for initialization:
>>> import lino
>>> lino.startup('lino_book.projects.avanti1.settings')
>>> from lino.api.doctest import *
Overview¶
A client is a person using our services.
The legacy file number¶
Dossiernummern:
Wenn du "ip6" eintippst, sucht Lino die letzte Dossiernummer, die mit "IP 6" beginnt und zählt +1 hinzu. Also wenn der letzte bestehende Klient "IP 6923" hat, macht Lino aus "ip6" eine "IP 6924".
Du kannst auch im Schnellsuche-Feld "ip 6900" eintippen, um nach Dossiernummer zu suchen.
Wenn du "ip 1234" eintippst (also die Dossiernummer selber als vierstellige Zahl angibst), dann lässt Lino diese Nummer stehen.
Ob du "ip" oder "IP" eintippst, ist egal, Lino macht daraus immer "IP".
Auch das Leerzeichen kannst du beim Eintippen sparen, das setzt Lino automatisch rein.
Wenn die Dossiernummer nicht mit "ip" beginnt, lässt Lino sie unverändert
>>> other = avanti.Client.objects.get(pk=116)
>>> other2 = avanti.Client.objects.get(pk=117)
>>> def update_other(ref, ref2):
... other.ref = ref
... other.full_clean()
... other.save()
... other2.ref = ref2
... other2.full_clean()
... other2.save()
>>> update_other(None, None) # tidy up from previous test run
>>> def test(ref):
... obj = avanti.Client(ref=ref, name="x")
... obj.full_clean()
... print(obj.ref)
>>> test("ip")
IP 0001
>>> test("ip 1")
IP 1001
>>> update_other("IP 4010", "IP 5123")
>>> test("ip 4")
IP 4011
>>> test("ip")
IP 5124
>>> update_other("IP 6999", "IP 7000")
>>> test("ip6")
IP 61000
>>> update_other("IP 60999", "IP 61000")
>>> test("ip6")
IP 61001
Damit Lino die Referenzen automatisch verteilen kann, müssen alle bestehenden Dossiernummern die gleiche Länge haben. Ansonsten kann Lino durcheinander kommen. Zum Beispiel:
>>> update_other("IP 6999", "IP 61000")
>>> test("ip6")
Traceback (most recent call last):
...
django.core.exceptions.ValidationError: {'ref': ['Client with this Legacy file number already exists.']}
>>> update_other(None, None) # tidy up for the following tests
Clients¶
- class lino_avanti.lib.avanti.Client(lino.core.model.Model)¶
- translator_type¶
Which type of translator is needed with this client.
See also
TranslatorTypes
- professional_state¶
The professional situation of this client.
See also
ProfessionalStates
- overview¶
A panel with general information about this client.
- client_state¶
The state of this client record.
This is a pointer to
ClientStates
and can have the following values:>>> rt.show('clients.ClientStates') ======= ========== ============ ============= value name text Button text ------- ---------- ------------ ------------- 05 incoming Incoming 07 informed Informed 10 newcomer Newcomer 15 equal Equal 20 coached Registered 25 inactive Inactive 30 former Ended 40 refused Abandoned ======= ========== ============ =============
- unemployed_since¶
The date when this client got unemployed and stopped to have a regular work.
- seeking_since¶
The date when this client registered as unemployed and started to look for a new job.
- work_permit_suspended_until¶
- city¶
The place (village or municipality) where this client lives.
See
lino_xl.lib.contacts.Partner.city
.
- class lino_avanti.lib.avanti.ClientDetail¶
- class lino_avanti.lib.avanti.Clients¶
Base class for most lists of clients.
- client_state¶
If not empty, show only Clients whose client_state equals the specified value.
- class lino_avanti.lib.avanti.AllClients(Clients)¶
This table is visible for Explorer who can also export it.
This table shows only a very limited set of fields because e.g. an auditor may not see all data for privacy reasons. For example the names are hidden. OTOH it includes the
municipality
virtual field.
>>> show_columns(avanti.AllClients, all=True)
...
- State (client_state) : The state of this client record.
- Starting reason (starting_reason) :
- Ending reason (ending_reason) :
- Locality (city) : The locality, i.e. usually a village, city or town.
- Municipality (municipality) : The municipality where this client lives. This is basically
equal to city, except when city is a village
and has a parent which is a municipality (which causes that
place to be returned).
- Country (country) :
- Zip code (zip_code) :
- Nationality (nationality) : The nationality. This is a pointer to
countries.Country which should
contain also entries for refugee statuses.
- Gender (gender) : The sex of this person (male or female).
- Birth country (birth_country) :
- Lives in Belgium since (in_belgium_since) : Uncomplete dates are allowed, e.g.
"00.00.1980" means "some day in 1980",
"00.07.1980" means "in July 1980"
or "23.07.0000" means "on a 23th of July".
- Needs work permit (needs_work_permit) :
- Translator type (translator_type) : Which type of translator is needed with this client.
- Mother tongues (mother_tongues) :
- None (cef_level_de) :
- None (cef_level_fr) :
- None (cef_level_en) :
- Primary coach (user) : The author of this database object.
- Recurrency policy (event_policy) :
- class lino_avanti.lib.avanti.MyClients(Clients)¶
Shows all clients having me as primary coach. Shows all client states.
>>> rt.login('robin').show('avanti.MyClients') ... ===================================== ============ =============== ======== ================================= ========== ================ ======= ===== ==================== Name State National ID Mobile Address Age e-mail address Phone ID Legacy file number ------------------------------------- ------------ --------------- -------- --------------------------------- ---------- ---------------- ------- ----- -------------------- ABDALLAH Aáish (127/robin) Registered 920417 001-91 Bellmerin, 4700 Eupen 24 years 127 ABDO Aásim (138/robin) Registered 831201 001-50 Gülcherstraße, 4700 Eupen 33 years 138 ABDULLAH Afááf (155/robin) Ended 760102 002-86 4730 Raeren 41 years 155 ABOUD Ahláám (166/robin) Ended 690627 002-97 4730 Raeren 47 years 166 ARENT Afánásiiá (124/robin) Ended 891219 002-23 Bergkapellstraße, 4700 Eupen 27 years 124 ASTAFUROV Agáfiiá (175/robin) Registered 820120 002-60 Aachen, Germany 35 years 175 BARTOSZEWICZ Agáfokliiá (146/robin) Ended 781018 002-02 Herbesthaler Straße, 4700 Eupen 38 years 146 BERENDT Antoshá (165/robin) Ended 700602 001-93 4730 Raeren 46 years 165 CONTEE Chike (131/robin) Registered 870822 001-58 Edelstraße, 4700 Eupen 29 years 131 DIOP Ashánti (142/robin) Registered 810214 002-32 Habsburgerweg, 4700 Eupen 36 years 142 JALLOH Diállo (158/robin) Registered 740810 001-48 4730 Raeren 42 years 158 ===================================== ============ =============== ======== ================================= ========== ================ ======= ===== ====================
- class lino_avanti.lib.avanti.ClientsByNationality(Clients)¶
- class lino_avanti.lib.avanti.Residence(lino.core.model.Model)¶
- class lino_avanti.lib.avanti.EndingReason(lino.core.model.Model)¶
>>> rt.show('avanti.EndingReasons')
==== ======================== ========================== ========================
ID Designation Designation (de) Designation (fr)
---- ------------------------ -------------------------- ------------------------
1 Successfully ended Erfolgreich beendet Successfully ended
2 Health problems Gesundheitsprobleme Health problems
3 Familiar reasons Familiäre Gründe Familiar reasons
4 Missing motivation Fehlende Motivation Missing motivation
5 Return to home country Rückkehr ins Geburtsland Return to home country
9 Other Sonstige Autre
==== ======================== ========================== ========================
- class lino_avanti.lib.avanti.Category(BabelDesignated)¶
>>> rt.show('avanti.Categories')
==== =============================== =============================== ===============================
ID Designation Designation (de) Designation (fr)
---- ------------------------------- ------------------------------- -------------------------------
1 Language course Sprachkurs Language course
2 Integration course Integrationskurs Integration course
3 Language & integration course Language & integration course Language & integration course
4 External course External course External course
5 Justified interruption Begründete Unterbrechung Justified interruption
6 Successfully terminated Erfolgreich beendet Successfully terminated
==== =============================== =============================== ===============================
- class lino_avanti.lib.avanti.TranslatorTypes¶
List of choices for the
Client.translator_type
field of a client.>>> rt.show(rt.models.avanti.TranslatorTypes, language="de") ====== ====== ========== Wert name Text ------ ------ ---------- 10 SETIS 20 Sonstige 30 Privat ====== ====== ==========
- class lino_avanti.lib.avanti.ProfessionalStates¶
List of choices for the
Client.professional_state
field of a client.>>> rt.show(rt.models.avanti.ProfessionalStates, language="de") ... ====== ====== ================================ Wert name Text ------ ------ -------------------------------- 100 Student 200 Arbeitslos 300 Eingeschrieben beim Arbeitsamt 400 Angestellt 500 Selbstständig 600 Pensioniert 700 Arbeitsunfähig ====== ====== ================================
>>> rt.show(checkdata.Checkers, language="en")
...
================================= ========================================
value text
--------------------------------- ----------------------------------------
beid.SSINChecker Check for invalid SSINs
cal.ConflictingEventsChecker Check for conflicting calendar entries
cal.EventGuestChecker Entries without participants
cal.LongEntryChecker Too long-lasting calendar entries
cal.ObsoleteEventTypeChecker Obsolete generated calendar entries
countries.PlaceChecker Check data of geographical places
dupable.DupableChecker Check for missing phonetic words
dupable.SimilarObjectsChecker Check for similar objects
memo.PreviewableChecker Check for previewables needing update
printing.CachedPrintableChecker Check for missing target files
system.BleachChecker Find unbleached html content
uploads.UploadChecker Check metadata of upload files
uploads.UploadsFolderChecker Find orphaned files in uploads folder
================================= ========================================
Career¶
Language knowledges¶
Avanti adds an entry date to the language knowledge table of a client. There can be multiple entries per language and client. Because we want to report whether knowledge changed after attending a course.
Some example cases:
>>> client = rt.models.avanti.Client.objects.get(pk=120)
>>> rt.show('cv.LanguageKnowledgesByPerson', client, nosummary=True)
...
========== =============== ============ ============ =========== ============= ============
Language Mother tongue Spoken Written CEF level Certificate Entry date
---------- --------------- ------------ ------------ ----------- ------------- ------------
Dutch No a bit moderate A2+ No 05/02/2017
Dutch No moderate quite well A2 No 12/01/2016
German No quite well very well A1+ No 12/01/2016
French Yes No 12/01/2016
========== =============== ============ ============ =========== ============= ============
>>> client = rt.models.avanti.Client.objects.get(pk=121)
>>> rt.show('cv.LanguageKnowledgesByPerson', client, nosummary=True)
...
========== =============== ======== ========= =========== ============= ============
Language Mother tongue Spoken Written CEF level Certificate Entry date
---------- --------------- -------- --------- ----------- ------------- ------------
Estonian Yes No 12/01/2016
========== =============== ======== ========= =========== ============= ============
>>> client = rt.models.avanti.Client.objects.get(pk=122)
>>> rt.show('cv.LanguageKnowledgesByPerson', client, nosummary=True, language="de")
...
============= =============== ============== ============== =============== ============ =================
Sprache Muttersprache Wort Schrift CEF-Kategorie Zertifikat Erfassungsdatum
------------- --------------- -------------- -------------- --------------- ------------ -----------------
Deutsch Nein gar nicht ein bisschen A1 Nein 05.02.17
Deutsch Nein ein bisschen mittelmäßig A0 Nein 12.01.16
Französisch Ja Nein 12.01.16
============= =============== ============== ============== =============== ============ =================
The end user usually sees the summary of language knowledges , which shows the
CEF level of the languages defined in lino.core.site.Site.languages
,
and only the most recent CEF level. For above client the CEF level for German
is A1 (not A0):
>>> rt.show('cv.LanguageKnowledgesByPerson', client, language="de")
...
en: Ohne Angabe
de: A1
fr: Ohne Angabe
Muttersprachen: Französisch
Creating a new client¶
>>> ses = rt.login("romain")
>>> url = '/api/avanti/MyClients/-99999?an=insert&fmt=json'
>>> test_client.force_login(ses.user)
>>> res = test_client.get(url)
>>> res.status_code
200
>>> d = AttrDict(json.loads(res.content))
>>> sorted(d.keys())
...
['data', 'phantom', 'title']
>>> d.phantom
True
>>> print(d.title)
Nouveau Bénéficiaire
The dialog window has 6 data fields:
>>> sorted(d.data.keys())
['disabled_fields', 'email', 'first_name', 'gender', 'genderHidden', 'last_name']
>>> fld = avanti.Clients.parameters['observed_event']
>>> rt.show(fld.choicelist, language="en")
No data to display
Miscellaneous¶
Until 20200818 the help_text of the municipality field wasn't set at all, and
the help text of Partner.city talked about a client because it had been
overwritten by the help text of lino_avanti.lib.contacts.Person.city
.
On 20210709 (after moving help texts to separate plugin lino.modlib.help
)
there was another subtle problem here.
Compare (a) the specs (i.e. the target of the links) and (b) the help texts of the following fields:
lino_avanti.lib.contacts.Person.city
lino_avanti.lib.contacts.Person.municipality
The lino_xl.lib.countries.CountryCity.municipality
field is defined on
the lino_xl.lib.countries.CountryCity
model mixin. It is documented on
countries : Countries and cities, which causes the help_text_extractor to extract its
help_text:
The locality, i.e. usually a village, city or town.
This help_text
is inherited by all models that use this model mixin:
Person, Partner, Company, Client. But Client overrides it again to be more
specific.
>>> print(contacts.Person._meta.get_field('municipality').help_text)
The municipality, i.e. either the city or a parent of it.
>>> print(contacts.Person._meta.get_field('city').help_text)
The locality, i.e. usually a village, city or town.
>>> print(contacts.Person._meta.get_field('city').help_text)
The locality, i.e. usually a village, city or town.
>>> print(avanti.Client._meta.get_field('municipality').help_text)
The municipality where this client lives. This is basically
equal to city, except when city is a village
and has a parent which is a municipality (which causes that
place to be returned).
Don't read¶
>>> obj = avanti.Client.objects.get(pk=167)
>>> rt.login("robin").show("changes.ChangesByMaster", obj)
Aucun enregistrement
The following (specifying a wrong mt) caused a server traceback until 20230620
>>> url = "/api/changes/ChangesByMaster?dm=grid&fmt=json&limit=15&mk=167&mt=75&start=0&ul=en&wt=d"
>>> res = test_client.get(url)
>>> d = json.loads(res.content.decode())
>>> print(d['title'])
Changes of MissingRow(Course matching query does not exist. (pk=167))