Merge branch 'texnote' into develop

main
Fabien Benureau 12 years ago
commit 7a21780ec9

@ -1,7 +1,10 @@
from .. import repo from .. import repo
from .. import color from .. import color
from .. import configs
from .helpers import add_references_argument, parse_references from .helpers import add_references_argument, parse_references
from ..events import RemoveEvent
def parser(subparsers, config): def parser(subparsers, config):
parser = subparsers.add_parser('remove', help='removes a paper') parser = subparsers.add_parser('remove', help='removes a paper')
@ -18,4 +21,7 @@ def command(config, ui, references):
sure = ui.input_yn(question=are_you_sure, default='n') sure = ui.input_yn(question=are_you_sure, default='n')
if sure: if sure:
for c in citekeys: for c in citekeys:
rmevent = RemoveEvent(config, ui, c)
rmevent.send()
rp.remove(c) rp.remove(c)

@ -0,0 +1,34 @@
_listener = {}
class Event(object):
"""Base event that can be sent to listeners.
"""
def send(self):
""" This function sends the instance of the class, i.e. the event
to be sent, to all function that listen to it.
"""
if self.__class__.__name__ in _listener:
for f, args in _listener[self.__class__.__name__]:
f(self, *args)
@classmethod
def listen(cls, *args):
def wrap(f):
if cls.__name__ not in _listener:
_listener[cls.__name__] = []
_listener[cls.__name__].append((f, args))
# next step allow us to call the function itself without Event raised
def wrapped_f(*args):
f(*args)
return wrapped_f
return wrap
class RemoveEvent(Event):
def __init__(self, config, ui, citekey):
self.config = config
self.ui = ui
self.citekey = citekey

@ -8,6 +8,7 @@ import collections
from papers.ui import UI from papers.ui import UI
from papers import configs from papers import configs
from papers import commands from papers import commands
from papers import plugin
cmds = collections.OrderedDict([ cmds = collections.OrderedDict([
('init', commands.init_cmd), ('init', commands.init_cmd),
@ -29,18 +30,16 @@ config = configs.read_config()
ui = UI(config) ui = UI(config)
# Extend with plugin commands # Extend with plugin commands
plugs = configs.get_plugins(config) plugin.load_plugins(config, ui, configs.get_plugins(config))
for plugname in plugs: for p in plugin.get_plugins().values():
module_name = 'papers.plugins.' + plugname + '.' + plugname + '_cmd' cmds.update(collections.OrderedDict([(p.name, p)]))
plug = __import__(module_name, globals(), locals(), #
['parser', 'command'], -1)
cmds.update(collections.OrderedDict([(plugname, plug)]))
parser = argparse.ArgumentParser(description="research papers repository") parser = argparse.ArgumentParser(description="research papers repository")
subparsers = parser.add_subparsers(title="valid commands", dest="command") subparsers = parser.add_subparsers(title="valid commands", dest="command")
for cmd_mod in cmds.values(): for cmd_mod in cmds.values():
subparser = cmd_mod.parser(subparsers, config) subparser = cmd_mod.parser(subparsers, config) # why do we return the subparser ?
args = parser.parse_args() args = parser.parse_args()
args.config = config args.config = config

@ -0,0 +1,79 @@
import importlib
PLUGIN_NAMESPACE = 'plugs'
_classes = []
_instances = {}
class PapersPlugin(object):
"""The base class for all plugins. Plugins provide
functionality by defining a subclass of PapersPlugin and overriding
the abstract methods defined here.
"""
def __init__(self, config, ui):
"""Perform one-time plugin setup.
"""
self.name = self.__module__.split('.')[-1]
self.config = config
self.ui = ui
#config and ui and given again to stay consistent with the core papers cmd.
#two options:
#- create specific cases in script papers/papers
#- do not store self.config and self.ui and use them if needed when command is called
#this may end up with a lot of function with config/ui in argument
#or just keep it that way...
def parser(self, subparsers, config):
""" Should retrun the parser with plugins specific command.
This is a basic example
"""
parser = subparsers.add_parser(self.name, help="echo string in argument")
parser.add_argument('strings', nargs='*', help='the strings')
return parser
def command(self, config, ui, strings):
"""This function will be called with argument defined in the parser above
This is a basic example
"""
for s in strings:
print s
@classmethod
def get_instance(cls):
if cls in _instances:
return _instances[cls]
else:
raise RuntimeError("{} instance not created".format(cls.__name__))
def load_plugins(config, ui, names):
"""Imports the modules for a sequence of plugin names. Each name
must be the name of a Python module under the "PLUGIN_NAMESPACE" namespace
package in sys.path; the module indicated should contain the
PapersPlugin subclasses desired.
"""
for name in names:
modname = '%s.%s.%s.%s' % ('papers', PLUGIN_NAMESPACE, name, name)
try:
try:
namespace = importlib.import_module(modname)
except ImportError as exc:
# Again, this is hacky:
if exc.args[0].endswith(' ' + name):
ui.warning('plugin {} not found'.format(name))
else:
raise
else:
for obj in namespace.__dict__.values():
if isinstance(obj, type) and issubclass(obj, PapersPlugin) \
and obj != PapersPlugin:
_classes.append(obj)
_instances[obj] = obj(config, ui)
except:
ui.warning('error loading plugin {}'.format(name))
def get_plugins():
return _instances

@ -1,54 +1,68 @@
#import ConfigParser
#from ... import configs
#cfg = configs.read_config()
#TEXNOTE_SECTION = 'texnote'
#DEFAULT_EDIT_CMD = cfg.get(configs.MAIN_SECTION, 'edit-cmd')
#TODO file should not be created before the end of the process to ensure everything went ok
#TODO add subparser to have more feature
#TODO add clean command to wipe out any compilation file
#TODO add function to merge several texnote in one based on a research result
import os import os
import shutil import shutil
import subprocess import subprocess
from ... import repo from ... import repo
from ...paper import NoDocumentFile
from ... import configs from ... import configs
from ... import files from ... import files
from ...plugin import PapersPlugin
from ...commands.helpers import add_references_argument, parse_reference from ...commands.helpers import add_references_argument, parse_reference
from ...events import RemoveEvent
TEXNOTE_SECTION = 'texnote' TEXNOTE_SECTION = 'texnote'
TEXNOTE_SAMPLE_FILE = os.path.join(os.path.dirname(__file__), 'note_sample.tex') TEXNOTE_SAMPLE_FILE = os.path.join(os.path.dirname(__file__), 'note_sample.tex')
TEXNOTE_DIR = 'texnote' TEXNOTE_DIR = 'texnote'
def parser(subparsers, config):
parser = subparsers.add_parser('texnote', help="edit advance note in latex") class TexnotePlugin(PapersPlugin):
def parser(self, subparsers, config):
parser = subparsers.add_parser(self.name, help="edit advance note in latex")
sub = parser.add_subparsers(title="valid texnote commands", dest="texcmd")
p = sub.add_parser("remove", help="remove a reference")
add_references_argument(p, single=True)
p = sub.add_parser("edit", help="edit the reference texnote")
add_references_argument(p, single=True)
#add_references_argument(parser, single=True)
parser.add_argument('-v', '--view', action='store_true', help='open the paper in a pdf viewer', default=None) parser.add_argument('-v', '--view', action='store_true', help='open the paper in a pdf viewer', default=None)
add_references_argument(parser, single=True)
return parser return parser
def command(self, config, ui, texcmd, reference, view):
def command(config, ui, ref, view):
ui.print_('texnote test')
if view is not None: if view is not None:
subprocess.Popen(['papers', 'open', ref]) subprocess.Popen(['papers', 'open', reference])
if texcmd == 'edit':
# check if citekey exist open_texnote(config, ui, reference)
open_texnote(config, ui, ref)
def toto(self):
print "toto"
@RemoveEvent.listen()
def remove(rmevent):
texplug = TexnotePlugin.get_instance()
texplug.toto()
rp = repo.Repository.from_directory(rmevent.config)
paper = rp.get_paper(parse_reference(rmevent.ui, rp, rmevent.citekey))
if 'texnote' in paper.metadata:
try:
os.remove(paper.metadata['texnote'])
except OSError:
pass # For some reason, the texnote file didn't exist
paper.metadata.pop('texnote')
metapath = rp.path_to_paper_file(paper.citekey, 'meta')
files.save_meta(paper.metadata, metapath)
def open_texnote(config, ui, ref): def open_texnote(config, ui, ref):
rp = repo.Repository.from_directory(config) rp = repo.Repository.from_directory(config)
paper = rp.get_paper(parse_reference(ui, rp, ref)) paper = rp.get_paper(parse_reference(ui, rp, ref))
if not paper.metadata.has_key('texnote'): #ugly to recode like for the doc field
if not 'texnote' in paper.metadata:
texnote_dir = os.path.join(rp.papersdir, TEXNOTE_DIR) texnote_dir = os.path.join(rp.papersdir, TEXNOTE_DIR)
# if folder does not exist create it # if folder does not exist create it, this should be relative
if not os.path.exists(texnote_dir): if not os.path.exists(texnote_dir):
os.mkdir(texnote_dir) os.mkdir(texnote_dir)
texnote_path = os.path.join(texnote_dir, paper.citekey + '.tex') texnote_path = os.path.join(texnote_dir, paper.citekey + '.tex')
@ -73,7 +87,6 @@ def open_texnote(config, ui, ref):
subprocess.Popen([config.get(configs.MAIN_SECTION, 'edit-cmd'), texnote_path]) subprocess.Popen([config.get(configs.MAIN_SECTION, 'edit-cmd'), texnote_path])
##### ugly replace by proper ##### ##### ugly replace by proper #####
def format_author(author): def format_author(author):
first = author.first() first = author.first()
@ -88,6 +101,7 @@ def format_author(author):
formatted += ' ' + last[0] formatted += ' ' + last[0]
return formatted return formatted
def concatenate_authors(authors): def concatenate_authors(authors):
concatenated = '' concatenated = ''
for a in range(len(authors)): for a in range(len(authors)):
@ -107,23 +121,23 @@ def autofill_texnote(texnote_path, bibentry):
text = f.read() text = f.read()
f.close() f.close()
# modify with bib info # modify with bib info
print bibentry #print bibentry
fields = bibentry.fields fields = bibentry.fields
persons = bibentry.persons persons = bibentry.persons
if fields.has_key('title'): if 'title' in fields:
title_str = fields['title'] title_str = fields['title']
text = text.replace("TITLE", title_str) text = text.replace("TITLE", title_str)
if fields.has_key('year'): if 'year' in fields:
year_str = fields['year'] year_str = fields['year']
text = text.replace("YEAR", year_str) text = text.replace("YEAR", year_str)
if fields.has_key('abstract'): if 'abstract' in fields:
abstract_str = fields['abstract'] abstract_str = fields['abstract']
text = text.replace("ABSTRACT", abstract_str) text = text.replace("ABSTRACT", abstract_str)
if persons.has_key('author'): if 'author' in persons:
authors = [] authors = []
for author in persons['author']: for author in persons['author']:
authors.append(format_author(author)) authors.append(format_author(author))

@ -10,9 +10,9 @@ setup(name='papers',
description='research papers manager', description='research papers manager',
requires=['pybtex'], requires=['pybtex'],
packages=find_packages(), packages=find_packages(),
package_data={'': ['*.tex']},
scripts=['papers/papers'] scripts=['papers/papers']
) )
# TODO include package data from plugins # TODO include proper package data from plugins (08/06/2013)
# Jonathan could not make it works (08/06/2013)
# should we use MANIFEST.in or package_data = ..., or both # should we use MANIFEST.in or package_data = ..., or both

@ -0,0 +1,65 @@
from unittest import TestCase
from papers.events import Event
_output = None
class TestEvent(Event):
def __init__(self, string):
self.string = string
def print_one(self):
_output.append('one')
class AddEvent(Event):
def __init__(self):
pass
def add(self, a, b):
return a + b
@TestEvent.listen(12, 15)
def display(TestEventInstance, nb1, nb2):
_output.append("%s %s %s"
% (TestEventInstance.string, nb1, nb2))
@TestEvent.listen()
def hello_word(TestEventInstance):
_output.append('Helloword')
@TestEvent.listen()
def print_it(TestEventInstance):
TestEventInstance.print_one()
@AddEvent.listen()
def do_it(AddEventInstance):
_output.append(AddEventInstance.add(17, 25))
class TestEvents(TestCase):
def setUp(self):
global _output
_output = []
def test_listen_TestEvent(self):
# using the callback system
myevent = TestEvent('abcdefghijklmnopqrstuvwxyz')
myevent.send() # this one call three function
correct = ['abcdefghijklmnopqrstuvwxyz 12 15',
'Helloword',
'one']
self.assertEquals(_output, correct)
def test_listen_AddEvent(self):
addevent = AddEvent()
addevent.send()
correct = [42]
self.assertEquals(_output, correct)
Loading…
Cancel
Save