:date: 2017-10-25 =========================== Wednesday, October 25, 2017 =========================== Separate "coachings" from "clients" =================================== While adapting the test suites after yesterday's changes for :ticket:`2115`, I realized that it is time to split the coachings plugin into "coachings per se" and "clients". And :ref:`avanti` uses only :mod:`clients <lino_xl.lib.clients>`, not :mod:`coachings <lino_xl.lib.coachings>`. This fixes some less visible problems, e.g. the irritating unused menu entries for coachings in :ref:`avanti`. Database migration: The models :class:`ClientContact <lino_xl.lib.clients.ClientContact>` and :class:`ClientContactType <lino_xl.lib.clients.ClientContactType>`, and the choicelist :class:`ClientStates <lino_xl.lib.clients.ClientStates>` were moved from :mod:`lino_xl.lib.coachings` to :mod:`lino_xl.lib.clients`. After these changes I had -as expected- some work to repair the test suites for :ref:`welfare`. For example at some moment there were only 23 instead of 58 clients in :mod:`lino_welfare.projects.eupen`. This was actually because of the *default default* value of the :attr:`client_state` field (see :doc:`1009`). Login via Google+ ================= I continued on :ticket:`1275`. I reached the point where Google asks me to choose my account. I saw this nice RuntimeError: You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to localhost:8000/complete/google-plus/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings. And later, shortly after fixing above error:: AjaxExceptionResponse HTTPError: 403 Client Error: Forbidden for url: https://www.googleapis.com/plus/v1/people/me?access_token=...&alt=json in request POST /complete/google-plus/ (data: <QueryDict: {u'id_token': [u'...) TRACEBACK: File "/site-packages/django/core/handlers/base.py", line 185, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func response = view_func(request, *args, **kwargs) File "/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view return view_func(*args, **kwargs) File "/site-packages/social_django/utils.py", line 50, in wrapper return func(request, backend, *args, **kwargs) File "/site-packages/social_django/views.py", line 32, in complete redirect_name=REDIRECT_FIELD_NAME, *args, **kwargs) File "/site-packages/social_core/actions.py", line 41, in do_complete user = backend.complete(user=user, *args, **kwargs) File "/site-packages/social_core/backends/base.py", line 40, in complete return self.auth_complete(*args, **kwargs) File "/site-packages/social_core/utils.py", line 252, in wrapper return func(*args, **kwargs) File "/site-packages/social_core/backends/google.py", line 144, in auth_complete return self.do_auth(token, response=response, *args, **kwargs) File "/site-packages/social_core/utils.py", line 252, in wrapper return func(*args, **kwargs) File "/site-packages/social_core/backends/oauth.py", line 403, in do_auth data = self.user_data(access_token, *args, **kwargs) File "/site-packages/social_core/backends/google.py", line 59, in user_data 'alt': 'json' File "/site-packages/social_core/backends/base.py", line 233, in get_json return self.create_request(url, *args, **kwargs).json() File "/site-packages/social_core/backends/base.py", line 229, in request response.raise_for_status() File "/site-packages/requests/models.py", line 935, in raise_for_status raise HTTPError(http_error_msg, response=self) [25/Oct/2017 14:09:20] "POST /complete/google-plus/ HTTP/1.1" 400 1321 I didn't yet understand what's happening there... In the debugger I saw that the `X-CSRFToken <https://docs.djangoproject.com/en/5.2/ref/csrf/>`__ was null, maybe that's the problem? But theoretically it is being set by the general `beforerequest` handler defined in :xfile:`linoweb.js` :: Ext.Ajax.on('beforerequest', function (conn, options) { if (!(/^http:.*/.test(options.url) || /^https:.*/.test(options.url))) { if (typeof(options.headers) == "undefined") { options.headers = {'X-CSRFToken': Ext.util.Cookies.get('csrftoken')}; } else { options.headers.extend({'X-CSRFToken': Ext.util.Cookies.get('csrftoken')}); } } }, this); To be continued...