1# -*- coding: utf-8 -*-
2"""Rename file using metadata."""
3
4import logging
5import re
6import string
7import sys
8
9from isbnlib.dev._bouth23 import b2u3, u
10from isbnlib.dev.helpers import File, cutoff_tokens, cwdfiles, last_first
11
12from ....app import config, ean13, get_isbnlike, meta
13
14LOGGER = logging.getLogger(__name__)
15
16DEFAULT_PATT = '{firstAuthorLastName}{year}_{title}_{isbn}'
17PATTERN = config.options.get('REN_FORMAT', DEFAULT_PATT)
18CUTOFF = 65
19
20
21def checkpattern(pattern):
22    """Check the validity of pattern for renaming a file."""
23    placeholders = (
24        '{authorsFullNames}',
25        '{authorsLastNames}',
26        '{firstAuthorLastName}',
27        '{year}',
28        '{publisher}',
29        '{title}',
30        '{isbn}',
31        '{language}',
32    )
33    tocheck = pattern[:]
34
35    placeholderfound = False
36    for placeholder in placeholders:
37        if placeholder in tocheck:
38            tocheck = tocheck.replace(placeholder, '')
39            placeholderfound = True
40    if not placeholderfound or '{' in tocheck:
41        LOGGER.warning('Not valid pattern %s', pattern)
42        return False
43
44    validchars = '-_.,() {0}{1}'.format(string.ascii_letters, string.digits)
45
46    for char in tocheck:
47        if char not in validchars:
48            LOGGER.warning('Invalid character in pattern (%s)', char)
49            return False
50    return True
51
52
53PATTERN = PATTERN if checkpattern(PATTERN) else DEFAULT_PATT
54
55
56def get_isbn(filename):
57    """Extract the ISBN from file's name."""
58    isbnlikes = get_isbnlike(filename, level='normal')
59    eans = [ean13(isbnlike) for isbnlike in isbnlikes] if isbnlikes else None
60    isbn = eans[0] if eans else None
61    if not isbn:  # pragma: no cover
62        LOGGER.warning('No ISBN found in name of file %s', filename)
63        sys.stderr.write('no ISBN found in name of file %s \n' % filename)
64        return None
65    return isbn
66
67
68def cleannewname(newname):
69    """Clean and Strip '._!? ' from newname."""
70    regex1 = re.compile(r'[!?/\\]')
71    regex2 = re.compile(r'\s\s+')
72    newname = regex1.sub(' ', newname)
73    newname = regex2.sub(' ', newname)
74    return newname.strip('_., ')
75
76
77def newfilename(metadata, pattern=PATTERN):
78    """Return a new file name created from book metadata."""
79    pattern = pattern if pattern else PATTERN
80    for key in metadata.keys():
81        if not metadata[key]:
82            metadata[key] = u('UNKNOWN')
83
84    d = {
85        'authorsFullNames': ','.join(metadata['Authors']),
86        'year': metadata['Year'],
87        'publisher': metadata['Publisher'],
88        'title': metadata['Title'],
89        'language': metadata['Language'],
90        'isbn': metadata['ISBN-13'],
91    }
92
93    if d['title'] == u('UNKNOWN') or d['isbn'] == u('UNKNOWN'):
94        LOGGER.critical('Not enough metadata')
95        return None
96    # Drop subtitle from title (if available)
97    d['title'] = cleannewname(d['title']).split(' - ')[0]
98    cutoff = min(len(d['title']), CUTOFF)
99    d['title'] = ' '.join(cutoff_tokens(d['title'].split(' '), cutoff))
100
101    authorslastnames = [
102        last_first(authorname)['last'] for authorname in metadata['Authors']
103    ]
104    d['authorsLastNames'] = ','.join(authorslastnames)
105    d['firstAuthorLastName'] = authorslastnames[0]
106
107    try:
108        formatted = u(pattern).format(**d)
109        return cleannewname(formatted)
110    except KeyError as e:
111        LOGGER.warning('Error with placeholder: %s', e)
112        return None
113
114
115def renfile(filename, isbn, service, pattern=PATTERN):
116    """Rename file with associate ISBN."""
117    service = service if service else 'default'
118    metadata = meta(isbn, service)
119    if not metadata:  # pragma: no cover
120        LOGGER.warning('No metadata for %s', filename)
121        sys.stderr.write('No metadata for %s\n' % filename)
122        return None
123    newname = newfilename(metadata, pattern)
124    if not newname:  # pragma: no cover
125        LOGGER.warning('%s NOT renamed!', filename)
126        sys.stderr.write('%s NOT renamed \n' % filename)
127        return None
128    oldfile = File(filename)
129    ext = oldfile.ext
130    newbasename = b2u3(newname + ext)
131    oldbasename = oldfile.basename
132    if oldfile.mkwinsafe(newbasename) == oldbasename:  # pragma: no cover
133        return True
134    success = oldfile.baserename(newbasename)
135    if success:
136        try:  # pragma: no cover
137            sys.stdout.write('%s renamed to %s \n' %
138                             (oldbasename, oldfile.basename))
139        except Exception as e:  # pragma: no cover
140            LOGGER.info('Error when writing for stdout: %s', e)
141        return True
142    return None  # pragma: no cover
143
144
145def rencwdfiles(fnpatt='*', service='default', pattern=PATTERN):
146    """Rename cwd files with a ISBN in their filenames and within fnpatt."""
147    files = [(get_isbn(f), f) for f in cwdfiles(fnpatt) if get_isbn(f)]
148    for isbn, f in files:
149        renfile(f, isbn, service, pattern)
150    return True
151