Source code for dotapatch.patch

# coding: utf-8
'''This is the main module the core of dotapatch.'''
from __future__ import print_function, absolute_import
import os.path as path
from collections import defaultdict
from logging import getLogger as get_logger
from dotapatch.model import Html
from dotapatch.data import HeropediaData


ERROR = -1
SUCCESS = 0
WARNING = 1

_LOGGER = get_logger('dotapatch.patch')


def _read_file(file_path):
    '''Reads the changelog file.

    Parameters
    ----------
    file_path : str
        Changelog to be parsed, it can either be ``filename`` or
        ``absolute_path/filename``

    Returns
    -------
    general : list[str]
        Dictionary of items to be added into the items section

    patch_version : str
        Patch version, e.g. ``7.07f``

    line_count : int
        Changelog lines read

    Raises
    ------
    exceptions.OSError
        Changelog file not found

    '''
    if not path.isfile(file_path):
        error_title = '{} not found'.format(file_path)
        raise OSError(error_title)

    with open(file_path, 'r') as changelog:
        general = []
        for line in changelog:
            line = line.replace('* ', '').rstrip()
            if line:
                general.append(line)

    patch_version = general[0].split(':')[0].strip()
    general = general[2:]
    line_count = len(general)

    return general, patch_version, line_count


def _organize_heropedia(general):
    '''Searches for ``items`` and ``heroes`` lines.

    Parameters
    ----------
    general : list[str]
        List of general changelog lines to be added into the general section

    Returns
    -------
    items : dict
        Dictionary of items to be added into the items section

    heroes : dict
        Dictionary of heroes to be added into the heroes section

    Note
    ----
    Both ``dict`` use the following structure:
    ``{'dname': ['Change one.', 'Change two.']}``

    '''

    data = HeropediaData()

    # Organize changelog
    items = defaultdict(list)
    heroes = defaultdict(list)

    for line in general[:]:
        found_hero = data.get_hero_name(line)

        # Remove hero/item name, capitalize the line
        formatted_line = ' '.join(line.split(': ', 1)[1:])
        if formatted_line:
            formatted_line = formatted_line[0].upper() + formatted_line[1:]

        if found_hero:
            heroes[found_hero].append(formatted_line)
            general.remove(line)
        else:
            found_item = data.get_item_name(line)
            if found_item:
                items[found_item].append(formatted_line)
                general.remove(line)

    return items, heroes


def _generate_html(patch_version, template, general, items, heroes):
    '''Saves the parsed changelog lines as ``patch_name.html``.

    Note
    ----
    Both ``dict`` use the following structure:
    ``{'dname': ['Change one.', 'Change two.']}``

    Parameters
    ----------
    patch_version : str
        Patch version, e.g. ``7.07f``

    template : str
        Template to be used as base to parse the changelog, it can either be
        the ``name`` or ``absolute_path/name``

    general : list[str]
        List of general changelog lines to be added into the general section

    items : dict
        Dictionary of items to be added into the items section

    heroes : dict
        Dictionary of heroes to be added into the heroes section
    '''

    patch_name = patch_version.replace('.', '')

    with open(path.abspath(patch_name + '.html'), 'w') as text:
        model = Html(patch_version, template)
        model.add_general(general)
        model.add_items(items)
        model.add_heroes(heroes)
        model.close()
        print(model.get_content(), file=text)
        _LOGGER.info(
            'HTML saved at {}.html'
            .format(path.abspath(patch_name)))


[docs]def parse(file_path, template='default'): '''Parses the changelog. Parameters ---------- file_path : str Changelog to be parsed, it can either be ``filename`` or ``absolute_path/filename`` template : str Template to be used as base to parse the changelog, it can either be the ``name`` or ``absolute_path/name`` Returns ------- status : int Parsing status Note ---- ``status == 0`` : Conversion went smoothly ``status < 0`` : Critical error ``status >= 1`` : Some lines under GENERAL section should be reviewed ''' status = ERROR general, patch_version, line_count = _read_file(file_path) _LOGGER.info('Parsing {}'.format(patch_version)) items, heroes = _organize_heropedia(general) _generate_html(patch_version, template, general, items, heroes) # Feedback new_line_count = \ sum(len(changes) for changes in heroes.values()) \ + sum(len(changes) for changes in items.values()) status = line_count - new_line_count if status == 0: _LOGGER.info( '{} conversion went smoothly.'.format(patch_version)) elif status < 0: _LOGGER.critical('Contact me at @arthurazs') else: if status == 1: message = '''{} had 1 line under GENERAL updates: * {} This line might be a hero/item update and you should manually place it at the proper location.'''.format(patch_version, ''.join(general)) _LOGGER.warning(message) else: message = '{} had {} lines under GENERAL updates:' \ .format(patch_version, str(status)) for line in general: message = ('{}\n* {}'.format(message, line)) message = '''{} Some of these lines might be hero/item updates and you should manually place them at the proper location.'''.format(message) _LOGGER.warning(message) return status