GithubHelp home page GithubHelp logo

scieloorg / kernel Goto Github PK

View Code? Open in Web Editor NEW
6.0 11.0 11.0 534 KB

É o componente central da nova arquitetura de sistemas de informação da Metodologia SciELO, ainda em fase de desenvolvimento.

Home Page: https://docs.google.com/document/d/14YBl7--4ouaWBQhxzUYWRuhmegwnSYrDgupsED6rhvM/edit?usp=sharing

License: BSD 2-Clause "Simplified" License

Python 99.77% Dockerfile 0.23%
scielo-publishing-framework

kernel's People

Contributors

cesarbruschetta avatar dependabot[bot] avatar gitnnolabs avatar gustavofonseca avatar jamilatta avatar joffily avatar joffilyfe avatar patymori avatar robertatakenaka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kernel's Issues

[Domain][Journal] Adicionar, inserir e remover Issues

O Journal deve permitir que sejam adicionados, inseridos e removidos Issues que compõem a lista de items. A adição é feita sempre ao final da lista, a inserção é feita em uma posição específica da lista e a remoção necessita somente do item em si que precisa ser removido.

Introduzir suporte ao registro de PDFs dos documentos

Deve ser possível registrar zero ou mais manifestações do documento.

Pense no documento em XML como uma matriz -- uma representação que possibilita a derivação de outros formatos, entre eles o HTML e o PDF. Podemos dizer que o HTML ou o PDF derivado do XML é uma manifestação do mesmo, ou uma rendition conforme é chamado nos softwares DAM.

Uma manifestação (rendition) é intimamente relacionada a versão do XML, isto é, se o XML sofrer qualquer alteração uma nova versão do seu PDF terá de ser produzida.

Um único arquivo XML pode abrigar a representação de múltiplos documentos (no caso de traduções, por exemplo), portanto o relacionamento entre documento XML e menifestações é do tipo um para muitos. Uma manifestação poderá sofrer alterações e deverá ser passível de versionamento.

Sugestão para a representação no manifesto do documento:

{
    "id": "S0034-89102014000200347",
    "versions": [
        {
            "assets": [ ... ],
            "data": "...",
            "timestamp": "...",
            "renditions": [
                {
                    "filename": "rsp-2018-v34n12-003-pt.pdf",
                    "data": [
                        {
                            "timestamp": "2018-08-05T23:08:50.331687Z", 
                            "url": "https://files.scielo.br/catalog/rsp/2018/v34n12/hkj23huas/f714373b20aec5790ba17253ba631807.pdf",
                            "size_bytes": 243000,
                        },
                    ],
                    "mimetype": "application/pdf",
                    "lang": "pt-br",
                }
            ]
        }
    ]
}

Interface RESTful:

  • GET /documents/:document_id/renditions: retorna uma lista com todas as manifestações do documento XML na sua versão mais recente. Exemplo:
[
    {
        "filename": "rsp-2018-v34n12-003-pt.pdf",
        "url": "https://files.scielo.br/catalog/rsp/2018/v34n12/hkj23huas/f714373b20aec5790ba17253ba631807.pdf",
        "mimetype": "application/pdf",
        "lang": "pt-br",
        "size_bytes": 243000,
    }
]

Argumentos opcionais:

  • when: data e hora, no formato timestamp utc, indicando o momento o qual o estado deve retratar.

  • PATCH /documents/:document_id/renditions: registra uma nova versão do arquivo identificado por filename. Cria um novo caso não exista previamente. O arquivo está sempre relacionado com a versão mais recente do documento XML, no momento em que a requisição ocorrer. O payload a ser enviado para o servidor é o mesmo recebido ao acessar o método GET. Para ser considerada uma nova versão de uma manifestação, os valores de filename, mimetype e lang devem ser casar.

Criar endpoints em formato API RESTful para Periódicos

Comportamento esperado

O kernel deve disponibilizar uma interface RESTful para a criação e leitura de Periódicos.

As rotas disponibilizadas serão:

  • GET - /journals/<str:id> deve retornar as informações atreladas ao journal recuperado ou 404 caso não seja encontrado
  • PUT - /journals/<str:id> deve criar um novo periódico de acordo com as informações passadas no corpo da requisição HTTP e retornar o status 201 em caso de sucesso. Referencia de retornos possíveis para o PUT: https://stackoverflow.com/a/827045
  • PATCH - /journals/<str:id> deve adicionar mudanças a um journal existente.

Comportamento atual

Não há interface RESTful para Periódicos

Etapas do desenvolvimento

  • Rota para recuperar um journal existente
  • Rota para criar um journal
  • Rota para atualizar um journal

[Domain][Journal] Compor com Documents Bundles por meio de inserção

O Journal deve manter uma lista de bundles que o compõe por meio de inserção nesta lista, dada uma determinada posição. O bundle é representado aqui pela uri no kernel e a inserção deve ser na posição informada.

Exemplo da lista:

item = [
    "1",
    "2",
    "3",
]

Onde 1, 2 e 3 são ids de bundles.

Mudança nos dados retornados dos *atributos de metadado* de instâncias de *Journal* e *DocumentsBundle* não deve alterar o estado interno das instâncias

Por "Atributo de metadado" me refiro a todos os getters e setters que armazenam e recuperam seus valores da chave metadata, no manifest da instância.

O fragmento de código abaixo demonstra o comportamento indesejado, onde a mudança no valor retornado altera o estado interno da instância:

>>> from documentstore.domain import Journal
>>>
>>> bjmbr = Journal(id="bjmbr")
>>> bjmbr.other_titles = ["Foo"]
>>>
>>> bjmbr.manifest
{'id': 'bjmbr', 'created': '2019-02-07T20:38:09.815314Z', 'updated': '2019-02-07T20:38:45.787322Z', 'items': [], 'metadata': {'other_titles': [('2019-02-07T20:38:45.787322Z', ['Foo'])]}}
>>>
>>> other_titles = bjmbr.other_titles
>>> other_titles
['Foo']
>>> other_titles.append('Bar')
>>>
>>> bjmbr.other_titles
['Foo', 'Bar']
>>>
>>> bjmbr.manifest
{'id': 'bjmbr', 'created': '2019-02-07T20:38:09.815314Z', 'updated': '2019-02-07T20:38:45.787322Z', 'items': [], 'metadata': {'other_titles': [('2019-02-07T20:38:45.787322Z', ['Foo', 'Bar'])]}}
>>>

Note que o objeto retornado pela expressão bjmbr.other_titles é o mesmo referenciado internamente pela instância e sua manipulação altera o estado interno de bjmbr.

A solução que proponho aqui é a de levar mesma estratégia copy-on-read implementada para o atributo manifest, que consiste em retornar uma cópia (deep copy) das estruturas internas sempre que acessada para leitura.

Do ponto de vista prático se trata de realizar a seguinte mudança nas properties tipo getter dos atributos de metadado :

Código atual que permite o comportamento indesejado:

@property
def publication_year(self):
    return BundleManifest.get_metadata(self._manifest, "publication_year")

Código que elimina o problema por meio do copy-on-read:

@property
def publication_year(self):
    return BundleManifest.get_metadata(self.manifest, "publication_year")

API de mudanças

Deverá retornar uma lista ordenada dos registros que sofreram mudanças. A
ordenação será dada por meio do timestamp da aplicação. Apenas a versão mais
recente de cada registro é garantida de ser obtida, i.e., no caso de registros
atualizados e na sequência removidos a API poderá não mais fornecer os dados
no estado intermediário, antes da remoção.

Request:

GET /changes HTTP/1.1

Parâmetros:

  • since (string): Inicia a lista de resultados na mudança ocorrida no timestamp
    informado. Por padrão retornará desde a primeira mudança.
  • limit (number): Limita o total de resultados obtidos. Deve ser utilizado
    com o parâmetro since para iterar sobre os resultados de maneira paginada.
    O valor padrão é 500.

Response:

{
  "since": "2018-08-05T23:02:29.392990Z",
  "limit": 500,
  "results": [
    {
      "timestamp": "2018-08-05T23:02:29.392990Z", 
      "id": "documents/0034-8910-rsp-48-2-0347"
    },
    {
      "timestamp": "2018-08-05T23:09:09.569312Z", 
      "id": "documents/0034-8910-rsp-48-2-0347",
      "deleted": true
    }
  ]
}

A resposta é um objeto JSON com 3 atributos:

  • since: o valor do argumento de mesmo nome enviado na requisição. O valor padrão é uma string vazia;
  • limit: o valor do argumento de mesmo nome enviado na requisição. O valor padrão é 500;
  • results: um array que indica o recurso que sofreu a mudança. Note que,
    exceto para o caso de remoção do registro, não há qualquer indicação sobre a
    natureza da mudança (criação ou atualização), já que em ambos os casos
    é esperado que o cliente (re)obtenha o registro completo.

Restfull para a API de mudança.

Adicionar a capacidade de obtermos as mudanças que ocorreram nos registros do Kernel através de uma interface HTTP.

Request:

GET /changes HTTP/1.1

Parâmetros:

  • since (string): Inicia a lista de resultados na mudança imediatamente
    posterior ao timestamp informado. Por padrão retornará desde a primeira
    mudança.
  • limit (number): Limita o total de resultados obtidos. Deve ser utilizado
    com o parâmetro since para iterar sobre os resultados de maneira paginada.
    O valor padrão é 500.

Response:

{
  "last_timestamp": "2018-08-05T23:09:09.569312Z",
  "results": [
    {
      "timestamp": "2018-08-05T23:02:29.392990Z", 
      "id": "documents/0034-8910-rsp-48-2-0347"
    },
    {
      "timestamp": "2018-08-05T23:09:09.569312Z", 
      "id": "documents/0034-8910-rsp-48-2-0347",
      "deleted": true
    }
  ]
}

A resposta é um objeto JSON com 2 atributos:

  • last_timestamp: que indica o timestamp da última mudança registrada;
  • results: um array que indica o recurso que sofreu a mudança. Note que,
    exceto para o caso de remoção do registro, não há qualquer indicação sobre a
    natureza da mudança (criação ou atualização), já que em ambos os casos
    é esperado que o cliente (re)obtenha o registro completo.

[Services][Journal] Incluir serviço de criação

Para que um Journal seja incluído no Kernel, é necessário que exista um serviço para criá-lo. É esperado que os seguintes dados sejam informados:

  • id: do tipo string e obrigatório
  • bundles: lista de DocumentsBundle que compõem o Journal. A lista não é obrigatória.
  • metadata: metadados do Journal (dicionário). O metadados não é obrigatório.

Sistema de eventos

A criação de um sistema de eventos é parte da infraestrutura desejada para a implementação do ticket #50, que resolve a questão da API de mudanças.

A idéia é que seja possível registrar callbacks que serão executados sempre que eventos conhecidos como, por exemplo, a criação de um novo documento ou a sua remoção ocorrerem.

A notificação do evento (disparo) deve ser uma ação deliberada, geralmente de responsabilidade da rotina que implementa a transação completa. Exemplo de código:

class RegisterDocument(BaseRegisterDocument):
    ...  
    session.notify(DOCUMENT_CREATED, new_document)

Perceba que no fragmento de código acima o papel de notificar o evento é do objeto Session, que atua como um concetrador de configurações e de dependências no design da aplicação. Outro ponto é que os eventos são constantes globais do módulo. A proposta é que esta primeira versão implemente o padrão Observable de maneira enxuta, sem a criação de muitas abstrações. A interface proposta para o Observable -- que será implementada pelas classes Session --, é:

class Observable:
    def observe(self, event: str, callback: Callable[[Any], None]) -> None:
        pass

    def notify(self, event: str, data: Any) -> None:
        pass

[Domain][Journal] Incluir campos de dados não estruturados

O Journal deve manter os seguintes dados:

title = StringField()
title_iso = StringField()
short_title = StringField()
title_slug = StringField()
created = DateTimeField()
updated = DateTimeField()
acronym = StringField()
scielo_issn = StringField()
print_issn = StringField()
eletronic_issn = StringField()
current_status = StringField()
is_public = BooleanField(default=True)
unpublish_reason = StringField()

[Journal] Remover Campos `unpublish_reason` e `is_public`

Remover o campo unpublish_reason, esse campo é so utilizado para saber se o periodico deve ser mostrado no site. Ele so foi implementado devido ao lote de campo do ticker #8

remover trecho do codigo abaixo e seus testes tambem

https://github.com/scieloorg/document-store/blob/master/documentstore/domain.py#L619

    @property
    def unpublish_reason(self):
        return BundleManifest.get_metadata(self._manifest, "unpublish_reason")

    @unpublish_reason.setter
    def unpublish_reason(self, value: str):
        _value = str(value)
        self.manifest = BundleManifest.set_metadata(
            self._manifest, "unpublish_reason", _value
        )

    @property
    def is_public(self):
        return BundleManifest.get_metadata(self._manifest, "is_public", True)

    @is_public.setter
    def is_public(self, value: bool):
        self.manifest = BundleManifest.set_metadata(self._manifest, "is_public", value)

https://github.com/scieloorg/document-store/blob/master/tests/test_domain.py#L835

 def test_unpublish_reason_is_empty_str(self):
        journal = domain.Journal(id="0034-8910-rsp-48-2")
        self.assertEqual(journal.unpublish_reason, "")

    def test_set_unpublish_reason(self):
        journal = domain.Journal(id="0034-8910-rsp-48-2")
        journal.unpublish_reason = "not-open-access"
        self.assertEqual(journal.unpublish_reason, "not-open-access")
        self.assertEqual(
            journal.manifest["metadata"]["unpublish_reason"],
            [("2018-08-05T22:33:49.795151Z", "not-open-access")],
        )

    def test_is_public_is_default_true(self):
        journal = domain.Journal(id="0034-8910-rsp-48-2")
        self.assertTrue(journal.is_public)

    def test_set_is_public(self):
        journal = domain.Journal(id="0034-8910-rsp-48-2")
        journal.is_public = True
        self.assertTrue(journal.is_public)
        self.assertEqual(
            journal.manifest["metadata"]["is_public"],
            [("2018-08-05T22:33:49.795151Z", True)],
        )

    def test_set_is_public_to_false(self):
        journal = domain.Journal(id="0034-8910-rsp-48-2")
        journal.is_public = False
        self.assertFalse(journal.is_public)
        self.assertEqual(
            journal.manifest["metadata"]["is_public"],
            [("2018-08-05T22:33:49.795151Z", False)],
        )

[Domain][Journal] Remoção de Documents Bundle

O Journal deve permitir que um bundle seja retirado da lista de bundles que o compõe. O bundle é representado aqui pelo id.

Exemplo da lista:

item = [
    "1",
    "2",
    "3",
]

Onde 1, 2 e 3 são ids de bundles.

[Domain][Journal] Incluir campo Próximo Título (next_journal)

O Journal deve manter campo para Próximo Periódico (previous_journal), que armazena o primeiro mais novo a ele. Este pode conter somente o próximo nome do periódico, nos casos onde o próximo não for publicado no SciELO, com o nome e a URL, quando o próximo for publicado no SciELO

Ex.:

"next_journal": {
    "title": "New Title of Journal",
}
"next_journal": {
    "title": "New Title of Journal",
    "id": "/journals/:journal_id",
}

Obter versão do documento em determinada data e hora

Deve ser possível realizar uma consulta do tipo:

GET /documents/0034-8910-rsp-48-2-0347?when=2018-08-05

onde o parâmetro when é um timestamp UTC referente ao estado desejado. O valor de when poderá ter uma resolução maior, como por exemplo:

GET /documents/0034-8910-rsp-48-2-0347?when=2018-08-05 23:09:42

Adicionar property issues no domínio Journal

Comportamento esperado

Ao acessar uma instância da classe Journal é possível acessar a propriedade .issues onde o retorno deve ser todas as issues já adicionadas para o journal ou uma lista vazia caso não haja issues atreladas ao journal.

Comportamento atual

N/A

[Services] Criar comandos para adicionar, inserir e remover fascículo em periódico

Deve existir comandos para adicionar, inserir e remover um fascículo em um periódico.

O comando adicionar deve receber o identificador do periódico e do fascículo, que deve ser adicionado ao final da lista de fascículos.

O comando inserir deve receber o identificador do periódico e do fascículo e a posição da lista onde o fascículo deve ser inserido na lista de fascículos.

O comando remover deve receber o identificador do periódico e do fascículo, que deve ser removido da lista de fascículos.

[Services] Criar comandos para set, get e remove de bundle de aop

Deve existir comandos para set, get e remove de um "bundle" de artigos ahead of print.

O comando set deve receber o identificador do periódico e do fascículo.

O comando get deve receber o identificador do periódico.

O comando remover deve receber o identificador do periódico.

Suportar registro de documentos sintetizados a partir das bases ISIS

Esses documentos seguem a especificação SciELO PS, da mesma maneira que qualquer outro, com a exceção de que o elemento body contém todo o conteúdo do artigo, obtido por meio de raspagem de dados na interface pública do site.

Exemplo: http://articlemeta.scielo.org/api/v1/article/?collection=scl&code=S0102-33062013000300005&body=true&format=xmlrsps

Ao registrar um documento deste tipo, o sistema deve identificar corretamente os identificadores dos ativos digitais associados e permitir que eles sejam substituídos durante a recuperação dos dados.

[Domain][Journal] Incluir campo de títulos paralelos (parallel_titles)

O Journal deve manter campo para Títulos Paralelos. Esses títulos são oficiais e são referenciados assim como o título original (traduções, outros nomes publicados em diferentes países etc.) e o ISSN é mantido. É diferente da alteração do título original, onde há alteração do registro.

É uma lista de títulos oficiais, ou seja, uma lista de strings, que inclusive pode ser vazia.

[Domain][Journal] Incluir campo para editor chefe

Esse campo eve conter uma lista com os dados dos editores chefes pelo journal

"editor_" : [ {
            "name": "Faculdade de Saúde Pública da Universidade de São Paulo",
            "address": "Avenida Dr. Arnaldo, 715\n01246-904 São Paulo SP Brazil",
            "phone_number": "+55 11 3061-7985",
            "email": "[email protected]"
}]

Escapar pontos (.) em chaves de dicionários que serão armazenados no MongoDB

Trata-se de uma limitação atual do MongoDB por conta da função especial que os caracteres "." e "$" possuem na navegação pela estrutura do registro e em consultas.

screen shot 2019-02-14 at 17 54 21
https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names

Cifrões nem tanto, mas pontos aparecerão frequentemente nas chaves dos ativos digitais, e.g., 1678-2674-acb-33-11-00975-gf1.jpg.

Relacionado: https://jira.mongodb.org/browse/SERVER-30575

Melhorar o Readme do Projeto.

Olhando o readme do projeto verifique que precisamos melhorar, até para que possamos ter contribuições de outras equipe e/ou pessoas.

[Domain][Journal] Incluir campo Periódico Anterior (previous_journal)

O Journal deve manter campo para Periódico Anterior (previous_journal). Este pode conter somente o nome anterior do periódico, nos casos onde o anterior não foi publicado no SciELO, com o nome e a URL, quando o anterior foi publicado no SciELO

Ex.:

"previous_journal": {
    "title": "Former Title of Journal",
}
"previous_journal": {
    "title": "Former Title of Journal",
    "id": "/journals/:journal_id",
}

Retornar o *front* do documento estruturado de maneira consistente

São muitas as maneiras de representarmos os mesmos dados em XML SciELO PS. Essa diversidade se dá por conta das mudanças que a especificação SciELO PS sofreu ao longo do tempo e também pela flexibilidade garantida pela norma JATS. De qualquer maneira, para o uso efetivo dos dados é necessário abstrairmos essas variações e produzirmos uma estrutura regular.

A proposta deste ticket é de criarmos o serviço:

GET /documents/:document_id/front: retorna o front-matter do artigo codificado em JSON seguindo uma estrutura regular ainda a definir. A sugestão é que seja usada a lib https://github.com/scieloorg/clea como base para a implementação.

Introduzir suporte ao conceito de arquivo de documentos

O arquivo de documentos é uma maneira de agrupar documentos de maneira hierárquica. O schema proposto aqui sugere que a estrutura hierárquica seja representada por meio do valor da chave label, onde o caractere @ funcionaria como separador conforme exemplos 2018@1, 2018@2, 2018@1@1 etc.

Os serviços propostos são:

  • GET /archives/:journal_id/: retorna o arquivo do periódico referenciado por :journal_id.

Sugestão de esquema de dados para Números:

// exemplo para vol.70 no.5 Belo Horizonte Sept./Oct. 2018
{"timestamp": "2018-09-05T23:03:44.971230Z", "label": "2018@70@5", "docs": []}
// exemplo para vol.71 supl.3 Brasília  2018
{"timestamp": "2018-09-05T23:03:44.971230Z", "label": "2018@[email protected]", "docs": []}
// exemplo para vol.66 no.spe Brasília Sept. 2013
{"timestamp": "2018-09-05T23:03:44.971230Z", "label": "2018@66@spe", "docs": []}
// exemplo para vol.43 no.1-2-3-4 Brasília Jan./Dec. 1990
{"timestamp": "2018-09-05T23:03:44.971230Z", "label": "1990@43@1-2-3-4", "docs": []}
// exemplo para no.18 Guarulhos Jan./Apr. 2018
{"timestamp": "2018-09-05T23:03:44.971230Z", "label": "2018@@18", "docs": []}
// exemplo para ahead-of-print
{"timestamp": "2018-09-05T23:03:44.971230Z", "label": "nahead", "docs": []}

Repare que no campo label o caráctere @ é utilizado como separador de volume e número. Os documentos são agrupados pela estrutura descrita no campo label.

[Domain][Journal] Incluir campo para dados de contato do periódico (`contact`)

O Journal deve manter campo para os dados de contato do periódico (endereço, telefone, email). Estes dados devem ser exibidos no rodapé da Home do periódico no site do OPAC (que hoje exibe os dados de contato da casa publicadora (publisher))

exemplo de dado para o campo:

"contact": {
            "name": "Faculdade de Saúde Pública da Universidade de São Paulo",
            "country": "Brasil",
            "state": "SP",
            "city": "São Paulo",
            "address": "Avenida Dr. Arnaldo, 715\n01246-904 São Paulo SP Brazil",
            "phone_number": "+55 11 3061-7985",
            "email": "[email protected]",
            "enable_contact": "true"
} 

[Domain][Journal] Incluir campo para métricas

O Journal deve manter campo para as métricas do periódico. No site novo, elas são exibidas da seguinte forma:
screen shot 2019-01-31 at 11 49 45

O valor a ser esperado do campo metrica é um dicionário onde sua chaves serão o nome da metrica o valor sera uma estrutura de dado flexível (list, dict, string) com os dados que sera usados pelas aplicação

{
    "scimago": {
        "url": "http://scimago.org"
        "title": "Scimago"
    },
    "google": {
        "total_h5": 10,
        "h5_median": 5,
        "h5_year": 2018
    },
    "scielo": "valor medio"
} 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.