Skip to content

Commit 736ba1b

Browse files
Merge pull request #1162 from scieloorg/beta
Incorporação de códigos estáveis
2 parents 3c9b7e1 + 9c4fd93 commit 736ba1b

File tree

17 files changed

+356
-255
lines changed

17 files changed

+356
-255
lines changed

scielomanager/editorialmanager/forms.py

+51-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
# coding: utf-8
2-
2+
import re
33
from django import forms
44
from . import models
55
from django.forms.models import BaseInlineFormSet
66
from django.utils.translation import ugettext_lazy as _
77
from journalmanager.models import Issue, Journal
88

99

10+
orcid_pattern = re.compile(r'^\d{4}-\d{4}-\d{4}-\d{3}[\d|X]$')
11+
12+
1013
class EditorialMemberForm(forms.ModelForm):
1114
class Meta:
1215
model = models.EditorialMember
@@ -16,6 +19,53 @@ class Meta:
1619
'country': forms.Select(attrs={'class': 'chzn-select'}),
1720
}
1821

22+
def clean_orcid(self):
23+
"""
24+
Validação do formato do campo: "orcid" segundo os criterios:
25+
- verifica se o tamanho esta certo (19 chars contando os 3 "-")
26+
- verifica se atende a regex em ``is_valid_format(...)``
27+
(o ultimo digito pode ser um "X", ver referencia)
28+
- verifica se o checksum é valido com a função: ``is_valid_checksum(...)``
29+
30+
Se atende as 3 condições retorna true.
31+
Se o campo é vazio, também é considerado como válido.
32+
"""
33+
34+
def is_valid_checksum(orcid):
35+
"""
36+
Calcula o checksum a partir do orcid
37+
Referencia:
38+
http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
39+
"""
40+
total = 0
41+
cleaned_orcid = orcid.replace('-', '') # removo '-'
42+
last_digit = cleaned_orcid[-1]
43+
sliced_orcid = cleaned_orcid[:-1] # todos os digitos, menos o último
44+
for ch in sliced_orcid:
45+
digit = int(ch)
46+
total = (total + digit) * 2
47+
remainder = total % 11
48+
calculated_checksum = (12 - remainder) % 11
49+
50+
if calculated_checksum == 10:
51+
calculated_checksum = 'X'
52+
return str(calculated_checksum) == str(last_digit)
53+
54+
def is_valid_format(orcid):
55+
"""
56+
Retorna True se o formato respeita a regex:
57+
Referencia:
58+
http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
59+
"""
60+
matches = orcid_pattern.match(orcid)
61+
return bool(matches)
62+
63+
orcid = self.cleaned_data['orcid'].strip()
64+
if orcid:
65+
if not is_valid_format(orcid) or not is_valid_checksum(orcid):
66+
raise forms.ValidationError(_('This field is not valid!'))
67+
return orcid
68+
1969

2070
DIRECTION_CHOICES = (
2171
('up', "Up"),

scielomanager/editorialmanager/templates/board/board_list.html

+2-45
Original file line numberDiff line numberDiff line change
@@ -26,51 +26,8 @@
2626

2727
//Register autocomplete when model made visible to the user
2828
$('#id_modal_form').on('loaded', function(){
29-
30-
$('#id_institution').devbridgeAutocomplete({
31-
serviceUrl: "{{SETTINGS.WAYTA_URL}}{{SETTINGS.WAYTA_PATH}}institution",
32-
dataType: 'jsonp',
33-
paramName: 'q',
34-
noCache: true,
35-
params: {'accuracy': 3},
36-
minChars: 3,
37-
transformResult: function(response) {
38-
return {
39-
query:'q',
40-
suggestions: $.map(response.choices, function(dataItem) {
41-
return { value: dataItem.value, data:
42-
{'country': dataItem.country,
43-
'city': dataItem.city,
44-
'state': dataItem.state,
45-
'iso3166': dataItem.iso3166 }};
46-
})
47-
}
48-
},
49-
50-
formatResult: function (suggestion, currentValue) {
51-
return '<b>{% trans 'Institution' %}:</b> ' + suggestion.value + '</br>' +
52-
'<b>{% trans 'City' %}:</b> ' + suggestion.data.city + '</br>' +
53-
'<b>{% trans 'State' %}:</b> ' + suggestion.data.state + '</br>' +
54-
'<b>{% trans 'Country' %}:</b> ' + suggestion.data.country;
55-
},
56-
57-
onSelect: function (suggestion) {
58-
$('#id_city').val(suggestion.data.city)
59-
$('#id_state').val(suggestion.data.state)
60-
61-
$('#id_country option').each(function() {
62-
if (this.value == suggestion.data.iso3166){
63-
this.selected = this.text;
64-
$(".chzn-select").trigger("chosen:updated");
65-
}
66-
});
67-
},
68-
69-
appendTo: $('#selection')
70-
71-
});//devbridgeAutocomplete
72-
73-
});//shown
29+
{% include "includes/add_member_form_loaded_callbacks.js" %}
30+
});//loaded
7431

7532
function display_error(message){
7633
var template = '<div class="sys-msgs">' +

scielomanager/editorialmanager/templates/board/board_member_edit.html

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ <h2>{% trans "Edit Board Member" %}:</h2>
3737
no_results_text: "{% trans 'No results found for' %}:",
3838
width: "100%",
3939
});
40+
{% include "includes/add_member_form_loaded_callbacks.js" %}
4041
});
4142
</script>
4243
{% endblock %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
{% load i18n %}
2+
3+
/* Este codigo contém callbacks que pode ser executado em duas situações:
4+
* - Quando o formulario de adicionar um Board Member é carregado no modal (após ser disparado o evento: 'loaded')
5+
* - QUando o formulario de adicionar um Board Member é carregado numa página simples, (não no modal, nem com ajax.load)
6+
*/
7+
8+
/*** campo Institution: AUTOCOMPLETE: ***/
9+
10+
$('#id_institution').devbridgeAutocomplete({
11+
serviceUrl: "{{SETTINGS.WAYTA_URL}}{{SETTINGS.WAYTA_PATH}}institution",
12+
dataType: 'jsonp',
13+
paramName: 'q',
14+
noCache: true,
15+
params: {'accuracy': 3},
16+
minChars: 3,
17+
transformResult: function(response) {
18+
return {
19+
query:'q',
20+
suggestions: $.map(response.choices, function(dataItem) {
21+
return {
22+
value: dataItem.value,
23+
data: {
24+
'country': dataItem.country,
25+
'city': dataItem.city,
26+
'state': dataItem.state,
27+
'iso3166': dataItem.iso3166
28+
}
29+
};
30+
})
31+
}
32+
},
33+
34+
formatResult: function (suggestion, currentValue) {
35+
return '<b>{% trans 'Institution' %}:</b> ' + suggestion.value + '</br>' +
36+
'<b>{% trans 'City' %}:</b> ' + suggestion.data.city + '</br>' +
37+
'<b>{% trans 'State' %}:</b> ' + suggestion.data.state + '</br>' +
38+
'<b>{% trans 'Country' %}:</b> ' + suggestion.data.country;
39+
},
40+
41+
onSelect: function (suggestion) {
42+
$('#id_city').val(suggestion.data.city)
43+
$('#id_state').val(suggestion.data.state)
44+
45+
$('#id_country option').each(function() {
46+
if (this.value == suggestion.data.iso3166){
47+
this.selected = this.text;
48+
$(".chzn-select").trigger("chosen:updated");
49+
}
50+
});
51+
},
52+
53+
appendTo: $('#selection')
54+
55+
});//devbridgeAutocomplete
56+
57+
/*** Campo ORCID: validação Orcid ***/
58+
59+
function is_valid_orcid_checksum(input_string){
60+
/* Calcula o checksum a partir do orcid que vem no parametro: "input_string".
61+
* Se o checksum é igual ao ultimo digito do orcid, retorna: true.
62+
*
63+
* referencia:
64+
* http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
65+
*/
66+
var total = 0, digit = 0;
67+
var cleaned = input_string.trim().replace(/-/g, '');
68+
var last_digit = cleaned.charAt(cleaned.length-1);
69+
cleaned = cleaned.substring(0, cleaned.length -1);
70+
71+
for (var i = 0; i < cleaned.length; i++) {
72+
digit = parseInt(cleaned.charAt(i), 10);
73+
total = (total + digit) * 2;
74+
}
75+
76+
var remainder = total % 11;
77+
var calculated_checksum = (12 - remainder) % 11;
78+
79+
if (calculated_checksum == 10){
80+
calculated_checksum = "X";
81+
}
82+
83+
return calculated_checksum == last_digit;
84+
}
85+
86+
function display_validation_result(field, is_valid){
87+
/* - adiciona no DOM as classes de "error" ou "success"
88+
* para mostrar para o usuário o resultado da validação
89+
* - adiciona o background com o icone verde (ok) ou vermelho (error)
90+
*/
91+
if (is_valid) {
92+
93+
field.parents('.control-group')
94+
.removeClass('error')
95+
.addClass('success');
96+
field.removeClass('invalid-input-icon')
97+
.addClass('valid-input-icon');
98+
99+
} else {
100+
101+
field.parents('.control-group')
102+
.removeClass('success')
103+
.addClass('error');
104+
field.removeClass('valid-input-icon')
105+
.addClass('invalid-input-icon');
106+
107+
}
108+
}
109+
110+
111+
function display_orcid_link(field, orcid_id){
112+
/* Adiciona um link para que o usurário acesse o perfil no site orchid.org
113+
*/
114+
var target_url = 'http://orcid.org/' + orcid_id; /* sem barra no final, porque se não manda pro login */
115+
var link = $('<a />').attr({
116+
'href': target_url,
117+
'title': target_url,
118+
"target": "_blank",
119+
}).html(target_url);
120+
var field_container = field.parents('.control-group'); /* control-group */
121+
var link_placeholder = field_container.find('#orcid_link_placeholder');
122+
123+
if (link_placeholder.size() > 0 ) {
124+
link_placeholder.html(link);
125+
} else {
126+
/* link placeholder not found, create a new one */
127+
link_placeholder = $('<span />').attr({
128+
'id': 'orcid_link_placeholder',
129+
'class': 'pull-right',
130+
}).html(link);
131+
field_container.append(link_placeholder);
132+
}
133+
}
134+
135+
function clear_orcid_link(field){
136+
var link_placeholder = field.parents('.control-group').find('#orcid_link_placeholder');
137+
link_placeholder.html('');
138+
}
139+
140+
function has_valid_orcid(orcid_id){
141+
/* Validação do formato do string: "orcid_id" segundo os criterios:
142+
* - verifica se o tamanho esta certo (19 chars contando os 3 "-")
143+
* - verifica se atende a regex (o ultimo digito pode ser um "X", ver referencia)
144+
* - verifica se o checksum é valido com a função: is_valid_orcid_checksum(...)
145+
*
146+
* Se atende as 3 condições retorna true.
147+
*
148+
* referencias:
149+
* - http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
150+
*/
151+
var re = /^\d{4}-\d{4}-\d{4}-\d{3}[\d|X]$/;
152+
return re.test(orcid_id) && is_valid_orcid_checksum(orcid_id);
153+
}
154+
155+
$('#id_orcid').on('input', function(event) {
156+
/* Para cada mudança no campo '#id_orcid':
157+
* Valida o fomarto do valor inserido no campo (has_valid_orcid(...)).
158+
* - Se for válido, é criado um link para que o usuário possa validar visualmente.
159+
* - Se o conteúdo do campo é vazio, então é considerado como válido também.
160+
*/
161+
162+
var orcid_field = $(this);
163+
var orcid_input = this.value.trim();
164+
165+
clear_orcid_link(orcid_field);
166+
167+
if (orcid_input.length > 0) {
168+
if (has_valid_orcid(orcid_input)) {
169+
/* input tem formato ok, mostrar link para o usuário */
170+
display_validation_result(orcid_field, true);
171+
display_orcid_link(orcid_field, orcid_input);
172+
} else {
173+
/* input de tamanho errado, ou formato errado, ou checksum inválido */
174+
display_validation_result(orcid_field, false);
175+
}
176+
} else {
177+
/* input pode ser vazio, isso é válido, mas não vai ter link! */
178+
display_validation_result(orcid_field, true);
179+
}
180+
});

scielomanager/editorialmanager/tests/modelfactories.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,4 @@ class EditorialMemberFactory(factory.Factory):
4343
state = factory.Sequence(lambda n: 'state_%s' % n)
4444
country = 'BR'
4545
research_id = factory.Sequence(lambda n: 'A-%04d-2014' % int(n))
46-
orcid = factory.Sequence(lambda n: '0000-0001-0002-%04d' % int(n))
47-
46+
orcid = factory.Sequence(lambda n: '0000-0002-4668-7157') # por padrão um orcid válido

0 commit comments

Comments
 (0)