From 2b1510e91f481a3e19781082abeecb8aba768a04 Mon Sep 17 00:00:00 2001 From: basdorea <bastien.doreau@uca.fr> Date: Fri, 14 Jun 2019 06:34:44 +0200 Subject: [PATCH] csv2hal + change name app --- hal/forms.py | 29 +- hal/scripts_bibtex.py | 29 +- hal/scripts_csv.py | 430 +++++++++++++++++++++++++++ hal/templates/base.html | 11 +- hal/templates/hal/aide.html | 35 ++- hal/templates/hal/bibtex2hal.html | 2 +- hal/templates/hal/bibtexxml.html | 2 +- hal/templates/hal/blog.html | 2 +- hal/templates/hal/connexion.html | 2 +- hal/templates/hal/csv2hal.html | 263 ++++++++++++++++ hal/templates/hal/index.html | 8 +- hal/templates/hal/text2hal.html | 2 +- hal/templates/hal/text_format.html | 2 +- hal/templates/hal/textmodif.html | 2 +- hal/templates/hal/textxml.html | 2 +- hal/urls.py | 1 + hal/views.py | 96 +++++- staticfiles/img/aide_upload_xml1.png | Bin 0 -> 4266 bytes 18 files changed, 902 insertions(+), 16 deletions(-) create mode 100644 hal/scripts_csv.py create mode 100644 hal/templates/hal/csv2hal.html create mode 100644 staticfiles/img/aide_upload_xml1.png diff --git a/hal/forms.py b/hal/forms.py index 3c2a54c..364d787 100644 --- a/hal/forms.py +++ b/hal/forms.py @@ -20,7 +20,12 @@ CHOICES_DEPOT= ( ) CHOICES_SOURCE_BIBTEX = ( -('DBLP','DBLP'), +('DBLP','DBLP'), +('ENDNOTE','ENDNOTE'), +) + +CHOICES_SOURCE_CSV = ( +('KN','KeyNote'), ) CHOICES_SOURCE_TEXT = ( @@ -68,6 +73,28 @@ class BibtexXmlForm(forms.Form): bibtex_file = forms.CharField(required=True, label="contenu bibtex", widget=forms.Textarea(attrs={'rows':20, 'cols':90}),) +class Csv2halForm(forms.Form): + form_author = forms.CharField(required=True, max_length=40, label="Forme auteur (*)") + name_user = forms.CharField(required=True, max_length=40, label="Nom chercheur (*)") + firstname_user = forms.CharField(required=True, max_length=40, label="Prénom chercheur (*)") + labo_auth_final = forms.CharField(required=True, max_length=40, label="N° de structure (*) (Ex: 490706)") + id_hal_user = forms.CharField(required=True, max_length=40, label="IdHal chercheur (*)") + login_user = forms.CharField(required=True, max_length=40, label="Login HAL chercheur (*)") + + choice_source = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES_SOURCE_CSV, label="Choix de la source", initial='KN' ) + + login_depot = forms.CharField(required=True, max_length=40, label="Login HAL référent (*)") + passwd_depot = forms.CharField(required=True, max_length=40, label=("Password HAL référent (*)"), widget=forms.PasswordInput()) + choice_depot = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES_DEPOT, label="Choix du dépôt", initial='NODEPOT' ) + + domain1 = forms.CharField(required=True, max_length=20, label="Domaine 1 (*)") + domain2 = forms.CharField(required=False, max_length=20, label="Domaine 2") + domain3 = forms.CharField(required=False, max_length=20, label="Domaine 3") + domain4 = forms.CharField(required=False, max_length=20, label="Domaine 4") + domain5 = forms.CharField(required=False, max_length=20, label="Domaine 5") + + csv_file = forms.CharField(required=True, label="contenu CSV", widget=forms.Textarea(attrs={'rows':20, 'cols':90}),) + class Text2halForm(forms.Form): form_author = forms.CharField(required=True, max_length=40, label="Forme auteur (*)") diff --git a/hal/scripts_bibtex.py b/hal/scripts_bibtex.py index 8d2eb28..9524fb4 100644 --- a/hal/scripts_bibtex.py +++ b/hal/scripts_bibtex.py @@ -176,7 +176,7 @@ def script_bibtex_2_hal (bibtex_file, bib_name_user, name_user, firstname_user, #print ("TEST name_user "+name_user+" - auth "+auth) if name_user in auth : - print ("Script_DBLP Match form_author DBLP") + print ("Script_bibtex Match form_author DBLP") auth = firstname_user+" "+name_user lab_struct = labo_auth_final if auth[-4:] == " and" : @@ -191,6 +191,33 @@ def script_bibtex_2_hal (bibtex_file, bib_name_user, name_user, firstname_user, except KeyError : cnt_error_auth+=1 + if source_bibtex == "ENDNOTE" : + try : + print ("AUTHOR:"+entry['author']) + authors = entry['author'] + + list_authors_mix = authors.split("and") + for auth in list_authors_mix : + lab_struct = "" + print ("TEST bib_name_user "+bib_name_user+" - auth "+auth) + + if bib_name_user in auth : + print ("Script_bibtex Match form_author EndNote") + auth = name_user + ", " + firstname_user + lab_struct = labo_auth_final + + auth_full = auth.split(",") + nom = auth_full[0] + nom = nom.encode('utf-8') + nom = nom.strip() + prenom = auth_full[-1] + prenom = prenom.encode('utf-8') + prenom = prenom.strip() + + #print ("Script_dblp "+"author "+auth.encode('utf-8')+ " - prenom "+ prenom.encode('utf-8')+ " - nom "+nom.encode('utf-8')+ str(type(auth.encode('utf-8')))) + listauthors.append((nom,prenom,lab_struct)) + except KeyError : + cnt_error_auth+=1 # if source_bibtex == "..." : # Get journal for ARTICLES diff --git a/hal/scripts_csv.py b/hal/scripts_csv.py new file mode 100644 index 0000000..acd61b2 --- /dev/null +++ b/hal/scripts_csv.py @@ -0,0 +1,430 @@ +#!/usr/bin/python +#-*- coding: utf-8 -*- + +from __future__ import unicode_literals +import requests +import csv + +# lib XML +from lxml import etree + +# lib csv + + + +from create_xml2hal import createXml_sendHal, create_single_xml + +################# +## VARIABLES +################# + + +# Fichier CSV en sortie +file_publis_csv = "all_csv.csv" + + +######################################################################################################################################## +######################################################################################################################################## +########## SCRIPT PRINCIPAL +######################################################################################################################################## +######################################################################################################################################## + + +def script_csv_2_hal (csv_file, bib_name_user, name_user, firstname_user, labo_auth_final, id_hal_user, login_user, listdomains, login_depot, passwd_depot, bool_depot_preprod, bool_depot_prod, single, source_csv): + ''' + take bibtex file and some values in entry and parse the bibtex to get info upon publications + + if the bool 'single' == False -> bibtex file has many publications, call createXml_sendHal (create XML and deposit) return a lis of results + if the bool 'single' == True -> bibtex file has one publication, call create_single_xml (post single XML no deposit) return the XML in string + ''' + print ("script_csv_2_hal bool_depot_prod ",bool_depot_prod) + resultat = "" + problemes_url = "" + problemes_doublon = "" + problemes_depot = "" + depots = "" + reponse_single_xml = "" + + ################################### + ###### COMPTEURS + ################################### + + cnt_article = 0 + cnt_inproceeding = 0 + cnt_proceeding = 0 + cnt_incollection = 0 + cnt_book = 0 + cnt_total = 0 + cnt_phdthesis = 0 + + # errors bibtex + cnt_error_auth = 0 + cnt_error_title = 0 + cnt_error_jrn = 0 + cnt_error_vol = 0 + cnt_error_numb = 0 + cnt_error_pages = 0 + cnt_error_year_art = 0 + cnt_error_doi = 0 + cnt_error_booktitle = 0 + cnt_error_pages = 0 + cnt_error_year_inp = 0 + cnt_error_crossref = 0 + cnt_error_publisher = 0 + cnt_error_editor = 0 + cnt_error_idcrossref = 0 + cnt_error_publisher_p = 0 + cnt_error_editor_p = 0 + + list_pub_csv = [] + + # une participation a une conf (inproceeding) peut avoir une crossref reliee a une proceeding qui est une conf, celle ci + # se trouve en fin du doc bibtex donc -> + # on cherche crossref dans inproceeding, si on le trouve aller le chercher en bas, sinon chercher editor et publier directement dans inproceeding + # car certains chercheurs peuvent creer leur bibtex comme ceci + + #bibtex_file = bibtex_file.encode("utf-8") + print (csv_file) + + parser = BibTexParser() + parser.ignore_nonstandard_types = False + parser.homogenize_fields = False + parser.common_strings = False + parser.customization = convert_to_unicode + bib_database = bibtexparser.loads(bibtex_file, parser = parser) + ''' + parser = BibTexParser() + parser.customization = convert_to_unicode + bib_database = bibtexparser.load(bibtex_file, parser=parser) + ''' + + # list_acronym_country -> nom du pays en anglais majuscule , acronyme du pays + list_acronym_country = [] + with open('hal/countries.csv', 'rb') as csvfile: + delim = str(':') + quotech = str('|') + list_countries = csv.reader(csvfile, delimiter=delim, quotechar=quotech) + for row in list_countries: + list_acronym_country.append((row[1],row[0])) + + + for entry in bib_database.entries : + # initialize entries + type_publi ="" + numero = "" + language = "en" + title = "" + conf="" + nb_pages="" + volume = "" + town="" + country="" + country_acr="XX" + year = 0 + doi_value = "" + publisher_book = "" + editor_book = "" + + # Recup type publi, numero, language + if entry['ENTRYTYPE']=='article' : + cnt_article +=1 + type_publi = "ART" + numero = "RI"+str(cnt_article) + language = "en" + + if entry['ENTRYTYPE']=='inproceedings' : + cnt_inproceeding +=1 + type_publi = "COMM" + numero = "CI"+str(cnt_inproceeding) + language = "en" + + if entry['ENTRYTYPE']=='book' : + cnt_book +=1 + type_publi = "OUV" + numero = "O"+str(cnt_book) + language = "en" + + # Recup title and format + title = entry['title'] + title = title.replace("\n"," ") + title = title.replace("\\emph","") + + # get authors according to source_bibtex + listauthors = [] + if source_bibtex == "DBLP" : + try : + #print ("AUTHOR:"+entry['author']) + authors = entry['author'] + list_authors_mix = authors.split("\n") + for auth in list_authors_mix : + lab_struct = "" + #print ("TEST name_user "+name_user+" - auth "+auth) + + if name_user in auth : + print ("Script_DBLP Match form_author DBLP") + auth = firstname_user+" "+name_user + lab_struct = labo_auth_final + if auth[-4:] == " and" : + auth = auth[:-4] + auth_full = auth.split(" ") + prenom = auth_full[0] + prenom = prenom.encode('utf-8') + nom = auth_full[-1] + nom = nom.encode('utf-8') + #print ("Script_dblp "+"author "+auth.encode('utf-8')+ " - prenom "+ prenom.encode('utf-8')+ " - nom "+nom.encode('utf-8')+ str(type(auth.encode('utf-8')))) + listauthors.append((nom,prenom,lab_struct)) + except KeyError : + cnt_error_auth+=1 + + # if source_bibtex == "..." : + + # Get journal for ARTICLES + if type_publi == "ART" : + try : + conf = entry['journal'] + conf = conf.replace("\n"," ") + except KeyError : + cnt_error_jrn+=1 + + # Get conf for COMM, split entry with ',' , first occurence is conf name + # then search country from CSV in other occurences and if found, get previous occurence to get town (except USA previuous previous occurence) + if type_publi == "COMM" : + try : + booktitle = entry['booktitle'] + conf_all = booktitle.split(",") + conf = conf_all[0] + conf = conf.replace("\n"," ") + conf = conf.replace("\\(^\\mboxth\\)","th") + conf = conf.replace("\\(^\\mboxe\\)","e") + prev_conf_elmt = "" + prev_prev_conf_elmt = "" + for conf_elmt in conf_all : + conf_elmt = conf_elmt.strip() + for csv_country in list_acronym_country : + if conf_elmt.upper() == csv_country[0] : + if csv_country[0] == "USA" : + prev_prev_conf_elmt = prev_prev_conf_elmt.replace("\n"," ") + town = prev_prev_conf_elmt + country_acr = csv_country[1] + country = csv_country[0] + + else : + prev_conf_elmt = prev_conf_elmt.replace("\n"," ") + town = prev_conf_elmt + country_acr = csv_country[1] + country = csv_country[0] + + prev_prev_conf_elmt = prev_conf_elmt + prev_conf_elmt = conf_elmt + + except KeyError : + cnt_error_booktitle+=1 + + # get volume + try : + volume = entry['volume'] + except KeyError : + cnt_error_vol+=1 + + # get nb_pages + try : + nb_pages = entry['pages'] + except KeyError : + cnt_error_pages+=1 + + # get Year + try : + year = entry['year'] + except KeyError : + cnt_error_year_art+=1 + + # get DOI + try : + doi_value = entry['doi'] + except KeyError : + cnt_error_doi+=1 + + + if (type_publi == "COMM") or (type_publi == "BOOK") : + # get Publisher + try : + publisher_book = entry['publisher'] + except KeyError : + cnt_error_publisher+=1 + + # get Editor + try : + editor_book = entry['editor'] + except KeyError : + cnt_error_editor+=1 + + + # Test value "single" + # if false -> call createXml_sendHal (create XML and deposit) + # if true -> call create_single_xml (build XML without creation, no deposit) + if (type_publi == "ART") or (type_publi == "COMM") or (type_publi == "OUV") : + if single == False : + + action_todo="" + + # Verification que la publi n'existe pas deja dans HAL + # pour un meilleur matching, titre en minuscule et recherche par le champ title_t (sans casse ni accent) + title_low = title.lower() + url_request = "https://api.archives-ouvertes.fr/search/?q=title_t:\"{0}\"&fl=uri_s,halId_s,authFullName_s,authIdHal_s,title_s&wt=json".format(title_low) + + req = requests.get(url_request) + json = "" + try : + json = req.json() + except ValueError : + print ("PROBLEME VALUEERROR") + try : + if json is not "" : + result = json['response']['docs'] + + # si un resultat est trouve, recup authors et URI pour affichage dans 'resultat', action_todo = "E" + if (len(result) == 1 ) : + all_auth = "" + try : + tous_authors = result[0]["authFullName_s"] + for auth in tous_authors: + all_auth = all_auth + auth+"-" + except KeyError, e : + print ("error print authors existing publi") + resultat = resultat + "num. "+numero+" - "+result[0]["uri_s"]+" - auteurs:"+all_auth+"<br/>" + action_todo = "E" + + # si plusieurs resultats trouves, recup URI pour affichage dans 'problemes_doublon', action_todo = "2" + if (len(result) >1 ) : + problemes_doublon = problemes_doublon + "num. "+numero+" - URLS "+result[0]["uri_s"]+" "+result[1]["uri_s"]+"<br/>" + action_todo = "2" + + # Si aucun resultat on peut deposer, action_todo = "D" + if (len(result) == 0 ) : + action_todo = "D" + result = False + accept_depot = True + + # Si caracteres incoherents (issus de DBLP) dans le titre -> pas de depot -> problemes_depot + if ("\\(^\\mbox" in title) : + print("-----------------> MBOX") + accept_depot = False + result = False + + title = title.encode("utf-8") + conf = conf.encode("utf-8") + town = town.encode("utf-8") + editor_book = editor_book.encode("utf-8") + + if accept_depot == True : + print ("bool_depot_prod ",bool_depot_prod ) + result = createXml_sendHal(numero, listauthors, language, title, conf, nb_pages, year, listdomains, type_publi, town, country, country_acr,doi_value, editor_book, volume, name_user, labo_auth_final, id_hal_user, login_user, login_depot, passwd_depot, bool_depot_preprod, bool_depot_prod) + + # methode createXml_sendHal renvoie true -> depot HAL ou preprod OK + if result == True : + depots = depots + "num. "+numero+" au titre "+title.decode("utf-8")+" deposee dans HAL<br/>" + # methode createXml_sendHal renvoie true -> pb depot HAL ou preprod ou pas de depot demande + if result == False : + problemes_depot = problemes_depot + "num. "+numero+" au titre "+title.decode("utf-8")+" a un probleme de depot<br/>" + else : + # pb de lecture json, pas depot -> problemes_url + action_todo = "P" + problemes_url = problemes_url + "num. "+numero+" au titre "+title.decode("utf-8")+"<br/>" + except KeyError : + # pb de lecture json, pas depot -> problemes_url + action_todo = "P" + problemes_url = problemes_url + "num. "+numero+" au titre "+title.decode("utf-8")+"<br/>" + + # Creation du CSV qui sera renvoye et affiche + authors = authors.replace(" and",", ") + list_pub_csv.append((numero,authors,title,conf,nb_pages, volume,year, type_publi, action_todo, town, country, country_acr, language)) + + + elif single == True : + title = title.encode("utf-8") + conf = conf.encode("utf-8") + town = town.encode("utf-8") + editor_book = editor_book.encode("utf-8") + login_user = login_depot + reponse_single_xml = create_single_xml(listauthors, language, title, conf, nb_pages, year, listdomains, type_publi, town, country, country_acr, doi_value, editor_book, volume, name_user, labo_auth_final, id_hal_user, login_user) + + # ------------------END LOOP ----------------- + cnt_total+=1 + + ######################## + ####### ECRITURE RESULTATS -> list_resultats + print ("list_pub_csv length :",cnt_total) + + list_resultats=[] + for pub in list_pub_csv : + allauth = pub[1] + allauth = allauth.replace("\n","") + title = pub[2]#.decode("utf-8") + conf = pub[3]#.decode("utf-8") + ville = pub[9]#.decode("utf-8") + + list_resultats.append((str(pub[0]),allauth,title,conf,str(pub[4]),str(pub[5]),str(pub[6]),str(pub[7]),str(pub[8]),ville,str(pub[10]),str(pub[11]),str(pub[12]))) + + cnt_nb_publis = cnt_article + cnt_inproceeding + cnt_book + cnt_incollection + cnt_phdthesis + + list_resultats.append(("RESULTATS","nombre de publis",str(cnt_nb_publis),"","","","","","","","","","")) + list_resultats.append(("RESULTATS","publis deja presentes dans HAL",resultat,"","","","","","","","","","")) + list_resultats.append(("RESULTATS","depots",depots,"","","","","","","","","","")) + list_resultats.append(("RESULTATS","problemes de depot",problemes_depot,"","","","","", "","","","","")) + list_resultats.append(("RESULTATS","problemes de doublon",problemes_doublon,"","","","", "","","","","","")) + list_resultats.append(("RESULTATS","problemes url",problemes_url,"","","","","","","","","","")) + + print ("####### RESULTATS PARSING BIBTEX ########") + print ("cnt_article ", cnt_article) + print ("cnt_inproceeding ", cnt_inproceeding) + print ("cnt_proceeding ", cnt_proceeding) + print ("cnt_incollection ", cnt_incollection) + print ("cnt_book ", cnt_book) + print ("cnt_phdthesis ", cnt_phdthesis) + print ("cnt_total ", cnt_total) + + print ("ERROR Author", cnt_error_auth) + print ("ERROR Title", cnt_error_title) + + print ("-------ERRORS ARTICLE------") + print ("cnt_error_jrn",cnt_error_jrn) + print ("cnt_error_vol",cnt_error_vol) + print ("cnt_error_numb",cnt_error_numb) + print ("cnt_error_pages",cnt_error_pages) + print ("cnt_error_year_art",cnt_error_year_art) + print ("cnt_error_doi",cnt_error_doi) + + print ("-------ERRORS INPROCEEDINGS------") + print ("cnt_error_booktitle:",cnt_error_booktitle) + print ("cnt_error_pages:",cnt_error_pages) + print ("cnt_error_year_inp:",cnt_error_year_inp) + print ("cnt_error_crossref:",cnt_error_crossref) + print ("cnt_error_publisher:",cnt_error_publisher) + print ("cnt_error_editor:",cnt_error_editor) + + print ("-------ERRORS PROCEEDINGS------") + print ("cnt_error_idcrossref:",cnt_error_idcrossref) + + print ("#########################################") + + print ("######## RESULTATS XML + DEPOTS ##########") + print ("RESULTATS existants") + print (resultat.encode("utf-8")) + print ("------------------") + print ("DEPOTS effectues") + print (depots.encode("utf-8")) + print ("------------------") + print ("PROBLEMES DEPOTS") + print (problemes_depot.encode("utf-8")) + print ("------------------") + print ("PROBLEMES DOUBLONS") + print (problemes_doublon.encode("utf-8")) + print ("------------------") + print ("PROBLEMES URL") + print (problemes_url.encode("utf-8")) + + + if single == False : + return list_resultats + if single == True : + return reponse_single_xml diff --git a/hal/templates/base.html b/hal/templates/base.html index 54ea2d6..39ab07e 100644 --- a/hal/templates/base.html +++ b/hal/templates/base.html @@ -10,7 +10,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils Hal</title> + <title>ImportHal</title> </head> {% endblock %} <!-- <body> --> @@ -49,6 +49,13 @@ </div> </li> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">CSV</a> + <div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 39px, 0px);"> + <a class="dropdown-item" href="{% url "csv2hal" %}">CSV_2_Hal</a> + </div> + </li> + <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">TEXT</a> <div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 39px, 0px);"> @@ -72,7 +79,7 @@ <h1 class="site-heading text-center text-white d-none d-lg-block"> - <span class="site-heading-upper text-primary mb-3">Outils HAL</span> + <span class="site-heading-upper text-primary mb-3">Import'HAL</span> </h1> diff --git a/hal/templates/hal/aide.html b/hal/templates/hal/aide.html index 3fe5481..04bea03 100644 --- a/hal/templates/hal/aide.html +++ b/hal/templates/hal/aide.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} @@ -243,8 +243,39 @@ Copiez-collez le contenu de ce fichier dans l'emplacement prévu nommé <b>conte <p align="left"><b><u><a id="dblp_2_xml"></a>Create XML (from DBLP)</u></b></p> <p align="justify"> -TODO +Cet outil permet de générer un fichier XML au format TEI, donc importable dans HAL, mais sans faire d'import. +<br/> +Il ne permet de générer qu'un seul fichier XML à partir d'une seule entrée Bibtex. +<br/> +Cet outil sera utile lorsqu'un dépôt ne passe pas. +En effet l'idée est d'utiliser une fonctionnalité de <a href="https://api.archives-ouvertes.fr/docs/sword">SWORD</a>, un client web permettant d'importer un fichier XML. +<br/> +<br/> +Comme pour l'outil Bibtex_2_hal, remplissez les champs demandés. +<br/><br/> + +La <b>forme auteur</b> correspond à la dénomination littérale de l'auteur dans le fichier bibtex. +<br> +Le <b>nom</b> et <b>prénom</b> du chercheur correspondent à ce que vous souhaitez voir apparaître dans HAL +<br/> +Le <b>login Hal</b> et l<b>'idHal</b> du chercheur doivent vous être fournis par l'auteur lui-même dans le cas d'un dépôt sur la base HAL. +<br/> +Le <b>numéro de structure</b> correspond à son identifiant (id). Rentrez le nom de la structure sur la base AuréHAL pour l'obtenir et rentrez le tel quel (Ex : 490706) +<br/> +Les <b>domaines</b> sont généralement fournis par le chercheur et s'appliquent à toutes les publications. +<br/> +Cliquez <a href="#trav_equip">ici</a> pour en avoir la liste. +<br/><br/> + +Ouvrir le client web avec vos identifiants HAL à l'URL suivante <a href="https://api.archives-ouvertes.fr/sword/upload/"> + https://api.archives-ouvertes.fr/sword/upload/</a> +<br/> +Vous pouvez laisser tels quels tous les champs et juste importer votre fichier et cliquer sur "Transférer". +Si le transfert (import dans HAL) ne se fait pas, un message d'erreur vous indiquera où se situe le problème. +<br/> +<img src="{% static 'img/aide_upload_xml1.png' %}" > </p> + <br/> <br/> diff --git a/hal/templates/hal/bibtex2hal.html b/hal/templates/hal/bibtex2hal.html index 547584a..49ec8f5 100644 --- a/hal/templates/hal/bibtex2hal.html +++ b/hal/templates/hal/bibtex2hal.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/templates/hal/bibtexxml.html b/hal/templates/hal/bibtexxml.html index 4af8064..6014c24 100644 --- a/hal/templates/hal/bibtexxml.html +++ b/hal/templates/hal/bibtexxml.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/templates/hal/blog.html b/hal/templates/hal/blog.html index 114a4c0..58e53c0 100644 --- a/hal/templates/hal/blog.html +++ b/hal/templates/hal/blog.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/templates/hal/connexion.html b/hal/templates/hal/connexion.html index adf7603..8369bea 100644 --- a/hal/templates/hal/connexion.html +++ b/hal/templates/hal/connexion.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/templates/hal/csv2hal.html b/hal/templates/hal/csv2hal.html new file mode 100644 index 0000000..519ef57 --- /dev/null +++ b/hal/templates/hal/csv2hal.html @@ -0,0 +1,263 @@ +{% extends "base.html" %} +{% load static %} +{% load staticfiles %} +{% load bootstrap3 %} +{% load bootstrap_themes %} + +{% block head %} +<head> + <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> + <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> + <title>ImportHAL</title> + +</head> +{% endblock %} + +{% block content %} +<h3>CSV to HAL</h3> + <br/> + {% if user.is_authenticated %} + <p align="left">Remplissez les données demandées ci-dessous + <br/> + Tous les champs sont requis + <br/> + Dans un premier temps, simulez le dépôt en laissant les champs "dépôt préprod" et "dépôt prod" décochés + <br/> + Copiez-collez le contenu de votre fichier CSV dans la zone prévue</p> + + <div class="well bs-component"> + <form action="{% url "csv2hal" %}" method="post"> + {% csrf_token %} + + <div class="row"> + + <div class="col-lg-12"> + <div class="fieldWrapper"> + <h3>Auteur</h3> + </div> + </div> + + <!-- 1e ligne --> + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.form_author.errors }} + {{ form.form_author.label_tag }}<br/> + {{ form.form_author }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.name_user.errors }} + {{ form.name_user.label_tag }}<br/> + {{ form.name_user }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.firstname_user.errors }} + {{ form.firstname_user.label_tag }}<br/> + {{ form.firstname_user }} + </div> + </div> + <!-- 2e ligne --> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.login_user.errors }} + {{ form.login_user.label_tag }}<br/> + {{ form.login_user }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.id_hal_user.errors }} + {{ form.id_hal_user.label_tag }}<br/> + {{ form.id_hal_user }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.labo_auth_final.errors }} + {{ form.labo_auth_final.label_tag }}<br/> + {{ form.labo_auth_final }} + </div> + <br/> + </div> + + + <!-- 3e ligne --> + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.domain1.errors }} + {{ form.domain1.label_tag }}<br/> + {{ form.domain1 }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.domain2.errors }} + {{ form.domain2.label_tag }}<br/> + {{ form.domain2 }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.domain3.errors }} + {{ form.domain3.label_tag }}<br/> + {{ form.domain3 }} + </div> + </div> + + <!-- 4e ligne --> + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.domain4.errors }} + {{ form.domain4.label_tag }}<br/> + {{ form.domain4 }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.domain5.errors }} + {{ form.domain5.label_tag }}<br/> + {{ form.domain5 }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + </div> + </div> + + <div class="col-lg-12"> + <div class="fieldWrapper"> + <h3>Source</h3> + </div> + </div> + + <div class="col-lg-12"> + <div class="fieldWrapper"> + {{ form.choice_source.errors }} + {{ form.choice_source.label_tag }}<br/> + {{ form.choice_source }} + </div> + </div> + + <div class="col-lg-12"> + <div class="fieldWrapper"> + <h3>Déposant</h3> + </div> + </div> + + <!-- 5e ligne --> + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.login_depot.errors }} + {{ form.login_depot.label_tag }}<br/> + {{ form.login_depot }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.passwd_depot.errors }} + {{ form.passwd_depot.label_tag }}<br/> + {{ form.passwd_depot }} + </div> + </div> + + <div class="col-lg-4"> + <div class="fieldWrapper"> + {{ form.choice_depot.errors }} + {{ form.choice_depot.label_tag }}<br/> + {{ form.choice_depot }} + </div> + </div> + + + <!-- 6e ligne --> + <div class="col-lg-12"> + <br/> + <hr> + <div class="fieldWrapper"> + {{ form.csv_file.errors }} + {{ form.csv_file.label_tag }}<br/> + {{ form.csv_file }} + </div> + </div> + </div> + + + <input type="submit" class="btn btn-primary" value="Submit" /> + </form> + </div> + + <div class="well bs-component"> + <div class="row"> + <div class="col-lg-12"> + <br/> + <b><u>Résultats :</u></b> + {% autoescape off %} + <br/> + <p align="left"> + {{ reponse_to_post }} + </p> + <br/> + {% endautoescape %} + <br/> + </div> + </div> + </div> + + {% if list_to_post|length > 0%} + <table class="table table-striped table-hover "> + <tr> + <th>n°</th> + <th>Auteurs </th> + <th>Titre</th> + <th>Conf</th> + <th>pp </th> + <th>vol</th> + <th>date</th> + <th>type </th> + <th>todo</th> + <th>Ville</th> + <th>pays </th> + <th>acr</th> + <th>lang</th> + </tr> + + {% for pub in list_to_post %} + <tr> + <td>{{ pub.num }}</td> + <td>{{ pub.authors }}</td> + <td>{{ pub.title }}</td> + <td>{{ pub.conf }}</td> + <td>{{ pub.page }}</td> + <td>{{ pub.vol }}</td> + <td>{{ pub.date }}</td> + <td>{{ pub.type }}</td> + <td>{{ pub.todo }}</td> + <td>{{ pub.ville }}</td> + <td>{{ pub.pays }}</td> + <td>{{ pub.acr }}</td> + <td>{{ pub.lang }}</td> + </tr> + {% endfor %} + </table> + {% endif %} + + {% else %} + <h5>Pour faire une demande d'identifiants, envoyer un mail à jessica.leyrit@uca.fr</h5> + <br/> + + {% endif %} + +{% endblock %} diff --git a/hal/templates/hal/index.html b/hal/templates/hal/index.html index a165d25..d5fa2ca 100644 --- a/hal/templates/hal/index.html +++ b/hal/templates/hal/index.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} @@ -30,7 +30,13 @@ <b>Bibtex_2_Hal</b> permet de faire des imports massifs dans HAL à partir de fichiers au format bibtex provenant de différentes sources. <br/> - <a href="https://dblp.uni-trier.de/" target="_blank">DBLP</a>. + <br/> + - <a href="https://dblp.uni-trier.de/" target="_blank">ENDNOTE</a>. <br/><br/> + <b>CSV_2_Hal</b> permet de faire des imports massifs dans HAL à partir de fichiers au format CSV provenant de différentes sources. + <br/> + - <a href="https://dblp.uni-trier.de/" target="_blank">KEYNOTE</a>. + <br/><br/> <b>Text_2_Hal</b> permet de faire des imports massifs dans HAL à partir d'une liste texte, de formater une liste texte ou de formater du texte issu de différentes sources telles que : <br/> - <a href="https://www.researchgate.net/" target="_blank">Research Gate</a> diff --git a/hal/templates/hal/text2hal.html b/hal/templates/hal/text2hal.html index 391d303..374c075 100644 --- a/hal/templates/hal/text2hal.html +++ b/hal/templates/hal/text2hal.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/templates/hal/text_format.html b/hal/templates/hal/text_format.html index 16522f2..318b428 100644 --- a/hal/templates/hal/text_format.html +++ b/hal/templates/hal/text_format.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/templates/hal/textmodif.html b/hal/templates/hal/textmodif.html index 6783e7b..9691b14 100644 --- a/hal/templates/hal/textmodif.html +++ b/hal/templates/hal/textmodif.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/templates/hal/textxml.html b/hal/templates/hal/textxml.html index 02f4363..42b886b 100644 --- a/hal/templates/hal/textxml.html +++ b/hal/templates/hal/textxml.html @@ -8,7 +8,7 @@ <head> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> <link rel="shortcut icon" href="{% static 'imgsite/favicon.ico' %}"> - <title>Outils HAL</title> + <title>ImportHAL</title> </head> {% endblock %} diff --git a/hal/urls.py b/hal/urls.py index 2255940..6932707 100644 --- a/hal/urls.py +++ b/hal/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ url(r'^blog$', views.article_blog, name='blog'), url(r'^bibtex2hal$', views.bibtex2hal, name='bibtex2hal'), url(r'^bibtexxml$', views.bibtexxml, name='bibtexxml'), + url(r'^csv2hal$', views.csv2hal, name='csv2hal'), url(r'^text2hal$', views.text2hal, name='text2hal'), url(r'^textxml$', views.textxml, name='textxml'), url(r'^textmodif$', views.textmodif, name='textmodif'), diff --git a/hal/views.py b/hal/views.py index 788fcc7..8b9e027 100644 --- a/hal/views.py +++ b/hal/views.py @@ -7,7 +7,8 @@ from django.contrib.auth.backends import ModelBackend from django.contrib.auth import authenticate, login from django.contrib.auth.views import logout -from forms import ConnexionForm, Bibtex2halForm, Text2halForm, BibtexXmlForm, TextXmlForm, ModifTextForm,TextFromRG +from forms import ConnexionForm, Bibtex2halForm, Text2halForm, BibtexXmlForm, Csv2halForm, \ + TextXmlForm, ModifTextForm,TextFromRG from scripts_bibtex import script_bibtex_2_hal from scripts_text import script_text_2_hal @@ -228,6 +229,99 @@ def bibtexxml(request): return render(request, 'hal/bibtexxml.html', locals()) + +################################################################################################################################## +## CSV2HAL ##################################################################################################################### +################################################################################################################################## + + +def csv2hal(request): + + reponse = "" + form = Csv2halForm(request.POST or None) + + if form.is_valid(): + # recup donnees + form_author = form.cleaned_data['form_author'] + name_user = form.cleaned_data['name_user'] + firstname_user = form.cleaned_data['firstname_user'] + labo_auth_final = form.cleaned_data['labo_auth_final'] + id_hal_user = form.cleaned_data['id_hal_user'] + login_user = form.cleaned_data['login_user'] + domain1 = form.cleaned_data['domain1'] + domain2 = form.cleaned_data['domain2'] + domain3 = form.cleaned_data['domain3'] + domain4 = form.cleaned_data['domain4'] + domain5 = form.cleaned_data['domain5'] + + login_depot = form.cleaned_data['login_depot'] + passwd_depot = form.cleaned_data['passwd_depot'] + #bool_depot_preprod = form.cleaned_data['bool_depot_preprod'] + #bool_depot_prod = form.cleaned_data['bool_depot_prod'] + choice_depot = form.cleaned_data['choice_depot'] + csv_file = form.cleaned_data['csv_file'] + + bool_depot_preprod = False + bool_depot_prod = False + + # recup du type de depot + if choice_depot == 'PREPROD' : + print ("Views.py depot preprod") + bool_depot_preprod = True + if choice_depot == 'PRODHAL' : + print ("Views.py depot prod") + bool_depot_prod = True + + + labo_auth_final = "struct-"+labo_auth_final + + #gestion domaines domain1 obligatoire + listdomains = [] + listdomains.append(domain1) + if domain2 is not "" : + listdomains.append(domain2) + if domain3 is not "" : + listdomains.append(domain3) + if domain4 is not "" : + listdomains.append(domain4) + if domain5 is not "" : + listdomains.append(domain5) + + single = False + + print ("begin script") + reponse = script_csv_2_hal (csv_file, bib_name_user, name_user, firstname_user, labo_auth_final, id_hal_user, login_user, listdomains, login_depot, passwd_depot, bool_depot_preprod, bool_depot_prod, single, source_csv) + + reponse_to_post = "" + list_to_post = [] + for result in reponse : + if result[0] == "RESULTATS" : + reponse_to_post = reponse_to_post+"<br/><br/><b>"+result[1]+"</b><br/>"+result[2] + else : + p = PubliDescribe() + p.num = result[0] + p.authors = result[1] + p.title = result[2] + p.conf = result[3] + p.page = result[4] + p.vol = result[5] + p.date = result[6] + p.type = result[7] + p.todo = result[8] + p.ville = result[9] + p.pays = result[10] + p.acr = result[11] + p.lang = result[12] + + list_to_post.append(p) + #list_to_post.append((result[0],result[1],result[2],result[3],result[4],result[5],\ + #result[6],result[7],result[8],result[9],result[10],result[11],result[12],)) + #reponse=("reponse HAL") + return render(request, 'hal/csv2hal.html', locals()) + + + + ################################################################################################################################## ## TEXT2HAL ##################################################################################################################### ################################################################################################################################## diff --git a/staticfiles/img/aide_upload_xml1.png b/staticfiles/img/aide_upload_xml1.png new file mode 100644 index 0000000000000000000000000000000000000000..7c3284212f8b32f6d46e6d90c55b945e9494543e GIT binary patch literal 4266 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 Ky=tBNk$(df4C(0r literal 0 HcmV?d00001 -- GitLab