Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: build xml elements #720

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 43 additions & 18 deletions packtools/sps/formats/sps_xml/contrib.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,70 @@


def build_contrib_author(data):
# Garantir que o contrib_type seja uma string válida ou usar 'author' como padrão
contrib_type = data.get("contrib_type") or ""
# Garantir que o contrib_type seja uma string válida
contrib_type = data.get("contrib_type")
if contrib_type is None:
raise KeyError("contrib-type is required")

# Criação do elemento principal <contrib> com o tipo de contribuição
contrib_elem = ET.Element("contrib", attrib={"contrib-type": contrib_type})

# Adiciona os elementos <contrib-id> para ORCID e Scopus, se existirem no dicionário
for contrib_id_type, contrib_id_value in data.get('contrib_ids', {}).items():
contrib_id_elem = ET.Element("contrib-id", attrib={"contrib-id-type": contrib_id_type})
contrib_id_elem.text = contrib_id_value
contrib_elem.append(contrib_id_elem)
if 'contrib_ids' in data:
if data.get('contrib_ids') is None:
raise KeyError("contrib-id-type is required")
else:
for contrib_id_type, contrib_id_value in (data['contrib_ids'] or {}).items():
contrib_id_elem = ET.Element("contrib-id", attrib={"contrib-id-type": contrib_id_type})
contrib_id_elem.text = contrib_id_value
contrib_elem.append(contrib_id_elem)

if "collab" in data:
collab_elem = ET.Element("collab")
collab_elem.text = data["collab"]
contrib_elem.append(collab_elem)

# Cria e adiciona o <name> com <surname> e <given-names> somente se existirem no dicionário
if "surname" in data or "given_names" in data:
if any(key in data for key in ("surname", "given_names", "prefix", "suffix")):
Copy link
Member

@robertatakenaka robertatakenaka Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Rossi-Luciano
Pode usar o seguinte:

if any(data.get(key) for key in ("surname", "given_names", "prefix", "suffix")):

name_elem = ET.Element("name")

if "surname" in data: # Adiciona <surname> somente se estiver presente no dicionário
surname_elem = ET.Element("surname")
surname_elem.text = data.get("surname", "") # Mesmo se for vazio, cria o elemento
surname_elem.text = data["surname"] # Mesmo se for vazio, cria o elemento
name_elem.append(surname_elem)

if "given_names" in data: # Adiciona <given-names> somente se estiver presente no dicionário
given_names_elem = ET.Element("given-names")
given_names_elem.text = data.get("given_names", "") # Mesmo se for vazio, cria o elemento
given_names_elem.text = data["given_names"] # Mesmo se for vazio, cria o elemento
name_elem.append(given_names_elem)

if "prefix" in data: # Adiciona <prefix> somente se estiver presente no dicionário
prefix_elem = ET.Element("prefix")
prefix_elem.text = data["prefix"] # Mesmo se for vazio, cria o elemento
name_elem.append(prefix_elem)

if "suffix" in data: # Adiciona <suffix> somente se estiver presente no dicionário
suffix_elem = ET.Element("suffix")
suffix_elem.text = data["suffix"] # Mesmo se for vazio, cria o elemento
name_elem.append(suffix_elem)

contrib_elem.append(name_elem)

# Adiciona o <xref> para afiliação apenas se rid ou text forem fornecidos no dicionário
for xref_data in data.get("affiliations", []):
rid = xref_data.get("rid")
text = xref_data.get("text")
if rid is not None or text is not None: # Certifica-se de que pelo menos um não seja None
xref_elem = ET.Element("xref", attrib={"ref-type": "aff"})
if rid is not None:
xref_elem.set("rid", rid)
if text is not None:
xref_elem.text = text
if "affiliations" in data:
if data.get("affiliations") is None:
xref_elem = ET.Element("xref")
contrib_elem.append(xref_elem)
else:
for xref_data in data.get("affiliations") or []:
# rid é obrigatório, a ausência da chave deveria levantar exceção
try:
xref_elem = ET.Element("xref", attrib={"ref-type": "aff", "rid": xref_data["rid"]})
xref_elem.text = xref_data["text"]
except KeyError as e:
raise KeyError(f"{e} is required")

contrib_elem.append(xref_elem)

return contrib_elem

126 changes: 71 additions & 55 deletions tests/sps/formats/sps_xml/test_contrib.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ def setUp(self):
"orcid": "0000-0001-8528-2091",
"scopus": "24771926600"
},
"prefix": "Prof.",
"surname": "Einstein",
"given_names": "Albert",
"suffix": "Neto",
"affiliations": [
{"rid": "aff1", "text": "1"}
]
Expand All @@ -40,8 +42,12 @@ def test_contrib_author_name(self):
self.assertIsNotNone(name_elem)
surname_elem = name_elem.find("surname")
given_names_elem = name_elem.find("given-names")
prefix_elem = name_elem.find("prefix")
suffix_elem = name_elem.find("suffix")
self.assertEqual(surname_elem.text, "Einstein")
self.assertEqual(given_names_elem.text, "Albert")
self.assertEqual(prefix_elem.text, "Prof.")
self.assertEqual(suffix_elem.text, "Neto")

def test_contrib_author_affiliations(self):
xref_elems = self.contrib_elem.findall("xref")
Expand All @@ -51,7 +57,18 @@ def test_contrib_author_affiliations(self):
self.assertEqual(xref_elem.get("rid"), "aff1")
self.assertEqual(xref_elem.text, "1")

def test_contrib_author_collab(self):
data = {
"contrib_type": "author",
"collab": "The Brazil Flora Group"
}
contrib_elem = build_contrib_author(data)
self.assertEqual(len(contrib_elem), 1)
self.assertEqual(contrib_elem.get("contrib-type"), "author")
self.assertEqual(contrib_elem.find("collab").text, "The Brazil Flora Group")

def test_contrib_author_as_string(self):
self.maxDiff = None
# Gera o XML a partir dos dados válidos
contrib_elem = build_contrib_author(self.valid_data)

Expand All @@ -66,6 +83,8 @@ def test_contrib_author_as_string(self):
'<name>'
'<surname>Einstein</surname>'
'<given-names>Albert</given-names>'
'<prefix>Prof.</prefix>'
'<suffix>Neto</suffix>'
'</name>'
'<xref ref-type="aff" rid="aff1">1</xref>'
'</contrib>'
Expand All @@ -76,10 +95,8 @@ def test_contrib_author_as_string(self):


class TestBuildContribAuthorNoneValues(unittest.TestCase):

def setUp(self):
# Dados de exemplo para o teste com valores nulos
self.valid_data = {
"""
{
"contrib_type": None,
"contrib_ids": {
"orcid": None,
Expand All @@ -91,75 +108,74 @@ def setUp(self):
{"rid": None, "text": None}
]
}
"""

def _build_and_compare(self, expected_xml_str):
"""Método auxiliar para construção e comparação do XML gerado"""
contrib_elem = build_contrib_author(self.valid_data)
generated_xml_str = ET.tostring(contrib_elem, encoding="unicode", method="xml")
self.assertEqual(generated_xml_str.strip(), expected_xml_str.strip())
def test_contrib_author_contrib_type_None(self):
data = {
"contrib_type": None
}

def test_contrib_author_with_all_null_values(self):
expected_xml_str = (
'<contrib contrib-type="">'
'<contrib-id contrib-id-type="orcid" />'
'<contrib-id contrib-id-type="scopus" />'
'<name>'
'<surname />'
'<given-names />'
'</name>'
'</contrib>'
)
self._build_and_compare(expected_xml_str)
with self.assertRaises(KeyError) as e:
build_contrib_author(data)

def test_contrib_author_without_contrib_id(self):
self.valid_data.pop("contrib_ids")
expected_xml_str = (
'<contrib contrib-type="">'
'<name>'
'<surname />'
'<given-names />'
'</name>'
'</contrib>'
)
self._build_and_compare(expected_xml_str)
self.assertEqual(str(e.exception), "'contrib-type is required'")

def test_contrib_author_contrib_ids_None(self):
data = {
"contrib_type": "author",
"contrib_ids": None
}
with self.assertRaises(KeyError) as e:
build_contrib_author(data)

self.assertEqual(str(e.exception), "'contrib-id-type is required'")

def test_contrib_author_without_contrib_id_type(self):
self.valid_data["contrib_ids"].pop("orcid")
def test_contrib_author_contrib_ids_attrib_None(self):
data = {
"contrib_type": "author",
"contrib_ids": {
"orcid": None,
},
}
expected_xml_str = (
'<contrib contrib-type="">'
'<contrib-id contrib-id-type="scopus" />'
'<name>'
'<surname />'
'<given-names />'
'</name>'
'<contrib contrib-type="author">'
'<contrib-id contrib-id-type="orcid" />'
'</contrib>'
)
self._build_and_compare(expected_xml_str)
contrib_elem = build_contrib_author(data)
generated_xml_str = ET.tostring(contrib_elem, encoding="unicode", method="xml")
self.assertEqual(generated_xml_str.strip(), expected_xml_str.strip())

def test_contrib_author_without_contrib_ids(self):
self.valid_data.pop("contrib_ids")
def test_contrib_author_contrib_surname_None(self):
# vale para os demais elementos em <name>
data = {
"contrib_type": "author",
"surname": None,
}
expected_xml_str = (
'<contrib contrib-type="">'
'<contrib contrib-type="author">'
'<name>'
'<surname />'
'<given-names />'
'</name>'
'</contrib>'
)
self._build_and_compare(expected_xml_str)
contrib_elem = build_contrib_author(data)
generated_xml_str = ET.tostring(contrib_elem, encoding="unicode", method="xml")
self.assertEqual(generated_xml_str.strip(), expected_xml_str.strip())

def test_contrib_author_without_surname(self):
self.valid_data.pop("surname")
def test_contrib_author_contrib_affiliations_None(self):
data = {
"contrib_type": "author",
"affiliations": None,
}
expected_xml_str = (
'<contrib contrib-type="">'
'<contrib-id contrib-id-type="orcid" />'
'<contrib-id contrib-id-type="scopus" />'
'<name>'
'<given-names />'
'</name>'
'<contrib contrib-type="author">'
'<xref />'
'</contrib>'
)
self._build_and_compare(expected_xml_str)
contrib_elem = build_contrib_author(data)
generated_xml_str = ET.tostring(contrib_elem, encoding="unicode", method="xml")
self.assertEqual(generated_xml_str.strip(), expected_xml_str.strip())


if __name__ == '__main__':
Expand Down