Skip to content
Snippets Groups Projects
Commit 39071dba authored by Johann Jacobsohn's avatar Johann Jacobsohn
Browse files

add loading indicator and info popup for papers with data from doi.org

parent 2c52b3cf
Branches
Tags
No related merge requests found
...@@ -13,12 +13,11 @@ import subprocess ...@@ -13,12 +13,11 @@ import subprocess
from dataclasses import dataclass from dataclasses import dataclass
import re import re
from functools import partial from functools import partial
import xml.etree.ElementTree as ET import json
import requests import requests
from mattermostdriver import Driver from mattermostdriver import Driver
import urwid import urwid
import configargparse import configargparse
from pybtex.database import BibliographyData, Entry
URL = "mattermost.cen.uni-hamburg.de" URL = "mattermost.cen.uni-hamburg.de"
CHANNEL = "n5myem9yc7fyzb9am7ym5o41ry" CHANNEL = "n5myem9yc7fyzb9am7ym5o41ry"
...@@ -37,9 +36,11 @@ class PostDTO: ...@@ -37,9 +36,11 @@ class PostDTO:
class PaperDTO: class PaperDTO:
""""Encapsulate Mattermost Posts.""" """"Encapsulate Mattermost Posts."""
author: str author: str
authors: str
title: str title: str
journal: str journal: str
year: int year: int
abstract: str
doi: str doi: str
...@@ -59,21 +60,39 @@ class Doi: ...@@ -59,21 +60,39 @@ class Doi:
def load_doi_data(self, doi): def load_doi_data(self, doi):
headers = { headers = {
'Accept': 'application/vnd.crossref.unixsd+xml', 'Accept': 'application/json',
} }
return requests.get(f'http://dx.doi.org/{doi}', return requests.get(f'http://dx.doi.org/{doi}',
headers=headers).content headers=headers).content
def parse_doi_xml(self, xml): def parse_doi_json(self, jsoncontent):
root = ET.fromstring(xml) """Tranform doi json to PaperDTO"""
scheme = ".//{http://www.crossref.org/xschema/1.1}" info = json.loads(jsoncontent)
author = root.find(f"{scheme}surname").text # fixme
title = root.find(f"{scheme}title").text with open("debug.json", "w") as file:
journal = root.find(f"{scheme}full_title").text file.write(json.dumps(info))
year = root.find(f"{scheme}year").text
doi = root.find(f"{scheme}doi").text author = (f"{info['author'][0]['given']} {info['author'][0]['family']}"
if "author" in info
return PaperDTO(author, title, journal, year, doi) else "Author N/A")
authors = (", ".join([f"{a['given']} {a['family']}"
for a in info['author']])
if "author" in info
else "Authors N/A")
title = (info['title']
if "title" in info
and isinstance(info['title'], str)
else "Title N/A")
journal = (info['publisher']
if "publisher" in info
else "Journal N/A")
year = info['created']['date-parts'][0][0]
doi = info['DOI']
abstract = (info['abstract']
if "abstract" in info
else "Abstract N/A")
return PaperDTO(author, authors, title, journal, year, abstract, doi)
def get_bibtex(self, doi): def get_bibtex(self, doi):
headers = { headers = {
...@@ -82,8 +101,8 @@ class Doi: ...@@ -82,8 +101,8 @@ class Doi:
return requests.get(f'http://dx.doi.org/{doi}', headers=headers).text return requests.get(f'http://dx.doi.org/{doi}', headers=headers).text
def get_info(self, doi): def get_info(self, doi):
xml = self.load_doi_data(doi) jsoncontent = self.load_doi_data(doi)
return self.parse_doi_xml(xml) return self.parse_doi_json(jsoncontent)
def extract_doi(self, hay): def extract_doi(self, hay):
"""Parse doi from string, or None if not found. """Parse doi from string, or None if not found.
...@@ -151,16 +170,6 @@ class PrettyButton(urwid.Button): ...@@ -151,16 +170,6 @@ class PrettyButton(urwid.Button):
super(self.__class__, self).__init__(*args, **kwargs) super(self.__class__, self).__init__(*args, **kwargs)
urwid.connect_signal(self, 'click', onclick) urwid.connect_signal(self, 'click', onclick)
def button(self, label, onclick):
"""Render a pretty button."""
btn = urwid.Button(label)
urwid.connect_signal(btn, 'click', onclick)
wrapper = urwid.AttrMap(btn, '', 'highlight')
padding = urwid.Padding(wrapper, left=4, right=4)
return padding, btn
class Papersurfer: class Papersurfer:
"""Provide UI and interface with mattermost class.""" """Provide UI and interface with mattermost class."""
...@@ -173,6 +182,10 @@ class Papersurfer: ...@@ -173,6 +182,10 @@ class Papersurfer:
('I say', 'default,bold', 'default', 'bold'), ('I say', 'default,bold', 'default', 'bold'),
('needle', 'default, bold, underline', 'default', 'bold'), ('needle', 'default, bold, underline', 'default', 'bold'),
('highlight', 'black', 'dark blue'), ('highlight', 'black', 'dark blue'),
('banner', 'black', 'light gray'),
('selectable', 'white', 'black'),
('focus', 'black', 'light gray'),
('papertitle', 'default,bold', 'default', 'bold')
] ]
ask = urwid.Edit(('I say', u"Filter?\n")) ask = urwid.Edit(('I say', u"Filter?\n"))
exitbutton = PrettyButton(u'Exit', onclick=self.on_exit_clicked) exitbutton = PrettyButton(u'Exit', onclick=self.on_exit_clicked)
...@@ -182,20 +195,87 @@ class Papersurfer: ...@@ -182,20 +195,87 @@ class Papersurfer:
self.mtm = Mattermost(username, password) self.mtm = Mattermost(username, password)
body = [self.list_item(paper) for paper in self.mtm.retrieve()] body = [urwid.Text("")]
self.listcontent = urwid.SimpleFocusListWalker(body) self.listcontent = urwid.SimpleFocusListWalker(body)
paperlist = urwid.BoxAdapter(urwid.ListBox(self.listcontent), paperlist = urwid.BoxAdapter(urwid.ListBox(self.listcontent),
self.size[1] - 5) self.size[1] - 5)
pile = urwid.Pile([ask, div, paperlist, div, pile = urwid.Pile([ask, div, paperlist, div,
urwid.Columns([exitbutton, self.exportbutton])]) urwid.Columns([exitbutton, self.exportbutton])])
top = urwid.Filler(pile, valign='middle') self.top = urwid.Filler(pile, valign='middle')
self._pile = urwid.Pile(
[
self.loading_indicator()
]
)
self._over = urwid.Overlay(
self._pile,
self.top,
align='center',
valign='middle',
width=20,
height=10
)
urwid.connect_signal(ask, 'change', self.onchange) urwid.connect_signal(ask, 'change', self.onchange)
self.main = pile
self.mainloop = urwid.MainLoop(top, palette) self.mainloop = urwid.MainLoop(self._over, palette)
self.mainloop.set_alarm_in(.1, self.load_list)
self.mainloop.run() self.mainloop.run()
def load_list(self, _loop, _data):
body = [self.list_item(paper) for paper in self.mtm.retrieve()]
self.listcontent.clear()
self.listcontent.extend(body)
self.mainloop.widget = self.top
def loading_indicator(self):
body_text = urwid.Text("Loading...", align='center')
body_filler = urwid.Filler(body_text, valign='middle')
body_padding = urwid.Padding(
body_filler,
left=1,
right=1
)
return urwid.Frame(body_padding)
def details_popup(self, paper):
header_text = urwid.Text(('banner', 'Paper details'), align='center')
header = urwid.AttrMap(header_text, 'banner')
body_pile = urwid.Pile([
urwid.Text(("papertitle", paper.title)),
urwid.Text(paper.authors),
urwid.Text(paper.journal),
urwid.Text(paper.doi),
urwid.Text(paper.abstract),
urwid.Text(" "),
urwid.Text(Bibtex().entry_from_doi(paper.doi)),
])
body_filler = urwid.Filler(body_pile, valign='top')
body_padding = urwid.Padding(
body_filler,
left=1,
right=1
)
body = urwid.LineBox(body_padding)
# Footer
footer = urwid.Button('Okay', self.close_details)
footer = urwid.AttrWrap(footer, 'selectable', 'focus')
footer = urwid.GridFlow([footer], 8, 1, 1, 'center')
# Layout
layout = urwid.Frame(
body,
header=header,
footer=footer,
focus_part='footer'
)
return layout
def list_item(self, paper, needle=""): def list_item(self, paper, needle=""):
"""Create highlighted text entry.""" """Create highlighted text entry."""
text_items = [] text_items = []
...@@ -214,7 +294,12 @@ class Papersurfer: ...@@ -214,7 +294,12 @@ class Papersurfer:
paper)) paper))
doi_button = PrettyButton("Open DOI", doi_button = PrettyButton("Open DOI",
onclick=partial(self.h_open_doi, paper)) onclick=partial(self.h_open_doi, paper))
button_bar = urwid.Columns([discuss_button, doi_button]) details_button = PrettyButton("Show details",
onclick=partial(self.h_show_details,
paper))
button_bar = urwid.Columns([
discuss_button, doi_button, details_button])
pile = urwid.Pile([title, button_bar, urwid.Divider()]) pile = urwid.Pile([title, button_bar, urwid.Divider()])
return pile return pile
...@@ -264,6 +349,10 @@ class Papersurfer: ...@@ -264,6 +349,10 @@ class Papersurfer:
"""Handle click/enter on doi button.""" """Handle click/enter on doi button."""
self.open_doi(post) self.open_doi(post)
def h_show_details(self, post, _):
"""Handle click/enter on doi button."""
self.show_details(post)
def open_discussion(self, post): def open_discussion(self, post):
"""Open Mattermost post in browser.""" """Open Mattermost post in browser."""
link = f"https://mattermost.cen.uni-hamburg.de/ifg/pl/{post.id}" link = f"https://mattermost.cen.uni-hamburg.de/ifg/pl/{post.id}"
...@@ -273,6 +362,14 @@ class Papersurfer: ...@@ -273,6 +362,14 @@ class Papersurfer:
"""Open paper page in browser.""" """Open paper page in browser."""
subprocess.call(["xdg-open", Doi().get_doi_link(post.doi)]) subprocess.call(["xdg-open", Doi().get_doi_link(post.doi)])
def show_details(self, post):
"""Open paper page in browser."""
paper = Doi().get_info(post.doi)
self.mainloop.widget = self.details_popup(paper)
def close_details(self, _):
self.mainloop.widget = self.top
def parse_args(): def parse_args():
"""Parse command line arguments and config file.""" """Parse command line arguments and config file."""
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment