276 lines
8.5 KiB
Python
276 lines
8.5 KiB
Python
import libbmc
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
|
|
from backend import config
|
|
from backend import tools
|
|
from libbmc import bibtex
|
|
from libbmc import fetcher
|
|
from libbmc.repositories import arxiv
|
|
from libbmc.papers import identifiers
|
|
from libbmc.papers import tearpages
|
|
|
|
|
|
def get_entry_from_index(item, file_or_id=None):
|
|
"""
|
|
Fetch an entry from the global index.
|
|
|
|
:param item: An identifier or filename.
|
|
:param file_or_id: Whether it is a file or an entry identifier. If \
|
|
``None``, will try to match both.
|
|
:returns: TODO.
|
|
"""
|
|
entry = None
|
|
# If explictly an identifier
|
|
if file_or_id == "id":
|
|
entry = bibtex.get_entry(config.get("index"), item)
|
|
# If explicitely a filename
|
|
elif file_or_id == "file":
|
|
entry = bibtex.get_entry_by_filter(config.get("index"),
|
|
lambda x: x.file == item) # TODO
|
|
# Else, get by id or file
|
|
else:
|
|
entry = bibtex.get_entry_by_filter(config.get("index"),
|
|
lambda x: (
|
|
x.id == item or
|
|
x.file == item)) # TODO
|
|
return entry
|
|
|
|
|
|
def download(url, manual, autoconfirm, tag):
|
|
"""
|
|
Download a given URL and add it to the library.
|
|
|
|
:param url: URL to download.
|
|
:param manual: Whether BibTeX should be fetched automatically.
|
|
:param autoconfirm: Whether import should be made silent or not.
|
|
:param tag: A tag for this file.
|
|
:returns: The name of the downloaded file once imported, \
|
|
or ``None`` in case of error.
|
|
"""
|
|
# Download the paper
|
|
print("Downloading %s…" % (url,))
|
|
dl, contenttype = fetcher.download(url)
|
|
|
|
if dl is not None:
|
|
print("Download finished.")
|
|
|
|
# Store it to a temporary file
|
|
try:
|
|
tmp = tempfile.NamedTemporaryFile(suffix='.%s' % (contenttype,))
|
|
with open(tmp.name, 'wb+') as fh:
|
|
fh.write(dl)
|
|
|
|
# And add it as a normal paper from now on
|
|
new_name = import_file(tmp.name, manual,
|
|
autoconfirm, tag)
|
|
if new_name is None:
|
|
return None
|
|
else:
|
|
return new_name
|
|
finally:
|
|
tmp.close()
|
|
else:
|
|
tools.warning("Could not fetch %s." % (url,))
|
|
return None
|
|
|
|
|
|
def import_file(src, manual=False, autoconfirm=False,
|
|
tag='', rename=True):
|
|
"""
|
|
Add a file to the library.
|
|
|
|
:param src: The path of the file to import.
|
|
:param manual: Whether BibTeX should be fetched automatically. \
|
|
Default to ``False``.
|
|
:param autoconfirm: Whether import should be made silent or not. \
|
|
Default to ``False``.
|
|
:param tag: A tag for this file. \
|
|
Default to no tag.
|
|
:param rename: Whether or not the file should be renamed according to the \
|
|
mask in the config.
|
|
:returns: The name of the imported file, or ``None`` in case of error.
|
|
"""
|
|
if not manual:
|
|
type, identifier = identifiers.find_identifiers(src)
|
|
|
|
if type is None:
|
|
tools.warning("Could not find an identifier for %s. \
|
|
Switching to manual entry." % (src))
|
|
# Fetch available identifiers types from libbmc
|
|
# Append "manual" for manual entry of BibTeX and "skip" to skip the
|
|
# file.
|
|
available_types_list = (libbmc.__valid_identifiers__ +
|
|
["manual", "skip"])
|
|
available_types = " / ".joint(available_types_list)
|
|
# Query for the type to use
|
|
while type not in available_types_list:
|
|
type = input("%s? " % (available_types)).lower()
|
|
|
|
if type == "skip":
|
|
# If "skip" is chosen, skip this file
|
|
return None
|
|
elif type == "manual":
|
|
identifier = None
|
|
else:
|
|
# Query for the identifier if required
|
|
identifier = input("Value? ")
|
|
else:
|
|
print("%s found for %s: %s." % (type, src, identifier))
|
|
|
|
# Fetch BibTeX automatically if we have an identifier
|
|
bibtex = None
|
|
if identifier is not None:
|
|
# If an identifier was provided, try to automatically fetch the bibtex
|
|
bibtex = identifiers.get_bibtex((type, identifier))
|
|
|
|
# TODO: Check bibtex
|
|
|
|
# Handle tag
|
|
if not autoconfirm:
|
|
# If autoconfirm is not enabled, query for a tag
|
|
user_tag = input("Tag for this paper [%s]? " % tag)
|
|
if user_tag != "":
|
|
tag = user_tag
|
|
bibtex["tag"] = tag
|
|
|
|
# TODO: Handle renaming
|
|
new_name = src
|
|
if rename:
|
|
pass
|
|
|
|
bibtex['file'] = os.path.abspath(new_name)
|
|
|
|
# Tear some pages if needed
|
|
should_tear_pages = True
|
|
if not autoconfirm:
|
|
# Ask for confirmation
|
|
pages_to_tear = tearpages.tearpage_needed(bibtex)
|
|
user_tear_pages = input("Found some pages to tear: %s. \
|
|
Confirm? [Y/n]" % (pages_to_tear)).lower()
|
|
if user_tear_pages == "n":
|
|
should_tear_pages = False
|
|
if should_tear_pages:
|
|
tearpages.tearpage(new_name, bibtex=bibtex)
|
|
|
|
# TODO: Append to global bibtex index
|
|
|
|
return new_name
|
|
|
|
|
|
def delete(item, keep=False, file_or_id=None):
|
|
"""
|
|
Delete an entry in the main BibTeX file, and the associated documents.
|
|
|
|
:param item: An entry or filename to delete from the database.
|
|
:param keep: Whether or not the document should be kept on the disk. \
|
|
If True, will simply delete the entry in the main BibTeX index.
|
|
:param file_or_id: Whether it is a file or an entry identifier. If \
|
|
``None``, will try to match both.
|
|
:returns: Nothing.
|
|
"""
|
|
entry = get_entry_from_index(item, file_or_id)
|
|
|
|
# Delete the entry from the bibtex index
|
|
bibtex.delete(config.get("index"), entry.id) # TODO
|
|
|
|
# If file should not be kept
|
|
if not keep:
|
|
# Delete it
|
|
os.unlink(entry.file) # TODO
|
|
|
|
|
|
def edit(item, file_or_id):
|
|
"""
|
|
Edit an entry in the main BibTeX file.
|
|
|
|
:param item: An entry or filename to edit in the database.
|
|
:param file_or_id: Whether it is a file or an entry identifier. If \
|
|
``None``, will try to match both.
|
|
:returns: Nothing.
|
|
"""
|
|
# TODO
|
|
pass
|
|
|
|
|
|
def list_entries():
|
|
"""
|
|
List all the available entries and their associated files.
|
|
|
|
:returns: A dict with entry identifiers as keys and associated files as \
|
|
values.
|
|
"""
|
|
# Get the list of entries from the BibTeX index
|
|
entries_list = bibtex.get(config.get("index"))
|
|
return {entry.id: entry.file for entry in entries_list} # TODO
|
|
|
|
|
|
def open(id):
|
|
"""
|
|
Open the file associated with the provided entry identifier.
|
|
|
|
:param id: An entry identifier in the main BibTeX file.
|
|
:returns: ``False`` if an error occured. ``True`` otherwise.
|
|
"""
|
|
# Fetch the entry from the BibTeX index
|
|
entry = bibtex.get_entry(config.get("index"), id)
|
|
if entry is None:
|
|
return False
|
|
else:
|
|
# Run xdg-open on the associated file to open it
|
|
subprocess.Popen(['xdg-open', entry.filename]) # TODO
|
|
return True
|
|
|
|
|
|
def export(item, file_or_id=None):
|
|
"""
|
|
Export the BibTeX entries associated to some items.
|
|
|
|
:param item: An entry or filename to export as BibTeX.
|
|
:param file_or_id: Whether it is a file or an entry identifier. If \
|
|
``None``, will try to match both.
|
|
:returns: TODO.
|
|
"""
|
|
# Fetch the entry from the BibTeX index
|
|
entry = get_entry_from_index(item, file_or_id)
|
|
if entry is not None:
|
|
return bibtex.dict2BibTeX(entry) # TODO
|
|
|
|
|
|
def resync():
|
|
"""
|
|
Compute the diff between the main BibTeX index and the files on the disk,
|
|
and try to resync them.
|
|
|
|
:returns: Nothing.
|
|
"""
|
|
# TODO
|
|
pass
|
|
|
|
|
|
def update(item, file_or_id=None):
|
|
"""
|
|
Update an entry, trying to fetch a more recent version (on arXiv for \
|
|
instance.)
|
|
|
|
:param item: An entry or filename to fetch update from.
|
|
:param file_or_id: Whether it is a file or an entry identifier. If \
|
|
``None``, will try to match both.
|
|
:returns: TODO.
|
|
"""
|
|
entry = get_entry_from_index(item, file_or_id)
|
|
# Fetch latest version
|
|
latest_version = arxiv.get_latest_version(entry.eprint) # TODO
|
|
if latest_version != entry.eprint: # TODO
|
|
print("New version found for %s: %s" % (entry, latest_version))
|
|
confirm = input("Download it? [Y/n] ")
|
|
if confirm.lower() == 'n':
|
|
return
|
|
|
|
# Download the updated version
|
|
# TODO
|
|
|
|
# Delete previous version if needed
|
|
# TODO
|