bmc/backend/commands.py

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