Allow fetching data from Decitre given ISBN. #28

This commit is contained in:
Gabriel Augendre 2018-06-15 23:23:59 +02:00
parent f98bc4f14d
commit dc2ae9ea81
7 changed files with 141 additions and 21 deletions

View file

@ -14,6 +14,7 @@ uuid = "*"
django-anymail = {extras = ["mailgun"]} django-anymail = {extras = ["mailgun"]}
whitenoise = "*" whitenoise = "*"
django-import-export = "*" django-import-export = "*"
"beautifulsoup4" = "*"
[dev-packages] [dev-packages]
selenium = "*" selenium = "*"

29
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "84c82ea544617a654420f88f0121bd55a61f6aa48bf9e0da6e74670dad53743c" "sha256": "6a69113216b0e4cdef5dca808dac46d78b2c79edf087766d6608e8abcc721a9e"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -16,6 +16,15 @@
] ]
}, },
"default": { "default": {
"beautifulsoup4": {
"hashes": [
"sha256:11a9a27b7d3bddc6d86f59fb76afb70e921a25ac2d6cc55b40d072bd68435a76",
"sha256:7015e76bf32f1f574636c4288399a6de66ce08fb7b2457f628a8d70c0fbabb11",
"sha256:808b6ac932dccb0a4126558f7dfdcf41710dd44a4ef497a0bb59a77f9f078e89"
],
"index": "pypi",
"version": "==4.6.0"
},
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
@ -99,10 +108,10 @@
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
], ],
"version": "==2.6" "version": "==2.7"
}, },
"jdcal": { "jdcal": {
"hashes": [ "hashes": [
@ -185,10 +194,10 @@
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
"sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
], ],
"version": "==2.18.4" "version": "==2.19.1"
}, },
"six": { "six": {
"hashes": [ "hashes": [
@ -211,10 +220,10 @@
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
"sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
], ],
"version": "==1.22" "version": "==1.23"
}, },
"uuid": { "uuid": {
"hashes": [ "hashes": [

View file

@ -1,7 +1,46 @@
document.addEventListener("DOMContentLoaded", function (event) { document.addEventListener("DOMContentLoaded", function (event) {
console.log('document loaded'); var isbnButton = document.querySelector('#id_isbn_button');
var isbn = document.querySelector('#id_isbn'); var isbn = document.querySelector('#id_isbn');
isbn.addEventListener('change', function (event) { isbnButton.addEventListener('click', function (event) {
$.get("/isbn_api/" + isbn.value, {}, function (data, status, xhr) {
if (data.error) {
isbn.classList.add('is-invalid');
isbn.classList.remove('is-valid');
document.querySelector('#id_isbn_invalid_feedback').style.display = 'block';
document.querySelector('#id_isbn_error_text').textContent = data.error;
return;
}
isbn.classList.remove('is-invalid');
isbn.classList.add('is-valid');
document.querySelector('#id_isbn_invalid_feedback').style.display = 'none';
document.querySelector('#id_title').value = data.title;
document.querySelector('#id_authors').value = data.authors;
document.querySelector('#id_publication_year').value = data.year;
document.querySelector('#id_price').value = data.price;
var editorValue = "";
var editorIsOther = false;
for (var option of document.querySelector('#id_editor').children) {
if (editorValue === "" && option.firstChild.data.toLowerCase().indexOf('autre') !== -1) {
editorValue = option.value;
editorIsOther = true;
}
if (option.firstChild.data.toLowerCase() === data.editor.toLowerCase()) {
editorValue = option.value;
editorIsOther = false;
}
}
document.querySelector('#id_editor').value = editorValue;
event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
event.eventName = "change";
document.querySelector('#id_editor').dispatchEvent(event);
if (editorIsOther) {
document.querySelector('#id_other_editor').value = data.editor
}
});
}); });
}); });

View file

@ -27,7 +27,27 @@
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="col-12"> <div class="col-12">
{% bootstrap_field form.isbn %} <div class="form-group">
{% bootstrap_label content=form.isbn.label label_for=id_isbn %}
<div class="input-group">
<input name="isbn" maxlength="20"
class="form-control"
placeholder="{{ form.isbn.label }}"
required="" id="id_isbn" type="text">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" id="id_isbn_button">
Chercher sur Decitre
</button>
</div>
</div>
<div class="invalid-feedback" id="id_isbn_invalid_feedback">
Erreur lors de la recherche. Veuillez saisir les informations du livre à la main.<br>
Données techniques : <span id="id_isbn_error_text"></span>
</div>
<small class="form-text text-muted">
{{ form.isbn.help_text|safe }}
</small>
</div>
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
@ -55,11 +75,11 @@
</div> </div>
</div> </div>
{% if form.add_another %} {% if form.add_another %}
<div class="form-row"> <div class="form-row">
<div class="col-12"> <div class="col-12">
{% bootstrap_field form.add_another %} {% bootstrap_field form.add_another %}
</div>
</div> </div>
</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -43,8 +43,7 @@
{% block end_js %} {% block end_js %}
{% endblock %} {% endblock %}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" <script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"
integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ"

View file

@ -1,7 +1,7 @@
from django.urls import path from django.urls import path
from manuels.views import AddBookView, ListBooksView, clear_teacher_view, AddSuppliesView, EditBookView, \ from manuels.views import AddBookView, ListBooksView, clear_teacher_view, AddSuppliesView, EditBookView, \
EditSuppliesView, DeleteBookView, DeleteSuppliesView, ConfirmTeacherView EditSuppliesView, DeleteBookView, DeleteSuppliesView, ConfirmTeacherView, isbn_api
urlpatterns = [ urlpatterns = [
path('teacher/<uuid:pk>/add_book', AddBookView.as_view(), name='add_book'), path('teacher/<uuid:pk>/add_book', AddBookView.as_view(), name='add_book'),
@ -13,4 +13,5 @@ urlpatterns = [
path('teacher/<uuid:teacher_pk>/supplies/<int:pk>/delete', DeleteSuppliesView.as_view(), name='delete_supplies'), path('teacher/<uuid:teacher_pk>/supplies/<int:pk>/delete', DeleteSuppliesView.as_view(), name='delete_supplies'),
path('teacher/<uuid:pk>/confirm', ConfirmTeacherView.as_view(), name='confirm_teacher'), path('teacher/<uuid:pk>/confirm', ConfirmTeacherView.as_view(), name='confirm_teacher'),
path('clear', clear_teacher_view, name='clear_teacher'), path('clear', clear_teacher_view, name='clear_teacher'),
path('isbn_api/<str:isbn>', isbn_api, name='isbn_api'),
] ]

View file

@ -1,6 +1,10 @@
import re
import bs4
import requests
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from django.views.generic import CreateView, ListView, UpdateView, DeleteView, FormView, DetailView, TemplateView from django.views.generic import CreateView, ListView, UpdateView, DeleteView, FormView, DetailView, TemplateView
@ -220,3 +224,50 @@ class ConfirmTeacherView(BaseTeacherView, UpdateView):
self.object.send_confirmation(request=self.request) self.object.send_confirmation(request=self.request)
messages.success(self.request, "Vos listes ont été validées. Votre documentaliste a été notifiée par email.") messages.success(self.request, "Vos listes ont été validées. Votre documentaliste a été notifiée par email.")
return response return response
def isbn_api(request, isbn):
res = requests.get(f'https://www.decitre.fr/livres/{isbn}.html')
try:
res.raise_for_status()
except Exception as exc:
return JsonResponse({
'error': str(exc)
})
decitre_soup = bs4.BeautifulSoup(res.text, "html.parser")
title = decitre_soup.select('h1.product-title')
if title:
title = title[0]
title.span.extract()
title = title.getText().strip()
authors = decitre_soup.select('h2.authors')
if authors:
authors = authors[0]
authors = authors.getText().strip()
price = decitre_soup.select('.product-add-to-cart-wrapper div.price span.final-price')
if price:
price = price[0]
price = price.getText().replace('', '').replace(',', '.').strip()
year = None
editor = None
extra_info = decitre_soup.select('ul.extra-infos.hide-on-responsive')
if extra_info:
extra_info = extra_info[0].getText().strip()
matches = re.match('^(?P<editor>.+)\nParu le : \d{2}/\d{2}/(?P<year>\d{4})$', extra_info)
groups = matches.groupdict()
year = groups.get('year')
editor = groups.get('editor')
return JsonResponse({
'title': title,
'authors': authors,
'isbn': isbn,
'price': float(price),
'year': year,
'editor': editor,
})