<commit_before>import sublime
import sublime_plugin
import os
import re
import subprocess

# view.find sadly can't handle naming groups
NO_NAMING_GROUPS_PATTERN = r"(?s)<{7}[^\n]*\n"\
                            "(.*?)(?:\|{7}[^\n]*\n"\
                            "(.*?))?={7}\n(.*?)>{7}[^\n]*\n"

CONFLICT_REGEX = re.compile(r"(?s)<{7}[^\n]*\n"
                            "(?P<ours>.*?)(?:\|{7}[^\n]*\n"
                            "(?P<ancestor>.*?))?={7}\n"
                            "(?P<theirs>.*?)>{7}[^\n]*\n")

# group patterns; this patterns always match the seperating lines too,
# so we have to remove then later from the matched regions
CONFLICT_GROUP_REGEX = {
    "ours": r"(?s)<{7}[^\n]*\n.*?\|{7}|>{7}",
    "ancestor": r"(?s)\|{7}[^\n]*\n.*?={7}",
    "theirs": r"(?s)={7}[^\n]*\n.*?>{7}"
}

icons = {
    "ours": "Packages/Git Conflict Resolver/gutter/ours.png",
    "ancestor": "Packages/Git Conflict Resolver/gutter/ancestor.png",
    "theirs": "Packages/Git Conflict Resolver/gutter/theirs.png"
}

messages = {
    "no_conflict_found": "No conflict found",
    "no_git_repo_found": "No git repository found",
    "no_such_group": "No such conflict group found",
    "git_executable_not_found": "Git Conflict Resolver is not able find your git executable.\n\n" +
                                "To ensure the plugin works properly you can use the settings file to tell Git Conflict Resolver about the path.\n" +
                                "(Preferences > Package Settings > Git Conflict Resolver > Settings - User)",
    "open_all": "Open all ..."
}

# Global settings
settings_file = "GitConflictResolver.sublime-settings"
settings = {}
default_settings = {
    "live_matching": True,
    "matching_scope": 'invalid',
    "fill_conflict_area": False,
    "outline_conflict_area": True,
    "ours_gutter": True,
    "ancestor_gutter": True,
    "theirs_gutter": True,
    "git_path": "git",
    "show_only_filenames": True
}

def plugin_loaded():
    load_settings()

def load_settings():
    global subl_settings, settings

    subl_settings = sublime.load_settings(settings_file)

    # Live matching
    settings['live_matching'] = subl_settings.get(
        'live_matching',
        default_settings['live_matching']
    )
    # Matching scope
    settings['matching_scope'] = subl_settings.get(
        'matching_scope',
        default_settings['matching_scope']
    )
    # Fill conflict area
    settings['fill_conflict_area'] = subl_settings.get(
        'fill_conflict_area',
        default_settings['fill_conflict_area']
    )
    # Outline conflict area
    settings['outline_conflict_area'] = subl_settings.get(
        'outline_conflict_area',
        default_settings['outline_conflict_area']
    )
    # Conflict group highlighting
    settings['ours_gutter'] = subl_settings.get(
        'ours_gutter',
        default_settings['ours_gutter']
    )
    settings['ancestor_gutter'] = subl_settings.get(
        'ancestor_gutter',
        default_settings['ancestor_gutter']
    )
    settings['theirs_gutter'] = subl_settings.get(
        'theirs_gutter',
        default_settings['theirs_gutter']
    )

    # Git Settings
    settings['git_path'] = subl_settings.get(
        'git_path',
        default_settings['git_path']
    )

    # Remove everything except for the part from the filename
    settings['show_only_filenames'] = subl_settings.get(
        'show_only_filenames',
        default_settings['show_only_filenames']
    )

def find_conflict(view, begin=0):
    conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, begin)

    if not conflict_region:
        conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, 0)
        if not conflict_region:
            sublime.status_message(messages['no_conflict_found'])
            return None

    return conflict_region

def join_regions(regions):
    if not regions:
        return sublime.Region(-1, -1)

    begin = min([region.begin() for region in regions if region.begin() >= 0])
    end = max([region.end() for region in regions])

    return sublime.Region(begin, end)

def highlight_conflict_group(view, group):
    scope = group + '_gutter'
    if settings[scope]:
        conflict_regions = view.find_all(CONFLICT_GROUP_REGEX[group])

        if not conflict_regions:
            return

        # Remove the first and last line since they just contain the separators
        highlight_regions = []
        for region in conflict_regions:
            region = view.split_by_newlines(region)[1:-1]
            # Ignore empty subregions
            if not region:
                continue

            for subregion in region:
                highlight_regions.append(subregion)

        view.erase_regions("GitConflictRegion_" + group)
        view.add_regions(
            "GitConflictRegions_" + group,
            highlight_regions,
            "warning",
            icons[group],
            sublime.DRAW_NO_FILL |
            sublime.DRAW_NO_OUTLINE |
            sublime.HIDE_ON_MINIMAP
        )

def highlight_conflicts(view):
    conflict_regions = view.find_all(NO_NAMING_GROUPS_PATTERN)

    view.erase_regions("GitConflictRegions")
    view.add_regions(
        "GitConflictRegions",
        conflict_regions,
        settings['matching_scope'],
        "",
        (0 if settings['fill_conflict_area'] else sublime.DRAW_NO_FILL)
        |
        (0 if settings['outline_conflict_area'] else sublime.DRAW_NO_OUTLINE)
    )

    highlight_conflict_group(view, 'ours')
    highlight_conflict_group(view, 'ancestor')
    highlight_conflict_group(view, 'theirs')

def extract(view, region, keep):
    conflict_text = view.substr(region)
    match = CONFLICT_REGEX.search(conflict_text)

    # If we didn't matched the group return None
    if not match.group(keep):
        sublime.status_message(messages['no_such_group'])
        return None

    return CONFLICT_REGEX.sub(r'\g<' + keep + '>', conflict_text)

class FindNextConflict(sublime_plugin.TextCommand):
    def run(self, edit):
        # Reload settings
        load_settings()

        current_selection = self.view.sel()

        # Use the end of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = self.view.sel()[-1].end()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        # Add the region to the selection
        self.view.show_at_center(conflict_region)
        current_selection.clear()
        current_selection.add(conflict_region)

class Keep(sublime_plugin.TextCommand):
    def run(self, edit, keep):
        # Reload settings
        load_settings()

        current_selection = self.view.sel()

        # Use the begin of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = current_selection[0].begin()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        replace_text = extract(self.view, conflict_region, keep)

        if not replace_text:
            replace_text = ""

        self.view.replace(edit, conflict_region, replace_text)

class ListConflictFiles(sublime_plugin.WindowCommand):
    def run(self):
        # Reload settings
        load_settings()

        # Ensure git executable is available
        if not self._git_executable_found():
            sublime.error_message(messages['git_executable_not_found'])
            return

        window = self.window
        git_repo = self._determine_git_dir()
        if not git_repo:
            sublime.status_message(messages['no_git_repo_found'])
            return

        conflict_files = self._get_conflict_files(git_repo)

        if not conflict_files:
            sublime.status_message(
                messages['no_conflict_found'] +
                ((" (" + git_repo + ")") if git_repo else "")
            )
            return

        conflict_files = conflict_files.split('\n')
        # Remove empty strings and sort the list
        # (TODO: sort also filenames only?)
        conflict_files = sorted([x for x in conflict_files if x])
        full_path = [os.path.join(git_repo, x) for x in conflict_files]

        # Copy the list for representation
        show_files = list(conflict_files)

        if settings['show_only_filenames']:
            only_filenames = []
            for string in conflict_files:
                only_filenames.append(string.rpartition('/')[2])

            show_files = only_filenames

        # Add an "Open all ..." option
        show_files.insert(0, messages['open_all'])

        # Show the conflict files in the quickpanel and open them on selection
        def open_conflict(index):
            if index < 0:
                return
            elif index == 0:
                # Open all ...
                for file in full_path:
                    window.open_file(file)
            else:
                window.open_file(full_path[index - 1])

        window.show_quick_panel(show_files, open_conflict)

    def _git_executable_found(self):
        try:
            execute_command(settings['git_path'])
        except FileNotFoundError:
            return False
        else:
            return True

    def _get_conflict_files(self, working_dir):
        # Search for conflicts using git executable
        return execute_command([
            settings['git_path'],
            "diff", "--name-only",
            "--diff-filter=U"
            ],
            working_dir=working_dir
        )

    def _determine_git_dir(self):
        open_view_path = self.window.active_view().file_name()
        open_folders = self.window.folders()

        working_dir = None
        if open_view_path:
            # Remove the traling filename, we just need the folder
            working_dir = os.path.dirname(open_view_path)
        elif open_folders:
            working_dir = open_folders[0]

        # Get git top-level folder
        if working_dir:
            working_dir = execute_command([
                settings['git_path'],
                "rev-parse", "--show-toplevel"
                ],
                working_dir=working_dir)

        return working_dir

def execute_command(command, working_dir=None):
    startupinfo = None
    # hide console window on windows
    if os.name == 'nt':
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

    output = None
    try:
        output = subprocess.check_output(
            command,
            cwd=working_dir,
            startupinfo=startupinfo
        )
    except (subprocess.CalledProcessError, AttributeError):
        # Git will return an error when the given directory
        # is not a repository, which means that we can ignore this error
        pass
    else:
        output = str(output, encoding="utf-8").strip()

    return output

class ScanForConflicts(sublime_plugin.EventListener):
    def on_activated(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_load(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_pre_save(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

# ST3 automatically calls plugin_loaded when the API is ready
# For ST2 we have to call the function manually
if not int(sublime.version()) > 3000:
    plugin_loaded()
<commit_msg>Plugin: Search for the first conflict in the opened views<commit_after>import sublime
import sublime_plugin
import os
import re
import subprocess

# view.find sadly can't handle naming groups
NO_NAMING_GROUPS_PATTERN = r"(?s)<{7}[^\n]*\n"\
                            "(.*?)(?:\|{7}[^\n]*\n"\
                            "(.*?))?={7}\n(.*?)>{7}[^\n]*\n"

CONFLICT_REGEX = re.compile(r"(?s)<{7}[^\n]*\n"
                            "(?P<ours>.*?)(?:\|{7}[^\n]*\n"
                            "(?P<ancestor>.*?))?={7}\n"
                            "(?P<theirs>.*?)>{7}[^\n]*\n")

# group patterns; this patterns always match the seperating lines too,
# so we have to remove then later from the matched regions
CONFLICT_GROUP_REGEX = {
    "ours": r"(?s)<{7}[^\n]*\n.*?\|{7}|>{7}",
    "ancestor": r"(?s)\|{7}[^\n]*\n.*?={7}",
    "theirs": r"(?s)={7}[^\n]*\n.*?>{7}"
}

icons = {
    "ours": "Packages/Git Conflict Resolver/gutter/ours.png",
    "ancestor": "Packages/Git Conflict Resolver/gutter/ancestor.png",
    "theirs": "Packages/Git Conflict Resolver/gutter/theirs.png"
}

messages = {
    "no_conflict_found": "No conflict found",
    "no_git_repo_found": "No git repository found",
    "no_such_group": "No such conflict group found",
    "git_executable_not_found": "Git Conflict Resolver is not able find your git executable.\n\n" +
                                "To ensure the plugin works properly you can use the settings file to tell Git Conflict Resolver about the path.\n" +
                                "(Preferences > Package Settings > Git Conflict Resolver > Settings - User)",
    "open_all": "Open all ..."
}

# Global settings
settings_file = "GitConflictResolver.sublime-settings"
settings = {}
default_settings = {
    "live_matching": True,
    "matching_scope": 'invalid',
    "fill_conflict_area": False,
    "outline_conflict_area": True,
    "ours_gutter": True,
    "ancestor_gutter": True,
    "theirs_gutter": True,
    "git_path": "git",
    "show_only_filenames": True
}

def plugin_loaded():
    load_settings()

def load_settings():
    global subl_settings, settings

    subl_settings = sublime.load_settings(settings_file)

    # Live matching
    settings['live_matching'] = subl_settings.get(
        'live_matching',
        default_settings['live_matching']
    )
    # Matching scope
    settings['matching_scope'] = subl_settings.get(
        'matching_scope',
        default_settings['matching_scope']
    )
    # Fill conflict area
    settings['fill_conflict_area'] = subl_settings.get(
        'fill_conflict_area',
        default_settings['fill_conflict_area']
    )
    # Outline conflict area
    settings['outline_conflict_area'] = subl_settings.get(
        'outline_conflict_area',
        default_settings['outline_conflict_area']
    )
    # Conflict group highlighting
    settings['ours_gutter'] = subl_settings.get(
        'ours_gutter',
        default_settings['ours_gutter']
    )
    settings['ancestor_gutter'] = subl_settings.get(
        'ancestor_gutter',
        default_settings['ancestor_gutter']
    )
    settings['theirs_gutter'] = subl_settings.get(
        'theirs_gutter',
        default_settings['theirs_gutter']
    )

    # Git Settings
    settings['git_path'] = subl_settings.get(
        'git_path',
        default_settings['git_path']
    )

    # Remove everything except for the part from the filename
    settings['show_only_filenames'] = subl_settings.get(
        'show_only_filenames',
        default_settings['show_only_filenames']
    )

def find_conflict(view, begin=0):
    conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, begin)

    if not conflict_region:
        conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, 0)
        if not conflict_region:
            sublime.status_message(messages['no_conflict_found'])
            return None

    return conflict_region

def join_regions(regions):
    if not regions:
        return sublime.Region(-1, -1)

    begin = min([region.begin() for region in regions if region.begin() >= 0])
    end = max([region.end() for region in regions])

    return sublime.Region(begin, end)

def highlight_conflict_group(view, group):
    scope = group + '_gutter'
    if settings[scope]:
        conflict_regions = view.find_all(CONFLICT_GROUP_REGEX[group])

        if not conflict_regions:
            return

        # Remove the first and last line since they just contain the separators
        highlight_regions = []
        for region in conflict_regions:
            region = view.split_by_newlines(region)[1:-1]
            # Ignore empty subregions
            if not region:
                continue

            for subregion in region:
                highlight_regions.append(subregion)

        view.erase_regions("GitConflictRegion_" + group)
        view.add_regions(
            "GitConflictRegions_" + group,
            highlight_regions,
            "warning",
            icons[group],
            sublime.DRAW_NO_FILL |
            sublime.DRAW_NO_OUTLINE |
            sublime.HIDE_ON_MINIMAP
        )

def highlight_conflicts(view):
    conflict_regions = view.find_all(NO_NAMING_GROUPS_PATTERN)

    view.erase_regions("GitConflictRegions")
    view.add_regions(
        "GitConflictRegions",
        conflict_regions,
        settings['matching_scope'],
        "",
        (0 if settings['fill_conflict_area'] else sublime.DRAW_NO_FILL)
        |
        (0 if settings['outline_conflict_area'] else sublime.DRAW_NO_OUTLINE)
    )

    highlight_conflict_group(view, 'ours')
    highlight_conflict_group(view, 'ancestor')
    highlight_conflict_group(view, 'theirs')

def extract(view, region, keep):
    conflict_text = view.substr(region)
    match = CONFLICT_REGEX.search(conflict_text)

    # If we didn't matched the group return None
    if not match.group(keep):
        sublime.status_message(messages['no_such_group'])
        return None

    return CONFLICT_REGEX.sub(r'\g<' + keep + '>', conflict_text)

class FindNextConflict(sublime_plugin.TextCommand):
    def run(self, edit):
        # Reload settings
        load_settings()

        current_selection = self.view.sel()

        # Use the end of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = self.view.sel()[-1].end()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        # Add the region to the selection
        self.view.show_at_center(conflict_region)
        current_selection.clear()
        current_selection.add(conflict_region)

class Keep(sublime_plugin.TextCommand):
    def run(self, edit, keep):
        # Reload settings
        load_settings()

        current_selection = self.view.sel()

        # Use the begin of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = current_selection[0].begin()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        replace_text = extract(self.view, conflict_region, keep)

        if not replace_text:
            replace_text = ""

        self.view.replace(edit, conflict_region, replace_text)

class ListConflictFiles(sublime_plugin.WindowCommand):
    def run(self):
        # Reload settings
        load_settings()

        # Ensure git executable is available
        if not self._git_executable_found():
            sublime.error_message(messages['git_executable_not_found'])
            return

        window = self.window
        git_repo = self._determine_git_dir()
        if not git_repo:
            sublime.status_message(messages['no_git_repo_found'])
            return

        conflict_files = self._get_conflict_files(git_repo)

        if not conflict_files:
            sublime.status_message(
                messages['no_conflict_found'] +
                ((" (" + git_repo + ")") if git_repo else "")
            )
            return

        conflict_files = conflict_files.split('\n')
        # Remove empty strings and sort the list
        # (TODO: sort also filenames only?)
        conflict_files = sorted([x for x in conflict_files if x])
        full_path = [os.path.join(git_repo, x) for x in conflict_files]

        # Copy the list for representation
        show_files = list(conflict_files)

        if settings['show_only_filenames']:
            only_filenames = []
            for string in conflict_files:
                only_filenames.append(string.rpartition('/')[2])

            show_files = only_filenames

        # Add an "Open all ..." option
        show_files.insert(0, messages['open_all'])

        # Show the conflict files in the quickpanel and open them on selection
        def open_conflict(index):
            if index < 0:
                return
            elif index == 0:
                # Open all ...
                self._open_files(*full_path)
            else:
                self._open_files(full_path[index - 1])

        window.show_quick_panel(show_files, open_conflict)

    def _git_executable_found(self):
        try:
            execute_command(settings['git_path'])
        except FileNotFoundError:
            return False
        else:
            return True

    def _get_conflict_files(self, working_dir):
        # Search for conflicts using git executable
        return execute_command([
            settings['git_path'],
            "diff", "--name-only",
            "--diff-filter=U"
            ],
            working_dir=working_dir
        )

    def _determine_git_dir(self):
        open_view_path = self.window.active_view().file_name()
        open_folders = self.window.folders()

        working_dir = None
        if open_view_path:
            # Remove the traling filename, we just need the folder
            working_dir = os.path.dirname(open_view_path)
        elif open_folders:
            working_dir = open_folders[0]

        # Get git top-level folder
        if working_dir:
            working_dir = execute_command([
                settings['git_path'],
                "rev-parse", "--show-toplevel"
                ],
                working_dir=working_dir)

        return working_dir

    def _open_files(self, *files):
        for file in files:
            view = self.window.open_file(file)
            sublime.set_timeout_async(lambda: init_view(view), 10)

def execute_command(command, working_dir=None):
    startupinfo = None
    # hide console window on windows
    if os.name == 'nt':
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

    output = None
    try:
        output = subprocess.check_output(
            command,
            cwd=working_dir,
            startupinfo=startupinfo
        )
    except (subprocess.CalledProcessError, AttributeError):
        # Git will return an error when the given directory
        # is not a repository, which means that we can ignore this error
        pass
    else:
        output = str(output, encoding="utf-8").strip()

    return output

def init_view(view):
    if view.is_loading():
        sublime.set_timeout(lambda: init_view(view), 10)
    else:
        view.run_command("find_next_conflict")
        view.window().focus_view(view)

class ScanForConflicts(sublime_plugin.EventListener):
    def on_activated(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_load(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_pre_save(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

# ST3 automatically calls plugin_loaded when the API is ready
# For ST2 we have to call the function manually
if not int(sublime.version()) > 3000:
    plugin_loaded()
<|endoftext|>


<commit_before>import sublime
import sublime_plugin
import os
import re
import subprocess

# view.find sadly can't handle naming groups
NO_NAMING_GROUPS_PATTERN = r"(?s)<{7}[^\n]*\n"\
                            "(.*?)(?:\|{7}[^\n]*\n"\
                            "(.*?))?={7}\n(.*?)>{7}[^\n]*\n"

CONFLICT_REGEX = re.compile(r"(?s)<{7}[^\n]*\n"
                            "(?P<ours>.*?)(?:\|{7}[^\n]*\n"
                            "(?P<ancestor>.*?))?={7}\n"
                            "(?P<theirs>.*?)>{7}[^\n]*\n")

# group patterns; this patterns always match the seperating lines too,
# so we have to remove then later from the matched regions
CONFLICT_GROUP_REGEX = {
    "ours": r"(?s)<{7}[^\n]*\n.*?\|{7}|>{7}",
    "ancestor": r"(?s)\|{7}[^\n]*\n.*?={7}",
    "theirs": r"(?s)={7}[^\n]*\n.*?>{7}"
}

icons = {
    "ours": "Packages/Git Conflict Resolver/gutter/ours.png",
    "ancestor": "Packages/Git Conflict Resolver/gutter/ancestor.png",
    "theirs": "Packages/Git Conflict Resolver/gutter/theirs.png"
}

messages = {
    "no_conflict_found": "No conflict found",
    "no_such_group": "No such conflict group found",
    "open_all": "Open all ..."
}

# Global settings
settings_file = "GitConflictResolver.sublime-settings"
settings = {}
default_settings = {
    "live_matching": True,
    "matching_scope": 'invalid',
    "fill_conflict_area": False,
    "outline_conflict_area": True,
    "ours_gutter": True,
    "ancestor_gutter": True,
    "theirs_gutter": True,
    "git_path": "git",
    "show_only_filenames": True
}

def plugin_loaded():
    global subl_settings, settings

    subl_settings = sublime.load_settings(settings_file)

    # Live matching
    settings['live_matching'] = subl_settings.get(
        'live_matching',
        default_settings['live_matching']
    )
    # Matching scope
    settings['matching_scope'] = subl_settings.get(
        'matching_scope',
        default_settings['matching_scope']
    )
    # Fill conflict area
    settings['fill_conflict_area'] = subl_settings.get(
        'fill_conflict_area',
        default_settings['fill_conflict_area']
    )
    # Outline conflict area
    settings['outline_conflict_area'] = subl_settings.get(
        'outline_conflict_area',
        default_settings['outline_conflict_area']
    )
    # Conflict group highlighting
    settings['ours_gutter'] = subl_settings.get(
        'ours_gutter',
        default_settings['ours_gutter']
    )
    settings['ancestor_gutter'] = subl_settings.get(
        'ancestor_gutter',
        default_settings['ancestor_gutter']
    )
    settings['theirs_gutter'] = subl_settings.get(
        'theirs_gutter',
        default_settings['theirs_gutter']
    )

    # Git Settings
    settings['git_path'] = subl_settings.get(
        'git_path',
        default_settings['git_path']
    )

    # Remove everything except for the part from the filename
    settings['show_only_filenames'] = subl_settings.get(
        'show_only_filenames',
        default_settings['show_only_filenames']
    )

def find_conflict(view, begin=0):
    conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, begin)

    if not conflict_region:
        conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, 0)
        if not conflict_region:
            sublime.status_message(messages['no_conflict_found'])
            return None

    return conflict_region

def join_regions(regions):
    if not regions:
        return sublime.Region(-1, -1)

    begin = min([region.begin() for region in regions if region.begin() >= 0])
    end = max([region.end() for region in regions])

    return sublime.Region(begin, end)

def highlight_conflict_group(view, group):
    scope = group + '_gutter'
    if settings[scope]:
        conflict_regions = view.find_all(CONFLICT_GROUP_REGEX[group])

        if not conflict_regions:
            return

        # Remove the first and last line since they just contain the separators
        highlight_regions = []
        for region in conflict_regions:
            region = view.split_by_newlines(region)[1:-1]
            # Ignore empty subregions
            if not region:
                continue

            for subregion in region:
                highlight_regions.append(subregion)

        view.erase_regions("GitConflictRegion_" + group)
        view.add_regions(
            "GitConflictRegions_" + group,
            highlight_regions,
            "warning",
            icons[group],
            sublime.DRAW_NO_FILL |
            sublime.DRAW_NO_OUTLINE |
            sublime.HIDE_ON_MINIMAP
        )

def highlight_conflicts(view):
    conflict_regions = view.find_all(NO_NAMING_GROUPS_PATTERN)

    view.erase_regions("GitConflictRegions")
    view.add_regions(
        "GitConflictRegions",
        conflict_regions,
        settings['matching_scope'],
        "",
        (0 if settings['fill_conflict_area'] else sublime.DRAW_NO_FILL)
        |
        (0 if settings['outline_conflict_area'] else sublime.DRAW_NO_OUTLINE)
    )

    highlight_conflict_group(view, 'ours')
    highlight_conflict_group(view, 'ancestor')
    highlight_conflict_group(view, 'theirs')

def extract(view, region, keep):
    conflict_text = view.substr(region)
    match = CONFLICT_REGEX.search(conflict_text)

    # If we didn't matched the group return None
    if not match.group(keep):
        sublime.status_message(messages['no_such_group'])
        return None

    return CONFLICT_REGEX.sub(r'\g<' + keep + '>', conflict_text)

class FindNextConflict(sublime_plugin.TextCommand):
    def run(self, edit):
        current_selection = self.view.sel()

        # Use the end of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = self.view.sel()[-1].end()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        # Add the region to the selection
        self.view.show_at_center(conflict_region)
        current_selection.clear()
        current_selection.add(conflict_region)

class Keep(sublime_plugin.TextCommand):
    def run(self, edit, keep):
        current_selection = self.view.sel()

        # Use the begin of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = current_selection[0].begin()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        replace_text = extract(self.view, conflict_region, keep)

        if not replace_text:
            replace_text = ""

        self.view.replace(edit, conflict_region, replace_text)

class ListConflictFiles(sublime_plugin.WindowCommand):
    def run(self):
        window = self.window
        working_dir = self._determine_working_dir()
        conflict_files = None
        try:
            conflict_files = self._get_conflict_files(working_dir)
        except subprocess.CalledProcessError:
            # Git will execute with an error when the given directory
            # is not a repository, so we can savely ignore the error
            pass

        if not conflict_files:
            sublime.status_message(
                messages['no_conflict_found'] +
                ((" (" + working_dir + ")") if working_dir else "")
            )
            return

        conflict_files = conflict_files.decode('utf-8').split('\n')
        # Remove empty strings and sort the list
        # (TODO: sort also filenames only?)
        conflict_files = sorted([x for x in conflict_files if x])

        # Copy the list for representation
        show_files = list(conflict_files)

        if settings['show_only_filenames']:
            only_filenames = []
            for string in conflict_files:
                only_filenames.append(string.rpartition('/')[2])

            show_files = only_filenames

        # Add an "Open all ..." option
        show_files.insert(0, messages['open_all'])

        # Show the conflict files in the quickpanel and open them on selection
        def open_conflict(index):
            if index < 0:
                return
            elif index == 0:
                # Open all ...
                for file in conflict_files:
                    window.open_file(file)
            else:
                window.open_file(conflict_files[index - 1])

        window.show_quick_panel(show_files, open_conflict)

    def _get_conflict_files(self, working_dir):
        startupinfo = None
        # hide console window on windows
        if os.name == 'nt':
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

        # Search for conflicts using git executable
        return subprocess.check_output([
            settings['git_path'],
            "diff", "--name-only",
            "--diff-filter=U"
            ],
            cwd=working_dir,
            startupinfo=startupinfo
        )

    def _determine_working_dir(self):
        open_views = self.window.views()
        open_folders = self.window.folders()

        working_dir = None
        if open_views:
            # Remove the traling filename, we just need the folder
            working_dir = re.sub(r"/[^/]*$", "", open_views[0].file_name())
        elif open_folders:
            working_dir = open_folders[0]

        print(open_views, open_folders, working_dir)

        return working_dir

class ScanForConflicts(sublime_plugin.EventListener):
    def on_activated(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_load(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_pre_save(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

# ST3 automatically calls plugin_loaded when the API is ready
# For ST2 we have to call the function manually
if not int(sublime.version()) > 3000:
    plugin_loaded()
<commit_msg>Plugin: Fix path issues between nt and posix systems<commit_after>import sublime
import sublime_plugin
import os
import re
import subprocess

# view.find sadly can't handle naming groups
NO_NAMING_GROUPS_PATTERN = r"(?s)<{7}[^\n]*\n"\
                            "(.*?)(?:\|{7}[^\n]*\n"\
                            "(.*?))?={7}\n(.*?)>{7}[^\n]*\n"

CONFLICT_REGEX = re.compile(r"(?s)<{7}[^\n]*\n"
                            "(?P<ours>.*?)(?:\|{7}[^\n]*\n"
                            "(?P<ancestor>.*?))?={7}\n"
                            "(?P<theirs>.*?)>{7}[^\n]*\n")

# group patterns; this patterns always match the seperating lines too,
# so we have to remove then later from the matched regions
CONFLICT_GROUP_REGEX = {
    "ours": r"(?s)<{7}[^\n]*\n.*?\|{7}|>{7}",
    "ancestor": r"(?s)\|{7}[^\n]*\n.*?={7}",
    "theirs": r"(?s)={7}[^\n]*\n.*?>{7}"
}

icons = {
    "ours": "Packages/Git Conflict Resolver/gutter/ours.png",
    "ancestor": "Packages/Git Conflict Resolver/gutter/ancestor.png",
    "theirs": "Packages/Git Conflict Resolver/gutter/theirs.png"
}

messages = {
    "no_conflict_found": "No conflict found",
    "no_such_group": "No such conflict group found",
    "open_all": "Open all ..."
}

# Global settings
settings_file = "GitConflictResolver.sublime-settings"
settings = {}
default_settings = {
    "live_matching": True,
    "matching_scope": 'invalid',
    "fill_conflict_area": False,
    "outline_conflict_area": True,
    "ours_gutter": True,
    "ancestor_gutter": True,
    "theirs_gutter": True,
    "git_path": "git",
    "show_only_filenames": True
}

def plugin_loaded():
    global subl_settings, settings

    subl_settings = sublime.load_settings(settings_file)

    # Live matching
    settings['live_matching'] = subl_settings.get(
        'live_matching',
        default_settings['live_matching']
    )
    # Matching scope
    settings['matching_scope'] = subl_settings.get(
        'matching_scope',
        default_settings['matching_scope']
    )
    # Fill conflict area
    settings['fill_conflict_area'] = subl_settings.get(
        'fill_conflict_area',
        default_settings['fill_conflict_area']
    )
    # Outline conflict area
    settings['outline_conflict_area'] = subl_settings.get(
        'outline_conflict_area',
        default_settings['outline_conflict_area']
    )
    # Conflict group highlighting
    settings['ours_gutter'] = subl_settings.get(
        'ours_gutter',
        default_settings['ours_gutter']
    )
    settings['ancestor_gutter'] = subl_settings.get(
        'ancestor_gutter',
        default_settings['ancestor_gutter']
    )
    settings['theirs_gutter'] = subl_settings.get(
        'theirs_gutter',
        default_settings['theirs_gutter']
    )

    # Git Settings
    settings['git_path'] = subl_settings.get(
        'git_path',
        default_settings['git_path']
    )

    # Remove everything except for the part from the filename
    settings['show_only_filenames'] = subl_settings.get(
        'show_only_filenames',
        default_settings['show_only_filenames']
    )

def find_conflict(view, begin=0):
    conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, begin)

    if not conflict_region:
        conflict_region = view.find(NO_NAMING_GROUPS_PATTERN, 0)
        if not conflict_region:
            sublime.status_message(messages['no_conflict_found'])
            return None

    return conflict_region

def join_regions(regions):
    if not regions:
        return sublime.Region(-1, -1)

    begin = min([region.begin() for region in regions if region.begin() >= 0])
    end = max([region.end() for region in regions])

    return sublime.Region(begin, end)

def highlight_conflict_group(view, group):
    scope = group + '_gutter'
    if settings[scope]:
        conflict_regions = view.find_all(CONFLICT_GROUP_REGEX[group])

        if not conflict_regions:
            return

        # Remove the first and last line since they just contain the separators
        highlight_regions = []
        for region in conflict_regions:
            region = view.split_by_newlines(region)[1:-1]
            # Ignore empty subregions
            if not region:
                continue

            for subregion in region:
                highlight_regions.append(subregion)

        view.erase_regions("GitConflictRegion_" + group)
        view.add_regions(
            "GitConflictRegions_" + group,
            highlight_regions,
            "warning",
            icons[group],
            sublime.DRAW_NO_FILL |
            sublime.DRAW_NO_OUTLINE |
            sublime.HIDE_ON_MINIMAP
        )

def highlight_conflicts(view):
    conflict_regions = view.find_all(NO_NAMING_GROUPS_PATTERN)

    view.erase_regions("GitConflictRegions")
    view.add_regions(
        "GitConflictRegions",
        conflict_regions,
        settings['matching_scope'],
        "",
        (0 if settings['fill_conflict_area'] else sublime.DRAW_NO_FILL)
        |
        (0 if settings['outline_conflict_area'] else sublime.DRAW_NO_OUTLINE)
    )

    highlight_conflict_group(view, 'ours')
    highlight_conflict_group(view, 'ancestor')
    highlight_conflict_group(view, 'theirs')

def extract(view, region, keep):
    conflict_text = view.substr(region)
    match = CONFLICT_REGEX.search(conflict_text)

    # If we didn't matched the group return None
    if not match.group(keep):
        sublime.status_message(messages['no_such_group'])
        return None

    return CONFLICT_REGEX.sub(r'\g<' + keep + '>', conflict_text)

class FindNextConflict(sublime_plugin.TextCommand):
    def run(self, edit):
        current_selection = self.view.sel()

        # Use the end of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = self.view.sel()[-1].end()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        # Add the region to the selection
        self.view.show_at_center(conflict_region)
        current_selection.clear()
        current_selection.add(conflict_region)

class Keep(sublime_plugin.TextCommand):
    def run(self, edit, keep):
        current_selection = self.view.sel()

        # Use the begin of the current selection for the search, or use 0 if nothing is selected
        begin = 0
        if len(current_selection) > 0:
            begin = current_selection[0].begin()

        conflict_region = find_conflict(self.view, begin)
        if conflict_region is None:
            return

        replace_text = extract(self.view, conflict_region, keep)

        if not replace_text:
            replace_text = ""

        self.view.replace(edit, conflict_region, replace_text)

class ListConflictFiles(sublime_plugin.WindowCommand):
    def run(self):
        window = self.window
        working_dir = self._determine_working_dir()
        conflict_files = None
        try:
            conflict_files = self._get_conflict_files(working_dir)
        except subprocess.CalledProcessError:
            # Git will execute with an error when the given directory
            # is not a repository, so we can savely ignore the error
            pass

        if not conflict_files:
            sublime.status_message(
                messages['no_conflict_found'] +
                ((" (" + working_dir + ")") if working_dir else "")
            )
            return

        conflict_files = conflict_files.decode('utf-8').split('\n')
        # Remove empty strings and sort the list
        # (TODO: sort also filenames only?)
        conflict_files = sorted([x for x in conflict_files if x])

        # Copy the list for representation
        show_files = list(conflict_files)

        if settings['show_only_filenames']:
            only_filenames = []
            for string in conflict_files:
                only_filenames.append(string.rpartition('/')[2])

            show_files = only_filenames

        # Add an "Open all ..." option
        show_files.insert(0, messages['open_all'])

        # Show the conflict files in the quickpanel and open them on selection
        def open_conflict(index):
            if index < 0:
                return
            elif index == 0:
                # Open all ...
                for file in conflict_files:
                    window.open_file(file)
            else:
                window.open_file(conflict_files[index - 1])

        window.show_quick_panel(show_files, open_conflict)

    def _get_conflict_files(self, working_dir):
        startupinfo = None
        # hide console window on windows
        if os.name == 'nt':
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

        # Search for conflicts using git executable
        return subprocess.check_output([
            settings['git_path'],
            "diff", "--name-only",
            "--diff-filter=U"
            ],
            cwd=working_dir,
            startupinfo=startupinfo
        )

    def _determine_working_dir(self):
        open_views = self.window.views()
        open_folders = self.window.folders()

        working_dir = None
        if open_views:
            # Remove the traling filename, we just need the folder
            working_dir = re.sub(r"[/\\][^\\/]*$",
                                 "",
                                 open_views[0].file_name())
        elif open_folders:
            working_dir = open_folders[0]

        return working_dir

class ScanForConflicts(sublime_plugin.EventListener):
    def on_activated(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_load(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

    def on_pre_save(self, view):
        if settings['live_matching']:
            highlight_conflicts(view)

# ST3 automatically calls plugin_loaded when the API is ready
# For ST2 we have to call the function manually
if not int(sublime.version()) > 3000:
    plugin_loaded()
<|endoftext|>


<commit_before># coding=utf-8
from sii import __SII_VERSION__
from sii.models import invoices_record

SIGN = {'B': -1, 'A': -1, 'N': 1, 'R': 1}

def get_importe_no_sujeto_a_iva(invoice):
    importe_no_sujeto = 0

    for line in invoice.invoice_line:
        no_iva = True
        for tax in line.invoice_line_tax_id:
            if 'iva' in tax.name.lower():
                no_iva = False
                break
        if no_iva:
            importe_no_sujeto += line.price_subtotal

    return importe_no_sujeto

def get_iva_values(invoice, in_invoice):
    vals = {
        'sujeta_a_iva': False,
        'detalle_iva': [],
        'no_sujeta_a_iva': False
    }
    for tax in invoice.tax_line:
        if 'iva' in tax.name.lower():
            iva = {
                'BaseImponible': SIGN[invoice.rectificative_type] * tax.base,
                'TipoImpositivo': tax.tax_id.amount * 100
            }
            if in_invoice:
                iva.update({
                    'CuotaRepercutida':
                        SIGN[invoice.rectificative_type] * tax.tax_amount
                })
            else:
                iva.update({
                    'CuotaSoportada':
                        SIGN[invoice.rectificative_type] * tax.tax_amount
                })
            vals['sujeta_a_iva'] = True
            vals['detalle_iva'].append(iva)
        else:
            vals['no_sujeta_a_iva'] = True
    return vals

def get_factura_emitida(invoice):
    iva_values = get_iva_values(invoice, in_invoice=True)
    desglose_factura = {}

    if iva_values['sujeta_a_iva']:
        desglose_factura['Sujeta'] = {
            'NoExenta': {  # TODO Exenta o no exenta??
                'TipoNoExenta': 'S1',
                'DesgloseIVA': {
                    'DetalleIVA': iva_values['detalle_iva']
                }
            }
        }
    if iva_values['no_sujeta_a_iva']:
        importe_no_sujeto = get_importe_no_sujeto_a_iva(invoice)
        if 'islas canarias' not in invoice.fiscal_position.name.lower():
            desglose_factura['NoSujeta'] = {
                'ImportePorArticulos7_14_Otros': importe_no_sujeto
            }
        else:
            desglose_factura['NoSujeta'] = {
                'ImporteTAIReglasLocalizacion': importe_no_sujeto
            }

    factura_expedida = {
        'TipoFactura': 'R4' if invoice.rectificative_type == 'R' else 'F1',
        'ClaveRegimenEspecialOTrascendencia':
            invoice.fiscal_position.sii_out_clave_regimen_especial,
        'ImporteTotal': SIGN[invoice.rectificative_type] * invoice.amount_total,
        'DescripcionOperacion': invoice.name,
        'Contraparte': {
            'NombreRazon': invoice.partner_id.name,
            'NIF': invoice.partner_id.vat
        },
        'TipoDesglose': {
            'DesgloseFactura': desglose_factura
        }
    }

    return factura_expedida

def get_factura_recibida(invoice):
    iva_values = get_iva_values(invoice, in_invoice=False)
    cuota_deducible = 0

    if iva_values['sujeta_a_iva']:
        desglose_factura = {  # TODO to change
            # 'InversionSujetoPasivo': {
            #     'DetalleIVA': iva_values['detalle_iva']
            # },
            'DesgloseIVA': {
                'DetalleIVA': iva_values['detalle_iva']
            }
        }

        for detalle_iva in iva_values['detalle_iva']:
            cuota_deducible += detalle_iva['CuotaSoportada']
    else:
        desglose_factura = {
            'DesgloseIVA': {
                'DetalleIVA': {
                    'BaseImponible': 0  # TODO deixem de moment 0 perquè no tindrem inversio sujeto pasivo
                }
            }
        }

    factura_recibida = {
        'TipoFactura': 'R4' if invoice.rectificative_type == 'R' else 'F1',
        'ClaveRegimenEspecialOTrascendencia':
            invoice.fiscal_position.sii_in_clave_regimen_especial,
        'ImporteTotal': SIGN[invoice.rectificative_type] * invoice.amount_total,
        'DescripcionOperacion': invoice.name,
        'Contraparte': {
            'NombreRazon': invoice.partner_id.name,
            'NIF': invoice.partner_id.vat
        },
        'DesgloseFactura': desglose_factura,
        'CuotaDeducible': cuota_deducible,
        'FechaRegContable': '2017-12-31'  # TODO to change
    }

    return factura_recibida

def get_header(invoice):
    cabecera = {
        'IDVersionSii': __SII_VERSION__,
        'Titular': {
            'NombreRazon': invoice.company_id.partner_id.name,
            'NIF': invoice.company_id.partner_id.vat
        },
        'TipoComunicacion': 'A0' if not invoice.sii_sent else 'A1'
    }

    return cabecera

def get_factura_rectificativa_fields():
    rectificativa_fields = {
        'TipoRectificativa': 'S',  # Por sustitución
        'ImporteRectificacion': {
            'BaseRectificada': 0,
            'CuotaRectificada': 0
        }
    }

    return rectificativa_fields

def get_factura_emitida_dict(invoice, rectificativa=False):
    obj = {
        'SuministroLRFacturasEmitidas': {
            'Cabecera': get_header(invoice),
            'RegistroLRFacturasEmitidas': {
                'PeriodoImpositivo': {
                    'Ejercicio': invoice.period_id.name[3:7],
                    'Periodo': invoice.period_id.name[0:2]
                },
                'IDFactura': {
                    'IDEmisorFactura': {
                        'NIF': invoice.company_id.partner_id.vat
                    },
                    'NumSerieFacturaEmisor': invoice.number,
                    'FechaExpedicionFacturaEmisor': invoice.date_invoice
                },
                'FacturaExpedida': get_factura_emitida(invoice)
            }
        }
    }

    if rectificativa:
        vals = get_factura_rectificativa_fields()

        (
            obj['SuministroLRFacturasEmitidas']['RegistroLRFacturasEmitidas']
            ['FacturaExpedida']
        ).update(vals)

    return obj

def get_factura_recibida_dict(invoice, rectificativa=False):
    obj = {
        'SuministroLRFacturasRecibidas': {
            'Cabecera': get_header(invoice),
            'RegistroLRFacturasRecibidas': {
                'PeriodoImpositivo': {
                    'Ejercicio': invoice.period_id.name[3:7],
                    'Periodo': invoice.period_id.name[0:2]
                },
                'IDFactura': {
                    'IDEmisorFactura': {
                        'NIF': invoice.partner_id.vat
                    },
                    'NumSerieFacturaEmisor': invoice.number,
                    'FechaExpedicionFacturaEmisor': invoice.date_invoice
                },
                'FacturaRecibida': get_factura_recibida(invoice)
            }
        }
    }

    if rectificativa:
        vals = get_factura_rectificativa_fields()

        (
            obj['SuministroLRFacturasRecibidas']['RegistroLRFacturasRecibidas']
            ['FacturaRecibida']
        ).update(vals)

    return obj

class SII(object):
    @staticmethod
    def generate_object(invoice):

        rectificativa = invoice.rectificative_type == 'R'
        if invoice.type.startswith('in'):
            invoice_model = invoices_record.SuministroFacturasRecibidas()
            invoice_dict = get_factura_recibida_dict(
                invoice, rectificativa=rectificativa
            )
        elif invoice.type.startswith('out'):
            invoice_model = invoices_record.SuministroFacturasEmitidas()
            invoice_dict = get_factura_emitida_dict(
                invoice, rectificativa=rectificativa
            )
        else:
            raise AttributeError('Unknown value in invoice.type')

        errors = invoice_model.validate(invoice_dict)
        if errors:
            raise Exception(
                'Errors were found while trying to validate the data:', errors)

        res = invoice_model.dump(invoice_dict)
        if res.errors:
            raise Exception(
                'Errors were found while trying to generate the dump:', errors)
        return res.data
<commit_msg>Use Journal description instead of invoice descr<commit_after># coding=utf-8
from sii import __SII_VERSION__
from sii.models import invoices_record

SIGN = {'B': -1, 'A': -1, 'N': 1, 'R': 1}

def get_importe_no_sujeto_a_iva(invoice):
    importe_no_sujeto = 0

    for line in invoice.invoice_line:
        no_iva = True
        for tax in line.invoice_line_tax_id:
            if 'iva' in tax.name.lower():
                no_iva = False
                break
        if no_iva:
            importe_no_sujeto += line.price_subtotal

    return importe_no_sujeto

def get_iva_values(invoice, in_invoice):
    vals = {
        'sujeta_a_iva': False,
        'detalle_iva': [],
        'no_sujeta_a_iva': False
    }
    for tax in invoice.tax_line:
        if 'iva' in tax.name.lower():
            iva = {
                'BaseImponible': SIGN[invoice.rectificative_type] * tax.base,
                'TipoImpositivo': tax.tax_id.amount * 100
            }
            if in_invoice:
                iva.update({
                    'CuotaRepercutida':
                        SIGN[invoice.rectificative_type] * tax.tax_amount
                })
            else:
                iva.update({
                    'CuotaSoportada':
                        SIGN[invoice.rectificative_type] * tax.tax_amount
                })
            vals['sujeta_a_iva'] = True
            vals['detalle_iva'].append(iva)
        else:
            vals['no_sujeta_a_iva'] = True
    return vals

def get_factura_emitida(invoice):
    iva_values = get_iva_values(invoice, in_invoice=True)
    desglose_factura = {}

    if iva_values['sujeta_a_iva']:
        desglose_factura['Sujeta'] = {
            'NoExenta': {  # TODO Exenta o no exenta??
                'TipoNoExenta': 'S1',
                'DesgloseIVA': {
                    'DetalleIVA': iva_values['detalle_iva']
                }
            }
        }
    if iva_values['no_sujeta_a_iva']:
        importe_no_sujeto = get_importe_no_sujeto_a_iva(invoice)
        if 'islas canarias' not in invoice.fiscal_position.name.lower():
            desglose_factura['NoSujeta'] = {
                'ImportePorArticulos7_14_Otros': importe_no_sujeto
            }
        else:
            desglose_factura['NoSujeta'] = {
                'ImporteTAIReglasLocalizacion': importe_no_sujeto
            }

    factura_expedida = {
        'TipoFactura': 'R4' if invoice.rectificative_type == 'R' else 'F1',
        'ClaveRegimenEspecialOTrascendencia':
            invoice.fiscal_position.sii_out_clave_regimen_especial,
        'ImporteTotal': SIGN[invoice.rectificative_type] * invoice.amount_total,
        'DescripcionOperacion': invoice.journal_id.name,
        'Contraparte': {
            'NombreRazon': invoice.partner_id.name,
            'NIF': invoice.partner_id.vat
        },
        'TipoDesglose': {
            'DesgloseFactura': desglose_factura
        }
    }

    return factura_expedida

def get_factura_recibida(invoice):
    iva_values = get_iva_values(invoice, in_invoice=False)
    cuota_deducible = 0

    if iva_values['sujeta_a_iva']:
        desglose_factura = {  # TODO to change
            # 'InversionSujetoPasivo': {
            #     'DetalleIVA': iva_values['detalle_iva']
            # },
            'DesgloseIVA': {
                'DetalleIVA': iva_values['detalle_iva']
            }
        }

        for detalle_iva in iva_values['detalle_iva']:
            cuota_deducible += detalle_iva['CuotaSoportada']
    else:
        desglose_factura = {
            'DesgloseIVA': {
                'DetalleIVA': {
                    'BaseImponible': 0  # TODO deixem de moment 0 perquè no tindrem inversio sujeto pasivo
                }
            }
        }

    factura_recibida = {
        'TipoFactura': 'R4' if invoice.rectificative_type == 'R' else 'F1',
        'ClaveRegimenEspecialOTrascendencia':
            invoice.fiscal_position.sii_in_clave_regimen_especial,
        'ImporteTotal': SIGN[invoice.rectificative_type] * invoice.amount_total,
        'DescripcionOperacion': invoice.journal_id.name,
        'Contraparte': {
            'NombreRazon': invoice.partner_id.name,
            'NIF': invoice.partner_id.vat
        },
        'DesgloseFactura': desglose_factura,
        'CuotaDeducible': cuota_deducible,
        'FechaRegContable': '2017-12-31'  # TODO to change
    }

    return factura_recibida

def get_header(invoice):
    cabecera = {
        'IDVersionSii': __SII_VERSION__,
        'Titular': {
            'NombreRazon': invoice.company_id.partner_id.name,
            'NIF': invoice.company_id.partner_id.vat
        },
        'TipoComunicacion': 'A0' if not invoice.sii_sent else 'A1'
    }

    return cabecera

def get_factura_rectificativa_fields():
    rectificativa_fields = {
        'TipoRectificativa': 'S',  # Por sustitución
        'ImporteRectificacion': {
            'BaseRectificada': 0,
            'CuotaRectificada': 0
        }
    }

    return rectificativa_fields

def get_factura_emitida_dict(invoice, rectificativa=False):
    obj = {
        'SuministroLRFacturasEmitidas': {
            'Cabecera': get_header(invoice),
            'RegistroLRFacturasEmitidas': {
                'PeriodoImpositivo': {
                    'Ejercicio': invoice.period_id.name[3:7],
                    'Periodo': invoice.period_id.name[0:2]
                },
                'IDFactura': {
                    'IDEmisorFactura': {
                        'NIF': invoice.company_id.partner_id.vat
                    },
                    'NumSerieFacturaEmisor': invoice.number,
                    'FechaExpedicionFacturaEmisor': invoice.date_invoice
                },
                'FacturaExpedida': get_factura_emitida(invoice)
            }
        }
    }

    if rectificativa:
        vals = get_factura_rectificativa_fields()

        (
            obj['SuministroLRFacturasEmitidas']['RegistroLRFacturasEmitidas']
            ['FacturaExpedida']
        ).update(vals)

    return obj

def get_factura_recibida_dict(invoice, rectificativa=False):
    obj = {
        'SuministroLRFacturasRecibidas': {
            'Cabecera': get_header(invoice),
            'RegistroLRFacturasRecibidas': {
                'PeriodoImpositivo': {
                    'Ejercicio': invoice.period_id.name[3:7],
                    'Periodo': invoice.period_id.name[0:2]
                },
                'IDFactura': {
                    'IDEmisorFactura': {
                        'NIF': invoice.partner_id.vat
                    },
                    'NumSerieFacturaEmisor': invoice.number,
                    'FechaExpedicionFacturaEmisor': invoice.date_invoice
                },
                'FacturaRecibida': get_factura_recibida(invoice)
            }
        }
    }

    if rectificativa:
        vals = get_factura_rectificativa_fields()

        (
            obj['SuministroLRFacturasRecibidas']['RegistroLRFacturasRecibidas']
            ['FacturaRecibida']
        ).update(vals)

    return obj

class SII(object):
    @staticmethod
    def generate_object(invoice):

        rectificativa = invoice.rectificative_type == 'R'
        if invoice.type.startswith('in'):
            invoice_model = invoices_record.SuministroFacturasRecibidas()
            invoice_dict = get_factura_recibida_dict(
                invoice, rectificativa=rectificativa
            )
        elif invoice.type.startswith('out'):
            invoice_model = invoices_record.SuministroFacturasEmitidas()
            invoice_dict = get_factura_emitida_dict(
                invoice, rectificativa=rectificativa
            )
        else:
            raise AttributeError('Unknown value in invoice.type')

        errors = invoice_model.validate(invoice_dict)
        if errors:
            raise Exception(
                'Errors were found while trying to validate the data:', errors)

        res = invoice_model.dump(invoice_dict)
        if res.errors:
            raise Exception(
                'Errors were found while trying to generate the dump:', errors)
        return res.data
<|endoftext|>


<commit_before>#Author : Lewis Mervin lhm30@cam.ac.uk
#Supervisor : Dr. A. Bender
#All rights reserved 2016
#Protein Target Prediction Tool trained on SARs from PubChem (Mined 21/06/16) and ChEMBL21
#Molecular Descriptors : 2048bit Morgan Binary Fingerprints (Rdkit) - ECFP4
#Dependencies : rdkit, sklearn, numpy

#libraries
from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit import DataStructs
import cPickle
import zipfile
import glob
import os
import sys
import math
import numpy as np
from multiprocessing import Pool
import multiprocessing
multiprocessing.freeze_support()

def introMessage():
	print '=============================================================================================='
	print ' Author: Lewis Mervin\n Email:  lhm30@cam.ac.uk\n Supervisor: Dr. A. Bender'
	print ' Address: Centre For Molecular Informatics, Dept. Chemistry, Lensfield Road, Cambridge CB2 1EW'
	print '==============================================================================================\n'
	return

#calculate 2048bit morgan fingerprints, radius 2
def calcFingerprints(smiles):
	m1 = Chem.MolFromSmiles(smiles)
	fp = AllChem.GetMorganFingerprintAsBitVect(m1,2, nBits=2048)
	return fp

#calculate fingerprints for chunked array of smiles
def arrayFP(inp):
	outfp = []
	outsmi = []
	for i in inp:
		try:
			outfp.append(calcFingerprints(i))
			outsmi.append(i)
		except:
			print 'SMILES Parse Error: ' + i
	return outfp,outsmi

#import user query
def importQuery(in_file):
	query = open(in_file).read().splitlines()
	smiles_per_core = int(math.ceil(len(query) / N_cores)+1)
	chunked_smiles = [query[x:x+smiles_per_core] for x in xrange(0, len(query), smiles_per_core)]
	pool = Pool(processes=N_cores)  # set up resources
	jobs = pool.imap(arrayFP, chunked_smiles)
	processed_fp = []
	processed_smi = []
	for i, result in enumerate(jobs):
		processed_fp += result[0]
		processed_smi += result[1]
	pool.close()
	pool.join()
	return processed_fp, processed_smi

#get info for uniprots
def getUniprotInfo():
	if os.name == 'nt': sep = '\\'
	else: sep = '/'
	model_info = [l.split('\t') for l in open(os.path.dirname(os.path.abspath(__file__)) + sep + 'classes_in_model.txt').read().splitlines()]
	return_dict = {l[0] : l[0:7] for l in model_info}
	return return_dict

#sim worker
def doSimSearch(model_name):
	if os.name == 'nt': sep = '\\'
	else: sep = '/'
	mod = model_name.split(sep)[-1].split('.')[0]
	try:
		with zipfile.ZipFile(os.path.dirname(os.path.abspath(__file__)) + sep + 'actives' + sep + mod + '.smi.zip', 'r') as zfile:
			comps = [i.split('\t') for i in zfile.open(mod + '.smi', 'r').read().splitlines()]
	except IOError: return
	comps2 = []
	afp = []
	for comp in comps:
		try:
			afp.append(calcFingerprints(comp[1]))
			comps2.append(comp)
		except: pass
	ret = []
	for i,fp in enumerate(querymatrix):
		sims = DataStructs.BulkTanimotoSimilarity(fp,afp)
		idx = sims.index(max(sims))
		ret.append([sims[idx], mod] + comps2[idx] + [smiles[i]])
	return ret

#prediction runner
def performSimSearch(models):
	sims_results = []
	pool = Pool(processes=N_cores, initializer=initPool, initargs=(querymatrix,))  # set up resources
	jobs = pool.imap_unordered(doSimSearch, models)
	out_file2.write('Uniprot\tPref_Name\tGene ID\tTarget_Class\tOrganism\tPDB_ID\tDisGeNET_Diseases_0.06\t' + '\t'.join(map(str,smiles)) + '\n')
	for i, result in enumerate(jobs):
		percent = (float(i)/float(len(models)))*100 + 1
		sys.stdout.write(' Calculating Sims for Query Molecules: %3d%%\r' % percent)
		sys.stdout.flush()
		if result is not None:
			sims_results += result
			out_file2.write('\t'.join(map(str,model_info[result[0][1]])))
			for sim in result:
				out_file2.write('\t' + str(round(sim[0],3)))
			out_file2.write('\n')
	pool.close()
	pool.join()
	return sims_results

#initializer for the pool
def initPool(querymatrix_):
	global querymatrix
	querymatrix = querymatrix_

#main
if __name__ == '__main__':
	if os.name == 'nt': sep = '\\'
	else: sep = '/'
	input_name = sys.argv[1]
	N_cores = int(sys.argv[2])
	introMessage()
	print ' Calculating Near-Neighbors for ' + input_name
	print ' Using ' + str(N_cores) + ' Cores'
	models = [modelfile for modelfile in glob.glob(os.path.dirname(os.path.abspath(__file__)) + sep + 'models' + sep + '*.zip')]
	model_info = getUniprotInfo()
	print ' Total Number of Classes : ' + str(len(models))
	output_name = input_name + '_out_similarity_details.txt'
	output_name2 = input_name + '_out_similarity_matrix.txt'
	out_file = open(output_name, 'w')
	out_file2 = open(output_name2, 'w')
	querymatrix,smiles = importQuery(input_name)
	print ' Total Number of Query Molecules : ' + str(len(querymatrix))
	sims_results = performSimSearch(models)
	out_file.write('Uniprot\tPref_Name\tGene ID\tTarget_Class\tOrganism\tNear_Neighbor_ChEMBLID\tNear_Neighbor_Smiles\tNear_Neighbor_Bioactive_organism\tNear_Neighbor_conf_score\tNN_activity\tNN_Units\tInput_Compound\tSimilarity\n')
	for row in sorted(sims_results,reverse=True):
		out_file.write('\t'.join(map(str,model_info[row[1]][:5])) + '\t' + '\t'.join(map(str,row[2:])) + '\t' + str(row[0]) + '\n')
	print '\n Wrote Results to: ' + output_name
	print ' Wrote Results to: ' + output_name2
	out_file.close()<commit_msg>specify organism update<commit_after>#Author : Lewis Mervin lhm30@cam.ac.uk
#Supervisor : Dr. A. Bender
#All rights reserved 2016
#Protein Target Prediction Tool trained on SARs from PubChem (Mined 21/06/16) and ChEMBL21
#Molecular Descriptors : 2048bit Morgan Binary Fingerprints (Rdkit) - ECFP4
#Dependencies : rdkit, sklearn, numpy

#libraries
from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit import DataStructs
import cPickle
import zipfile
import glob
import os
import sys
import math
import numpy as np
from multiprocessing import Pool
import multiprocessing
multiprocessing.freeze_support()

def introMessage():
	print '=============================================================================================='
	print ' Author: Lewis Mervin\n Email:  lhm30@cam.ac.uk\n Supervisor: Dr. A. Bender'
	print ' Address: Centre For Molecular Informatics, Dept. Chemistry, Lensfield Road, Cambridge CB2 1EW'
	print '==============================================================================================\n'
	return

#calculate 2048bit morgan fingerprints, radius 2
def calcFingerprints(smiles):
	m1 = Chem.MolFromSmiles(smiles)
	fp = AllChem.GetMorganFingerprintAsBitVect(m1,2, nBits=2048)
	return fp

#calculate fingerprints for chunked array of smiles
def arrayFP(inp):
	outfp = []
	outsmi = []
	for i in inp:
		try:
			outfp.append(calcFingerprints(i))
			outsmi.append(i)
		except:
			print 'SMILES Parse Error: ' + i
	return outfp,outsmi

#import user query
def importQuery(in_file):
	query = open(in_file).read().splitlines()
	smiles_per_core = int(math.ceil(len(query) / N_cores)+1)
	chunked_smiles = [query[x:x+smiles_per_core] for x in xrange(0, len(query), smiles_per_core)]
	pool = Pool(processes=N_cores)  # set up resources
	jobs = pool.imap(arrayFP, chunked_smiles)
	processed_fp = []
	processed_smi = []
	for i, result in enumerate(jobs):
		processed_fp += result[0]
		processed_smi += result[1]
	pool.close()
	pool.join()
	return processed_fp, processed_smi

#get info for uniprots
def getUniprotInfo():
	if os.name == 'nt': sep = '\\'
	else: sep = '/'
	model_info = [l.split('\t') for l in open(os.path.dirname(os.path.abspath(__file__)) + sep + 'classes_in_model.txt').read().splitlines()]
	return_dict = {l[0] : l[0:7] for l in model_info}
	return return_dict

#sim worker
def doSimSearch(model_name):
	if os.name == 'nt': sep = '\\'
	else: sep = '/'
	mod = model_name.split(sep)[-1].split('.')[0]
	try:
		with zipfile.ZipFile(os.path.dirname(os.path.abspath(__file__)) + sep + 'actives' + sep + mod + '.smi.zip', 'r') as zfile:
			comps = [i.split('\t') for i in zfile.open(mod + '.smi', 'r').read().splitlines()]
	except IOError: return
	comps2 = []
	afp = []
	for comp in comps:
		try:
			afp.append(calcFingerprints(comp[1]))
			comps2.append(comp)
		except: pass
	ret = []
	for i,fp in enumerate(querymatrix):
		sims = DataStructs.BulkTanimotoSimilarity(fp,afp)
		idx = sims.index(max(sims))
		ret.append([sims[idx], mod] + comps2[idx] + [smiles[i]])
	return ret

#prediction runner
def performSimSearch(models):
	sims_results = []
	pool = Pool(processes=N_cores, initializer=initPool, initargs=(querymatrix,smiles))  # set up resources
	jobs = pool.imap_unordered(doSimSearch, models)
	out_file2.write('Uniprot\tPref_Name\tGene ID\tTarget_Class\tOrganism\tPDB_ID\tDisGeNET_Diseases_0.06\t' + '\t'.join(map(str,smiles)) + '\n')
	for i, result in enumerate(jobs):
		percent = (float(i)/float(len(models)))*100 + 1
		sys.stdout.write(' Calculating Sims for Query Molecules: %3d%%\r' % percent)
		sys.stdout.flush()
		if result is not None:
			sims_results += result
			out_file2.write('\t'.join(map(str,model_info[result[0][1]])))
			for sim in result:
				out_file2.write('\t' + str(round(sim[0],3)))
			out_file2.write('\n')
	pool.close()
	pool.join()
	return sims_results

#initializer for the pool
def initPool(querymatrix_,smiles_):
	global querymatrix, smiles
	querymatrix = querymatrix_
	smiles = smiles_

#main
if __name__ == '__main__':
	if os.name == 'nt': sep = '\\'
	else: sep = '/'
	input_name = sys.argv[1]
	N_cores = int(sys.argv[2])
	try:
		desired_organism = sys.argv[3]
	except IndexError:
		desired_organism = None
	introMessage()
	print ' Calculating Near-Neighbors for ' + input_name
	print ' Using ' + str(N_cores) + ' Cores'
	models = [modelfile for modelfile in glob.glob(os.path.dirname(os.path.abspath(__file__)) + sep + 'models' + sep + '*.zip')]
	model_info = getUniprotInfo()

	if desired_organism is not None:
		models = [mod for mod in models if model_info[mod.split(sep)[-1].split('.')[0]][4] == desired_organism]
		output_name = input_name + '_out_similarity_details' + desired_organism[:3] + '.txt'
		output_name2 = input_name + '_out_similarity_matrix' + desired_organism[:3] + '.txt'
		print ' Predicting for organism : ' + desired_organism
	else:
		output_name = input_name + '_out_similarity_details.txt'
		output_name2 = input_name + '_out_similarity_matrix.txt'
	print ' Total Number of Classes : ' + str(len(models))
	output_name = input_name + '_out_similarity_details.txt'
	output_name2 = input_name + '_out_similarity_matrix.txt'
	out_file = open(output_name, 'w')
	out_file2 = open(output_name2, 'w')
	querymatrix,smiles = importQuery(input_name)
	print ' Total Number of Query Molecules : ' + str(len(querymatrix))
	sims_results = performSimSearch(models)
	out_file.write('Uniprot\tPref_Name\tGene ID\tTarget_Class\tOrganism\tNear_Neighbor_ChEMBLID\tNear_Neighbor_Smiles\tNear_Neighbor_Bioactive_organism\tNear_Neighbor_conf_score\tNN_activity\tNN_Units\tInput_Compound\tSimilarity\n')
	for row in sorted(sims_results,reverse=True):
		out_file.write('\t'.join(map(str,model_info[row[1]][:5])) + '\t' + '\t'.join(map(str,row[2:])) + '\t' + str(row[0]) + '\n')
	print '\n Wrote Results to: ' + output_name
	print ' Wrote Results to: ' + output_name2
	out_file.close()<|endoftext|>