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

lint

parent 0b0e44d1
Branches
Tags
No related merge requests found
[pydocstyle]
ignore = D104,D105,D107,D203,D204,D213
# D104: Missing docstring in public package
# D105 Missing docstring in magic method
# D107 Missing docstring in __init__
# D203 1 blank line required before class docstring
# D204 1 blank line required after class docstring (found 0)
# D213 Multi-line docstring summary should start at the second line
\ No newline at end of file
[MESSAGES CONTROL]
disable=no-self-use,too-many-instance-attributes
good-names=id, i
""""Paper surfer - browse papers posted on the mattermost channel.
"""Paper surfer - browse papers posted on the mattermost channel.
UI:
......@@ -16,6 +16,7 @@ from functools import partial
import json
import time
import os
import sys
import requests
import mattermostdriver
import urwid
......@@ -28,7 +29,7 @@ class ConfigError(Exception):
@dataclass
class PostDTO:
""""Encapsulate Mattermost Posts."""
"""Encapsulate Mattermost Posts."""
id: str
message: str
reporter: str
......@@ -40,7 +41,7 @@ class PostDTO:
@dataclass
class PaperDTO:
""""Encapsulate Paper meta data."""
"""Encapsulate Paper meta data."""
author: str
authors: str
title: str
......@@ -52,20 +53,24 @@ class PaperDTO:
class Bibtex:
"""Interface for bibtex string."""
def entry_from_doi(self, doi):
"""Get bibtex string for doi."""
return Doi().get_bibtex(doi)
def bib_from_dois(self, dois):
"""Get bibtex string for mulitple dois."""
return "\n".join([Doi().get_bibtex(doi) for doi in dois])
class Doi:
"""Interface w/ the doi.org api"""
"""Interface w/ the doi.org api."""
def get_doi_link(self, doi):
"""Assemble doi link."""
return f"http://doi.org/{doi}"
def load_doi_data(self, doi):
"""Load data for doi."""
headers = {
'Accept': 'application/json',
}
......@@ -73,7 +78,7 @@ class Doi:
headers=headers).content
def parse_doi_json(self, jsoncontent):
"""Tranform doi json to PaperDTO"""
"""Tranform doi json to PaperDTO."""
info = json.loads(jsoncontent)
with open("debug.json", "w") as file:
......@@ -87,8 +92,7 @@ class Doi:
if "author" in info
else "Authors N/A")
title = (info['title']
if "title" in info
and isinstance(info['title'], str)
if "title" in info and isinstance(info['title'], str)
else "Title N/A")
journal = (info['publisher']
if "publisher" in info
......@@ -105,12 +109,14 @@ class Doi:
slug)
def get_bibtex(self, doi):
"""Get bibtex string for doi."""
headers = {
'Accept': 'text/bibliography; style=bibtex',
}
return requests.get(f'http://dx.doi.org/{doi}', headers=headers).text
def get_info(self, doi):
"""Get information for doi."""
try:
jsoncontent = self.load_doi_data(doi)
data = self.parse_doi_json(jsoncontent)
......@@ -132,6 +138,7 @@ class Doi:
class Mattermost:
"""Provide a simplified interaction w/ mattermost api."""
def __init__(self, url, channelname, username, password):
self.msgs = []
self.mattermost = mattermostdriver.Driver({
'url': url,
'login_id': username,
......@@ -155,13 +162,14 @@ class Mattermost:
self.reporters = {}
def get_channel(self, channelname):
""""Try to find the paper channel by display name."""
mm = self.mattermost
teams = [team["id"] for team in mm.teams.get_user_teams("me")]
"""Try to find the paper channel by display name."""
teamapi = self.mattermost.teams
channelapi = self.mattermost.channels
teams = [team["id"] for team in teamapi.get_user_teams("me")]
channels = []
for team in teams:
teamchannels = [channel for channel
in mm.channels.get_channels_for_user("me", team)
in channelapi.get_channels_for_user("me", team)
if channel["display_name"] == channelname]
channels.extend(teamchannels)
......@@ -171,22 +179,20 @@ class Mattermost:
raise ConfigError
return channels[0]["id"]
def get_reporter(self, id):
def get_reporter(self, userid):
"""Load user from mattermost api and cache."""
if id not in self.reporters:
self.reporters[id] = self.mattermost.users.get_user(id)["username"]
userapi = self.mattermost.users
if userid not in self.reporters:
self.reporters[userid] = userapi.get_user(userid)["username"]
return self.reporters[id]
return self.reporters[userid]
def retrieve_all_messages(self):
"""Retrieve all messages from mattermost, unfiltered for papers."""
posts = self.mattermost.posts.get_posts_for_channel(self.channel)
return [PostDTO(
id=m['id'],
message=m['message'],
return [PostDTO(id=m['id'], message=m['message'],
reporter=self.get_reporter(m['user_id']),
doi=Doi().extract_doi(m['message']),
)
doi=Doi().extract_doi(m['message']),)
for m in posts['posts'].values()]
def filter_incoming(self, posts):
......@@ -200,6 +206,7 @@ class Mattermost:
return self.msgs
def check_doi_exits(self, doi):
"""Check for doi in current paper list."""
doi_needle = Doi().extract_doi(doi)
msg_found = [msg for msg in self.msgs
if Doi().extract_doi(msg.doi) == doi_needle]
......@@ -212,12 +219,15 @@ class Mattermost:
or needle.lower() in m.reporter.lower()]
def post(self, message):
"""Post message to thread."""
self.mattermost.posts.create_post({"channel_id": self.channel,
"message": message})
class PrettyButton(urwid.WidgetWrap):
"""Prettified urwid Button."""
def __init__(self, label, on_press=None, user_data=None):
self.label = ""
self.text = urwid.Text("")
self.set_label(label)
self.widget = urwid.AttrMap(self.text, '', 'highlight')
......@@ -226,21 +236,26 @@ class PrettyButton(urwid.WidgetWrap):
self._hidden_btn = urwid.Button(f"hidden {self.label}",
on_press, user_data)
super(self.__class__, self).__init__(self.widget)
super().__init__(self.widget)
def selectable(self):
"""Make button selectable."""
return True
def keypress(self, *args, **kw):
"""Handle keypresses."""
return self._hidden_btn.keypress(*args, **kw)
def mouse_event(self, *args, **kw):
"""Handle mouse events."""
return self._hidden_btn.mouse_event(*args, **kw)
def get_label(self):
"""Return current input label."""
return self.label
def set_label(self, label):
"""Return current input label."""
self.label = label
self.text.set_text(f"[ {label} ]")
......@@ -308,16 +323,19 @@ class Papersurfer:
self.mainloop.run()
def h_unhandled_input(self, key):
"""Handle keyboard input not otherwise handled."""
if key == "esc":
raise urwid.ExitMainLoop()
def load_list(self, _loop, _data):
"""Load and display paper list."""
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):
"""Create loading indicator dialog."""
body_text = urwid.Text("Loading...", align='center')
body_filler = urwid.Filler(body_text, valign='middle')
body_padding = urwid.Padding(
......@@ -329,6 +347,7 @@ class Papersurfer:
return urwid.Frame(body_padding)
def details_popup(self, paper):
"""Create Dialog with paper details."""
header_text = urwid.Text(('banner', 'Paper details'), align='center')
header = urwid.AttrMap(header_text, 'banner')
......@@ -391,7 +410,7 @@ class Papersurfer:
return pile
def updscrn(self):
""""Update (redraw) screen."""
"""Update (redraw) screen."""
self.mainloop.draw_screen()
def onchange(self, _, needle):
......@@ -402,6 +421,7 @@ class Papersurfer:
for paper in self.mtm.get_filtered(needle)])
def running_export(self, state):
"""Set exporting state."""
btn = self.exportbutton
label = btn.get_label()
running_indicator = " (running...)"
......@@ -422,6 +442,7 @@ class Papersurfer:
self.running_export(False)
def export_to_bibtex(self):
"""Export current filtered list to bibtex file."""
papers = self.mtm.get_filtered(self.filter)
dois = [paper.doi for paper in papers]
string = Bibtex().bib_from_dois(dois)
......@@ -455,13 +476,15 @@ class Papersurfer:
self.mainloop.widget = self.details_popup(paper)
def h_close_dialog(self, _):
"""Handle close dialog button."""
self.close_dialog()
def close_dialog(self):
"""Close currently open dialog."""
self.mainloop.widget = self.top
def open_submit_paper(self, _):
"""Open submit paper dialog."""
self._pile = urwid.Pile(
[
PostDialog(self.mtm, close=self.h_close_dialog)
......@@ -494,7 +517,6 @@ def get_config_file_paths():
>>> type(get_config_file_paths())
<class 'list'>
"""
env = os.environ
xdg_home = None
if 'XDG_CONFIG_HOME' in env:
......@@ -517,6 +539,7 @@ def get_config_file_paths():
def interactive_configuration():
"""Query user for credentials."""
url = input("Mattermost URL (eg. mattermost.example.net): ")
channel = input("Channel (eg. Paper Club): ")
username = input("Username (same as mattermost login, "
......@@ -526,7 +549,8 @@ def interactive_configuration():
class PostDialog(urwid.WidgetWrap):
"""
"""Dialog to submit a new paper to mattermost thread.
UI:
DOI: [ _________________]
Generated Message:
......@@ -535,6 +559,8 @@ class PostDialog(urwid.WidgetWrap):
[Submit] [Close]
"""
def __init__(self, mattermost, close):
self.doi = None
self.msg = None
self.mattermost = mattermost
self.close = close
self.doi_input = urwid.Edit("Doi: ")
......@@ -558,21 +584,21 @@ class PostDialog(urwid.WidgetWrap):
right=1
)
body = urwid.LineBox(body_padding)
frame = urwid.Frame(
body,
header=urwid.Text("Submit new paper to list"),
)
frame = urwid.Frame(body,
header=urwid.Text("Submit new paper to list"))
self.widget = frame
super(self.__class__, self).__init__(self.widget)
super().__init__(self.widget)
def submit(self, _):
"""Submit post to thread."""
if not self.mattermost.check_doi_exits(self.doi):
self.mattermost.post(self.msg)
self.close(_)
def create_mgs(self, paper):
"""Format post message."""
msg = f"""\
{paper.title}
{paper.authors}
......@@ -581,6 +607,7 @@ class PostDialog(urwid.WidgetWrap):
return msg
def h_input(self, _, doi):
"""Handle doi input field."""
self.doi_result.set_text("... loading ...")
self.doi = None
self.msg = None
......@@ -602,8 +629,8 @@ class PostDialog(urwid.WidgetWrap):
def parse_args():
"""Parse command line arguments and config file."""
parser = configargparse.ArgParser()
parser._default_config_files = get_config_file_paths()
parser = configargparse.ArgParser(
default_config_files=get_config_file_paths())
parser.add("-w", "--write-out-config-file",
help="takes the current command line args and writes them out "
"to a config file at the given path",
......@@ -630,7 +657,7 @@ def parse_args():
Mattermost(url, channel, username, password)
except ConfigError:
print("Failed to validate configuration, exiting.")
exit(1)
sys.exit(1)
options.url = url
options.channel = channel
......@@ -648,7 +675,7 @@ def parse_args():
time.sleep(2)
else:
parser.print_help()
exit(0)
sys.exit(0)
return options
......@@ -661,12 +688,14 @@ def just_papers(url, channel, username, password):
def just_bibtex(url, channel, username, password):
"""Retrieve and dump bibtext formated data, unfiltered."""
posts = Mattermost(url, channel, username, password).retrieve()
dois = [post.doi for post in posts]
print(Bibtex().bib_from_dois(dois))
def main():
"""Run main program."""
opt = parse_args()
if opt.dump_posts:
just_papers(opt.url, opt.channel, opt.username, opt.password)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment