Skip to content

Commit aab8409

Browse files
Merge pull request #1170 from scieloorg/beta
Incorporação de códigos estáveis
2 parents 736ba1b + 7b091a7 commit aab8409

24 files changed

+498
-156
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
sudo: false
12
language: python
23
python:
34
- 2.7

Makefile

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ deps:
1010
clean:
1111
@echo "Removing all .pyc files..."
1212
@find . -name "*.pyc" -delete
13-
@echo "Removing articletrack db structure..."
1413

1514
test:
1615
@python $(MANAGE) test --settings=$(SETTINGS_TEST)

circus.ini-TEMPLATE

-35
This file was deleted.
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.. _func-getScanArticlesBatch:
2+
3+
``scanResults getScanArticlesBatch(1:string batch_id) throws (1:ServerError srv_err, 2:BadRequestError req_err, 3:TimeoutError tou_err);``
4+
==========================================================================================================================================
5+
6+
7+
Retorna a struct ``ScanArticlesResults`` representando o lote identificado
8+
por *batch_id*. A struct contém dois campos: 1) next_batch_id - que é o
9+
identificador para a obtenção do próximo lote e 2) articles - a lista de
10+
``Article``.
11+
12+
A responsabilidade de saber o momento de parar de solicitar por novos lotes é
13+
do cliente. O serviço sempre retornará valores para ''next_batch_id'', mesmo
14+
que não haja mais resultado.
15+
16+
17+
Exemplo::
18+
19+
def get_articles(year):
20+
batch_id = client.scanArticles(
21+
'{"query": {"match": {"year": %s}}, "size": 50}' % year)
22+
23+
while True:
24+
result = client.getScanArticlesBatch(batch_id)
25+
26+
if not result.articles:
27+
return
28+
29+
for article in result.articles:
30+
yield article
31+
32+
batch_id = result.next_batch_id
33+

docs/dev/rpc_spec/scanArticles.rst

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
.. _func-scanArticles:
2+
3+
``string scanArticles(1:string es_dsl_query) throws (1:ServerError srv_err, 2:BadRequestError req_err, 3:TimeoutError tou_err);``
4+
=================================================================================================================================
5+
6+
7+
Realiza consulta nos registros do tipo de ``journalmanager.models.Article``,
8+
fazendo uso da DSL do Elasticsearch, e retorna identificador para o primeiro
9+
lote de resultados.
10+
11+
Os índices que podem ser utilizados na consulta são:
12+
13+
+----------------------+------------------------------------------------------+
14+
| Índice | Descrição |
15+
+======================+======================================================+
16+
| abbrev_journal_title | Título abreviado do periódico conforme ISSN. |
17+
+----------------------+------------------------------------------------------+
18+
| epub | ISSN eletrônico do periódico. |
19+
+----------------------+------------------------------------------------------+
20+
| ppub | ISSN impresso do periódico. |
21+
+----------------------+------------------------------------------------------+
22+
| volume | Identificador de volume do fascículo o qual o artigo |
23+
| | é parte. |
24+
+----------------------+------------------------------------------------------+
25+
| issue | Identificador do fascículo o qual o artigo é parte. |
26+
+----------------------+------------------------------------------------------+
27+
| year | Ano de publicação. |
28+
+----------------------+------------------------------------------------------+
29+
| doi | Digital Object Identifier. |
30+
+----------------------+------------------------------------------------------+
31+
| pid | Identificador único de artigo (legado). |
32+
+----------------------+------------------------------------------------------+
33+
| aid | Article Id (identificador único atual). |
34+
+----------------------+------------------------------------------------------+
35+
| head_subject | Seção do fascículo a qual o artigo é parte. |
36+
+----------------------+------------------------------------------------------+
37+
| article_type | O tipo do documento conforme consta no doc XML. |
38+
+----------------------+------------------------------------------------------+
39+
| version | A versão da especificação SciELO PS. |
40+
+----------------------+------------------------------------------------------+
41+
| is_aop | Se o artigo é `ahead of print`. |
42+
+----------------------+------------------------------------------------------+
43+
| source | O documento XML codificado em utf-8. |
44+
+----------------------+------------------------------------------------------+
45+
46+
47+
.. note:: A quantidade de registros por lote não é definida pelo cliente, e
48+
sim pelo servidor, a fim de manter a saúde dos serviços.
49+
50+
51+
Exemplo::
52+
53+
batch_id = client.scanArticles('{"query": {"match": {"year": "2013"}}}')
54+
next_batch_id, articles = client.getScanArticlesBatch(batch_id)
55+
56+
for article in articles:
57+
...
58+

fabfile.py

-45
This file was deleted.

fabfile_settings.py-TEMPLATE

-9
This file was deleted.
-117 KB
Binary file not shown.

requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ django-kombu
2323
defusedxml==0.4.1
2424
cython
2525
thriftpy
26-
django-countries
26+
django-countries==3.3
2727
-e git+https://github.com/scieloorg/[email protected]#egg=thriftpywrap
2828
elasticsearch>=1.0.0,<2.0.0
29-
zerorpc>=0.4.4
29+
zerorpc>=0.4.4

scielomanager/editorialmanager/models.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class EditorialMember(models.Model):
3535
link_cv = models.URLField(_('Link CV'), null=True, blank=True)
3636
city = models.CharField(_('City'), max_length=256, null=True, blank=True)
3737
state = models.CharField(_('State'), max_length=256, null=True, blank=True)
38-
country = CountryField(_('Country'), default='')
39-
research_id = models.CharField(_('ResearchID'), max_length=256, null=True, blank=True)
38+
country = CountryField(_('Country'), blank_label="----------")
39+
research_id = models.CharField(_('ResearcherID'), max_length=256, null=True, blank=True)
4040
orcid = models.CharField(_('ORCID'), max_length=256, null=True, blank=True)
4141

4242
order = models.IntegerField(_('board order'), default=1)

scielomanager/editorialmanager/templates/board/board_list.html

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
//Register autocomplete when model made visible to the user
2828
$('#id_modal_form').on('loaded', function(){
2929
{% include "includes/add_member_form_loaded_callbacks.js" %}
30+
31+
$('.btn-resize-full').click()
3032
});//loaded
3133

3234
function display_error(message){

scielomanager/editorialmanager/templates/board/board_list_data.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ <h4><strong>{{ member.get_full_name }}</strong></h4>
153153
{% if member.institution %} &bull; {{ member.institution }}{% endif %}
154154
{% if member.city %} &bull; {{ member.city }} {% endif %}
155155
{% if member.state %} &bull; {{ member.state }} {% endif %}
156-
{% if member.country %} &bull; {{ member.country }} {% endif %}
156+
{% if member.country %} &bull; {{ member.country.name }} {% endif %}
157157
{% if member.email %}
158158
<a href="mailto:{{ member.email }}">
159159
<i class="icon-envelope"></i> {{ member.email }}
@@ -210,6 +210,6 @@ <h4><strong>{{ member.get_full_name }}</strong></h4>
210210
{% endfor %}
211211
</div>
212212

213-
{% modal_form title="Edit Member" %}
213+
{% modal_form title="Manage Member" %}
214214
{% modal_form title="Confirm to delete this Member" modal_id="id_modal_delete_member" %}
215215
{% modal_form title="Roles" modal_id="id_modal_roles" %}

scielomanager/editorialmanager/templates/includes/add_member_form_loaded_callbacks.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/* Este codigo contém callbacks que pode ser executado em duas situações:
44
* - 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)
5+
* - Quando o formulario de adicionar um Board Member é carregado numa página simples, (não no modal, nem com ajax.load)
66
*/
77

88
/*** campo Institution: AUTOCOMPLETE: ***/
@@ -154,7 +154,7 @@ function has_valid_orcid(orcid_id){
154154

155155
$('#id_orcid').on('input', function(event) {
156156
/* Para cada mudança no campo '#id_orcid':
157-
* Valida o fomarto do valor inserido no campo (has_valid_orcid(...)).
157+
* Valida o formato do valor inserido no campo (has_valid_orcid(...)).
158158
* - Se for válido, é criado um link para que o usuário possa validar visualmente.
159159
* - Se o conteúdo do campo é vazio, então é considerado como válido também.
160160
*/

scielomanager/editorialmanager/tests/test_forms.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
RoleType,
1313
RoleTypeTranslation)
1414
from audit_log.models import AuditLogEntry, ADDITION, CHANGE, DELETION
15+
from django_countries import countries
1516

1617

1718
def _makePermission(perm, model, app_label='editorialmanager'):
@@ -180,6 +181,7 @@ def test_ADD_board_member_valid_POST_is_valid(self):
180181
"""
181182
User of the group "Editors" successfully ADD a new board member
182183
"""
184+
183185
# with
184186
role = editorial_modelfactories.RoleTypeFactory.create()
185187
response = self.app.get(reverse("editorial.board.add", args=[self.journal.id, self.issue.id]), user=self.user)
@@ -219,7 +221,8 @@ def test_ADD_board_member_valid_POST_is_valid(self):
219221
self.assertIn(member_data['institution'], response.body)
220222
# the link_cv is not displayed in the frontend
221223
self.assertIn(member_data['state'], response.body)
222-
self.assertIn(member_data['country'], response.body)
224+
225+
self.assertIn(str(dict(countries)[member_data['country']]), response.body)
223226

224227
# check new member in DB
225228
members = EditorialMember.objects.filter(
@@ -311,6 +314,7 @@ def test_EDIT_board_member_valid_POST_is_valid(self):
311314
"""
312315
User of the group "Editors" successfully EDIT a board member
313316
"""
317+
314318
# with
315319
member = editorial_modelfactories.EditorialMemberFactory.create()
316320
member.board = EditorialBoard.objects.create(issue=self.issue)
@@ -353,7 +357,7 @@ def test_EDIT_board_member_valid_POST_is_valid(self):
353357
self.assertIn(member_data_update['institution'], response.body)
354358
# the link_cv is not displayed in the frontend
355359
self.assertIn(member_data_update['state'], response.body)
356-
self.assertIn(member_data_update['country'], response.body)
360+
self.assertIn(str(dict(countries)[member_data_update['country']]), response.body)
357361

358362
# check data from db
359363
member_from_db = EditorialMember.objects.get(pk=member.pk)

scielomanager/journalmanager/tasks.py

+9-34
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,17 @@
99
import os
1010
import io
1111
import logging
12-
import base64
1312
import threading
1413
import contextlib
1514
import datetime
1615
from copy import deepcopy
1716

1817
from lxml import isoschematron, etree
19-
from elasticsearch import Elasticsearch
20-
from django.conf import settings
2118
from django.db.models import Q
2219
from django.db import IntegrityError
2320

2421
from scielomanager.celery import app
22+
from scielomanager import connectors
2523
from . import models
2624

2725

@@ -37,27 +35,7 @@
3735
ARTICLE_META_SCHEMATRON = isoschematron.Schematron(file=BASIC_ARTICLE_META_PATH)
3836

3937

40-
def get_elasticsearch():
41-
"""Fábrica de clientes do Elasticsearch.
42-
43-
Essa função é um singleton.
44-
"""
45-
if not hasattr(get_elasticsearch, 'client'):
46-
get_elasticsearch.client = Elasticsearch(settings.ELASTICSEARCH_NODES)
47-
48-
return get_elasticsearch.client
49-
50-
51-
def index_article(id, struct, **kwargs):
52-
"""Indexa `struct` no índice de artigos do catman no Elasticsearch.
53-
"""
54-
client = get_elasticsearch()
55-
result = client.index(
56-
index=settings.ES_ARTICLE_INDEX_NAME,
57-
doc_type=settings.ES_ARTICLE_DOC_TYPE,
58-
id=id, body=struct, **kwargs)
59-
60-
return result
38+
elasticsearch_client = connectors.ArticleElasticsearch()
6139

6240

6341
def _gen_es_struct_from_article(article):
@@ -78,13 +56,12 @@ def _gen_es_struct_from_article(article):
7856
for attr, expr in values_to_struct_mapping}
7957

8058
article_as_octets = str(article.xml)
81-
base64_octets = base64.b64encode(article_as_octets)
8259

8360
partial_struct = {
8461
'version': article.xml_version,
8562
'is_aop': article.is_aop,
86-
'b64_source': base64_octets,
8763
'source': article_as_octets,
64+
'aid': article.aid,
8865
}
8966

9067
es_struct.update(partial_struct)
@@ -122,16 +99,14 @@ def submit_to_elasticsearch(article_pk):
12299
return None
123100

124101
struct = _gen_es_struct_from_article(article)
125-
result = index_article(article.aid, struct)
102+
elasticsearch_client.add(article.aid, struct)
126103

127-
logger.info('Elasticsearch indexing result for article "%s": %s.',
128-
article.domain_key, result)
104+
logger.info('Article "%s" was indexed successfully.', article.domain_key)
129105

130-
if result.get('_version') > 0:
131-
article.es_updated_at = datetime.datetime.now()
132-
article.es_is_dirty = False
133-
with avoid_circular_signals(ARTICLE_SAVE_MUTEX):
134-
article.save()
106+
article.es_updated_at = datetime.datetime.now()
107+
article.es_is_dirty = False
108+
with avoid_circular_signals(ARTICLE_SAVE_MUTEX):
109+
article.save()
135110

136111

137112
@app.task(ignore_result=True)

0 commit comments

Comments
 (0)