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

export_excel : Exporting to Excel

The lino.modlib.export_excel plugin adds a button Export to Excel to every grid view.

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_book.projects.min3.settings.doctests')
>>> from lino.api.doctest import *

Which means that code snippets in this document are tested using the lino_book.projects.min3 demo project.

>>> settings.SITE.default_ui
'lino.modlib.extjs'

Overview

Robin has twelve appointments in the period 20141023..20141122:

>>> settings.SITE.the_demo_date
datetime.date(2017, 8, 19)
>>> from lino.utils import i2d
>>> ses = rt.login("robin")
>>> pv = dict(start_date=i2d(20170801), end_date=i2d(20170831))
>>> ses.show(cal.MyEntries, param_values=pv, header_level=1)
... 
=============================================================================
My appointments (Responsible user Robin Rood, Dates 01.08.2017 to 31.08.2017)
=============================================================================
================================================= ===========================
 Calendar entry                                    Workflow
------------------------------------------------- ---------------------------
 `Breakfast (01.08.2017 10:20) <…>`__              **☒ Cancelled**
 `Seminar (03.08.2017 08:30) <…>`__                **☒ Cancelled**
 `Interview (05.08.2017 11:10) <…>`__              **☒ Cancelled**
 `Breakfast (07.08.2017 09:40) <…>`__              **☒ Cancelled**
 `Seminar (09.08.2017 13:30) <…>`__                **☒ Cancelled**
 `Interview (11.08.2017 10:20) <…>`__              **☒ Cancelled**
 `Breakfast (13.08.2017 08:30) <…>`__              **☒ Cancelled**
 `Seminar (15.08.2017 11:10) <…>`__                **☒ Cancelled**
 `Interview (17.08.2017 09:40) <…>`__              **☒ Cancelled**
 `Breakfast (19.08.2017 13:30) <…>`__              **☒ Cancelled**
 `Absent for private reasons (20.08.2017) <…>`__   **☐ Draft** → [☼] [☒]
 `Seminar (21.08.2017 10:20) <…>`__                **☐ Draft** → [☼] [☒]
 `Absent for private reasons (23.08.2017) <…>`__   **? Suggested** → [☼] [☒]
 `Interview (23.08.2017 08:30) <…>`__              **? Suggested** → [☼] [☒]
 `Breakfast (25.08.2017 11:10) <…>`__              **☐ Draft** → [☼] [☒]
 `Absent for private reasons (26.08.2017) <…>`__   **☐ Draft** → [☼] [☒]
 `Seminar (27.08.2017 09:40) <…>`__                **? Suggested** → [☼] [☒]
 `Interview (29.08.2017 13:30) <…>`__              **☐ Draft** → [☼] [☒]
 `Breakfast (31.08.2017 10:20) <…>`__              **? Suggested** → [☼] [☒]
================================================= ===========================

Building the file

Let’s export them to .xls.

When exporting to .xls, the URL is rather long because it includes detailed information about the grid columns: their widths (cw), whether they are hidden (ch) and their ordering (ci). This is necessary because we want the resulting .xls sheet to reflect if the client has changed these.

>>> url = "/api/cal/MyEntries?_dc=1414106085710"
>>> url += "&cw=411&cw=287&cw=411&cw=73&cw=274&cw=140&cw=274&cw=220&cw=220&cw=220&cw=287&cw=181&cw=114&cw=181&cw=73&cw=73&cw=274&cw=140&cw=274&cw=274&cw=181&cw=274&cw=140"
>>> url += "&ch=&ch=true&ch="
>>> url += "&ch=true&ch=true&ch=true&ch=true&ch=true&ch=false&ch=true&ch=true&ch=false&ch=false&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true"
>>> url += "&ci=detail_link&ci=summary&ci=workflow_buttons&ci=id&ci=owner_type&ci=owner_id&ci=user&ci=modified&ci=created&ci=build_time&ci=build_method&ci=start_date&ci=start_time&ci=end_date&ci=end_time&ci=auto_type&ci=event_type&ci=transparent&ci=room&ci=priority&ci=state&ci=assigned_to&ci=owner&name=0"
>>> url += "&pv=01.08.2017&pv=31.08.2017&pv=&pv=&pv=2&pv=&pv=&pv=&pv=y"
>>> url += "&an=export_excel"
>>> print(' '.join(cal.MyEntries.params_layout.main.split()))
start_date end_date observed_event state user assigned_to project event_type room show_appointments
>>> test_client.force_login(ses.user)
>>> res = test_client.get(url, REMOTE_USER='robin')
>>> print(res.status_code)
200
>>> result = json.loads(res.content.decode())
>>> len(result)
2
>>> result['success']
True
>>> print(result['open_url'])
/media/cache/appyxlsx/127.0.0.1/cal.MyEntries.xlsx

Testing the generated file

The action performed without error. But does the file exist?

>>> from pathlib import Path
>>> p = Path(settings.MEDIA_ROOT) / 'cache/appyxlsx/127.0.0.1/cal.MyEntries.xlsx'
>>> p.exists()
True

In order to test whether the file is really okay, we load it using openpyxl.

>>> from openpyxl import load_workbook
>>> wb = load_workbook(filename=p)
>>> print(wb.sheetnames[0])
My appointments (Responsible us
>>> ws = wb.active
>>> print(ws.title)
My appointments (Responsible us

Note that long titles are truncated because Excel does not support worksheet names longer than 32 characters.

It has 5 columns and 13 rows:

>>> rows = list(ws.rows)
>>> print("{}, {}".format(len(list(ws.columns)), len(rows)))
5, 19

The first row contains our column headings. Which differ from those of the table above because our user had changed them manually:

>>> print(' | '.join([cell.value for cell in rows[0]]))
Calendar entry | Workflow | Created | Start date | Start time
>>> print(' | '.join([str(cell.value) for cell in rows[1]]))
... 
`Beratung (02.08.2017 13:30) <...>`__ | **☑ Took place** → ... | ... | 2017-08-02 00:00:00 | 13:30:00

Unicode

>>> test_client.force_login(rt.login('romain').user)
>>> res = test_client.get(url, REMOTE_USER='romain')
>>> print(res.status_code)
200
>>> wb = load_workbook(filename=p)
>>> ws = wb.active
>>> print(ws.title)
Mes rendez-vous (Utilisateur re
>>> rows = list(ws.rows)
>>> print(' | '.join([cell.value for cell in rows[0]]))
Entrée calendrier | Workflow | Créé | Date de début | Heure de début
>>> print(' | '.join([str(cell.value) for cell in rows[1]]))
... 
`Beratung (02.08.2017 13:30) <...>`__ | **☑ Terminé** → ... | ... | 2017-08-02 00:00:00 | 13:30:00

More queries

>>> url = "/api/cal/Events?an=export_excel"
>>> test_client.get(url, REMOTE_USER='robin').status_code
200
>>> url = "/api/cal/EntriesByDay?an=export_excel"
>>> test_client.get(url, REMOTE_USER='robin').status_code
200

The following failed with ValueError: Cannot convert 1973-07-21 to Excel until 20161014:

>>> url = "/api/contacts/Persons?an=export_excel"
>>> url += "&cw=123&cw=185&cw=129&cw=64&cw=64&cw=34&cw=64&cw=129&cw=129&cw=123&cw=123&cw=70&cw=123&cw=129&cw=129&cw=129&cw=70&cw=70&cw=129&cw=129&cw=366&cw=129&cw=129&cw=129&cw=129&cw=58&cw=76&cw=185&cw=185&cw=185&cw=185"
>>> url += "&ch=&ch=&ch=&ch=&ch=&ch=&ch=&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=true&ch=false&ch=true&ch=true&ch=true&ch=true&ch=true"
>>> url += "&ci=name_column&ci=address_column&ci=email&ci=phone&ci=gsm&ci=id&ci=language&ci=url&ci=fax&ci=country&ci=city&ci=zip_code&ci=region&ci=addr1&ci=street_prefix&ci=street&ci=street_no&ci=street_box&ci=addr2&ci=name&ci=remarks&ci=title&ci=first_name&ci=middle_name&ci=last_name&ci=gender&ci=birth_date&&ci=age"
>>> url += "&name=0&&pv=&pv=&pv=&pv="
>>> test_client.get(url, REMOTE_USER='robin').status_code
200