# AUTOGENERATED! DO NOT EDIT! File to edit: core.ipynb (unless otherwise specified).

__all__ = ['get_meta', 'meta_list', 'find_dupe', 'chk_dupe_title', 'chk_desc', 'chk_slug', 'chk_slug_len', 'chk_img',
           'chk_len', 'chk_desc_len']

# Cell
from fastcore.all import globtastic, Path, merge, L
from fastcore.script import call_parse
import re
import yaml
import json
import sys
from functools import partial
from collections import Counter

# Cell
_re_fm = re.compile(r'^---\s*(.*?)---\s*', flags=re.DOTALL)

def _load_yml(yml):
    if not yml: return {}
    else: return yaml.load(yml, Loader=yaml.FullLoader)

def get_meta(fname:str):
    "get metadata and front matter from `fname`."
    txt = Path(fname).read_text()
    fm = _re_fm.findall(txt)
    fm = _load_yml(fm[0] if fm else {})
    fm['mdseo-ignore'] = list(L(fm.get('mdseo-ignore', [])))
    clean_txt = re.sub('<[^<]+?>', '', _re_fm.sub('', txt))
    ignore = 'all' in fm['mdseo-ignore'] or 'mdseo-ignore-all' in txt
    if not ignore:
        return merge(dict(fname=fname,
                          n_words=len(clean_txt.split())),
                     fm)

# Cell
def meta_list(srcdir:str):
    "Get list of all metadata for markdown files in `srcdir`."
    docs = globtastic(srcdir, file_glob='*.md',
                      skip_folder_re='^[.]',
                      skip_file_re='^[_.]')
    return docs.map(get_meta).filter()

# Cell
def find_dupe(srcdir:str, key):
    "find duplicate values in front matter."
    c = Counter()
    for m in meta_list(srcdir):
        if 'dupe_title' in m['mdseo-ignore']: continue
        val = m.get(key)
        if val: c.update({f'{val}': 1})

    return [el[0] for el in c.items() if el[1] >= 2] if c else []

# Cell
@call_parse
def chk_dupe_title(srcdir:str='.', # directory of files to check
                  ):
    "Check for duplicate titles. Ignore with front matter `mdseo-ignore: [dupe_title]`"
    dupes = find_dupe(srcdir, 'title')
    msg = '\n\t'.join(dupes)
    if dupes: raise Exception(f"The following titles were found in multiple posts:\n\t{msg}")

# Cell
def _checker(func, msg:str, srcdir:str):
    fnames = meta_list(srcdir).filter(func).attrgot('fname')
    files = '\n\t'.join(fnames)
    if fnames: raise Exception(f"The following files {msg}:\n\t{files}")

# Cell
def _has_no_desc(d):
    if 'description' in d['mdseo-ignore'] or 'desc' in d['mdseo-ignore']: return False
    else: return 'description' not in d

@call_parse
def chk_desc(srcdir:str='.', # directory of files to check
            ):
    "Check if docs do not have the field `description` in their front matter. Ignore with front matter `mdseo-ignore: [description]`"
    return _checker(_has_no_desc, "do not have the field `description` in their front matter", srcdir)


# Cell
def _has_no_slug(d):
    if 'slug' in d['mdseo-ignore']: return False
    else: return 'slug' not in d

@call_parse
def chk_slug(srcdir:str='.', # directory of files to check
            ):
    "Check if docs do not have the field `slug` in their front matter. Ignore with front matter `mdseo-ignore: [slug]`"
    return _checker(_has_no_slug, "do not have the field `slug` in their front matter", srcdir)

# Cell
def _slug_len(d, n):
    if 'slug' in d['mdseo-ignore']: return False
    else: return len(d.get('slug', '')) > n

@call_parse
def chk_slug_len(srcdir:str='.', # directory of files to check
                 n=45 # max number of characters for slug
            ):
    "Check if docs have a `slug` field in their front matter that is less than `n` characters. Ignore with front matter `mdseo-ignore: [slug]`"
    return _checker(partial(_slug_len, n=n), f"have `slug` in their front matter that is greater than {n} characters", srcdir)

# Cell
def _has_no_img(d):
    if 'image' in d['mdseo-ignore'] or 'img' in d['mdseo-ignore']: return False
    return 'image' not in d

@call_parse
def chk_img(srcdir:str='.', # directory of files to check
            ):
    "Check if docs do not have the field `image` in their front matter. Ignore with front matter `mdseo-ignore: [image]`"
    return _checker(_has_no_img, "do not have the image `description` in their front matter", srcdir)

# Cell
def _lt_n(d, n):
    if 'len' in d['mdseo-ignore'] or 'length' in d['mdseo-ignore']: return False
    return d['n_words'] < n

@call_parse
def chk_len(n:int=50, # minimum number of words a document should contain
            srcdir:str='.', # directory of files to check
           ):
    "Check if docs contain less than `n` words. Ignore with front matter `mdseo-ignore: [length]`"
    return _checker(partial(_lt_n, n=n), "contain less than 50 words", srcdir)

# Cell
def _desc_len(d, lower ,upper):
    if 'description' in d['mdseo-ignore'] or 'desc' in d['mdseo-ignore']: return False
    desc = d.get('description', None)
    if desc: return len(desc) < lower or len(desc) > upper
    else: return False

@call_parse
def chk_desc_len(
            n_lower:int=50, # the lower bound number of characters a document should contain
            n_upper:int=300, # the upper bound number of characters a document should contain
            srcdir:str='.', # directory of files to check
            ):
    "Check if docs have a description that is not between `n_lower` and `n_upper` characters. Ignore with front matter `mdseo-ignore: [description]`"
    func = partial(_desc_len, lower=n_lower, upper=n_upper)
    return _checker(func, f"have a description that is not between {n_lower} and {n_upper} characters", srcdir)