<commit_before>import re

from markdown.preprocessors import Preprocessor
from markdown.postprocessors import Postprocessor
from markdown.extensions import Extension

class Todo(Extension):
    class Prep(Preprocessor):

        """
        Markdown preprocessor that matches all TODO and FIXME strings occurring at the beginning\
        of the line or after the `#` symbol and highlights them in the documentation.
        """

        matched_strings = ["TODO", "FIXME", "WARNING", "CAUTION"]
        regex = re.compile("(#|^)\s*((" + "|".join(matched_strings) + ")(:?.+)?)")

        def template(self, match):
            """
            Intended markup for TODO strings. The type of the string is used as a class.
            """
            return "{:s}{:s}".format(match.group(1),
                                                             match.group(3).lower(), match.group(2))

        def run(self, lines):
            """
            String matching is case insensitive
            """
            return [self.regex.sub(self.template, line, re.I) for line in lines]

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('todo', Todo.Prep(md), '_end')

class LineConnector(Extension):

    def __init__(self, regex=r"(\S)\s*\\\s*\n\s*(\S)", sub=r"\1 \2"):

        regex = re.compile(regex, flags=re.M)

        class Post(Postprocessor):

            def run(self, lines):
                """ Method ensures that there is exactly one space between 2 joined strings. """
                return regex.sub(sub, "\n".join(lines)).split("\n")
                # new_lines = []
                # append = False
                # for line in lines:
                #     if append and (not ignore_empty or line.strip()):
                #         new_lines[-1] = regex.sub(sub, new_lines[-1]) + ' ' + line.lstrip()
                #         append = False
                #     else:
                #         new_lines.append(line)

                #     if regex.search(line):
                #         append = True

                # return new_lines

        self.post = Post()

        super(LineConnector, self).__init__()

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('line-connector', self.post, '_end')

class SaneDefList(Extension):
    class Prep(Preprocessor):

        """
        Markdown preprocessor that prepares natural-style definition lists for native Markdown \
        extension `def_list`. It allows to write more compact and readable class field definitions.
        """

        def run(self, lines):
            """
            Searches for a line starting with a literal followed by a colon and multiple spaces:

                some arbitrary parameter name:  and its definition separated by `:\s\s+`
                `parameter name` might contain\
                everything except a \
                [colon](//en.wikipedia.org/wiki/Colon_(punctuation))\
                and be multiline:               it still works

            And turns it into:

            some arbitrary parameter name:  and its definition separated by `:\s\s+`
            `parameter name` might contain\
            everything except a \
            [colon](//en.wikipedia.org/wiki/Colon_(punctuation))\
            and be multiline:               it still works
            """
            new_lines = []
            for line in lines:
                match = re.match(r'^(\s*)([^:]+):\s{2,}(.+)', line)
                if match:
                    new_lines.append(match.group(1) + match.group(2))
                    new_lines.append(match.group(1) + ':   ' + match.group(3))
                    new_lines.append('')
                else:
                    new_lines.append(line)

            return new_lines

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('sane-def-list', SaneDefList.Prep(md), '_end')

class Pydoc(Extension):
    class Prep(Postprocessor):

        """
        Preprocessor used to parse PyDoc-style comments like `:param name:` and format them.
        """

        def run(self, lines):
            """
            :param text: Markdown-formatted text
            :return: Text with parsed PyDoc comments
            """
            new_lines = []
            for text in lines:
                # Regex that matches `@param name`
                text = re.sub(
                    r'^(\s*)@(\w+)\s+(["\'\`].+["\'\`]|\S+)\s*',
                    r'\1\2 \3 ',
                    text, re.M)
                # Regex that matches `@var`
                text = re.sub(
                    r'^(\s*)@(\w+)',
                    r'\1\2',
                    text, re.M)
                # Regex that matches `:param name:`
                text = re.sub(
                    r'^(\s*):([^: ]+) +([^:]+):',
                    r'\1\2 \3',
                    text, re.M)
                # Regex that matches single-word comments: `:return:`
                text = re.sub(
                    r'^(\s*):([^: ]+):',
                    r'\1\2',
                    text, re.M)

                new_lines.append(text)
            return new_lines

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('pydoc', Pydoc.Prep(md), '_end')
<commit_msg>Change a bit styling of TODOs, fix misuse of markdown.postprocessor<commit_after>import re

from markdown.preprocessors import Preprocessor
from markdown.postprocessors import Postprocessor
from markdown.extensions import Extension

class Todo(Extension):
    class Prep(Preprocessor):

        """
        Markdown preprocessor that matches all TODO and FIXME strings occurring at the beginning\
        of the line or after the `#` symbol and highlights them in the documentation.
        """

        matched_strings = ["TODO", "FIXME", "WARNING", "CAUTION"]
        regex = re.compile("(#|^)\s*(" + "|".join(matched_strings) + ":?)(.*)", flags=re.I)

        def template(self, match):
            """
            Intended markup for TODO strings. The type of the string is used as a class.
            """
            return "{:s}{:s}{:s}"\
                .format(match.group(1), match.group(2).lower(), match.group(2), match.group(3))

        def run(self, lines):
            """
            String matching is case insensitive
            """
            return [self.regex.sub(self.template, line, re.I) for line in lines]

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('todo', Todo.Prep(md), '_end')

class LineConnector(Extension):

    def __init__(self, regex=r"(\S)\s*\\\s*\n\s*(\S)", sub=r"\1 \2"):

        regex = re.compile(regex, flags=re.M)

        class Prep(Preprocessor):

            def run(self, lines):
                """ Method ensures that there is exactly one space between 2 joined strings. """
                return regex.sub(sub, "\n".join(lines)).split("\n")

        self.Prep = Prep()

        super(LineConnector, self).__init__()

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('line-connector', self.Prep, '_end')

class SaneDefList(Extension):
    class Prep(Preprocessor):

        """
        Markdown preprocessor that prepares natural-style definition lists for native Markdown \
        extension `def_list`. It allows to write more compact and readable class field definitions.
        """

        def run(self, lines):
            """
            Searches for a line starting with a literal followed by a colon and multiple spaces:

                some arbitrary parameter name:  and its definition separated by `:\s\s+`
                `parameter name` might contain\
                everything except a \
                [colon](//en.wikipedia.org/wiki/Colon_(punctuation))\
                and be multiline:               it still works

            And turns it into:

            some arbitrary parameter name:  and its definition separated by `:\s\s+`
            `parameter name` might contain\
            everything except a \
            [colon](//en.wikipedia.org/wiki/Colon_(punctuation))\
            and be multiline:               it still works
            """
            new_lines = []
            for line in lines:
                match = re.match(r'^(\s*)([^:]+):\s{2,}(.+)', line)
                if match:
                    new_lines.append(match.group(1) + match.group(2))
                    new_lines.append(match.group(1) + ':   ' + match.group(3))
                    new_lines.append('')
                else:
                    new_lines.append(line)

            return new_lines

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('sane-def-list', SaneDefList.Prep(md), '_end')

class Pydoc(Extension):
    class Prep(Postprocessor):

        """
        Preprocessor used to parse PyDoc-style comments like `:param name:` and format them.
        """

        def run(self, lines):
            """
            :param text: Markdown-formatted text
            :return: Text with parsed PyDoc comments
            """
            new_lines = []
            for text in lines:
                # Regex that matches `@param name`
                text = re.sub(
                    r'^(\s*)@(\w+)\s+(["\'\`].+["\'\`]|\S+)\s*',
                    r'\1\2 \3 ',
                    text, re.M)
                # Regex that matches `@var`
                text = re.sub(
                    r'^(\s*)@(\w+)',
                    r'\1\2',
                    text, re.M)
                # Regex that matches `:param name:`
                text = re.sub(
                    r'^(\s*):([^: ]+) +([^:]+):',
                    r'\1\2 \3',
                    text, re.M)
                # Regex that matches single-word comments: `:return:`
                text = re.sub(
                    r'^(\s*):([^: ]+):',
                    r'\1\2',
                    text, re.M)

                new_lines.append(text)
            return new_lines

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('pydoc', Pydoc.Prep(md), '_end')
<|endoftext|>


<commit_before># ===============================================================================
# Copyright 2015 Jake Ross
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================

# ============= enthought library imports =======================
from pyface.tasks.action.schema import SToolBar, SMenu
from pyface.tasks.action.schema_addition import SchemaAddition
from pyface.tasks.task_layout import TaskLayout, PaneItem, Splitter
from traits.api import Instance, Bool, on_trait_change
# ============= standard library imports ========================
from itertools import groupby
# ============= local library imports  ==========================
from pychron.core.helpers.filetools import list_gits
from pychron.dvc.dvc import experiment_has_staged, push_experiments
from pychron.globals import globalv
from pychron.paths import paths
from pychron.pipeline.engine import PipelineEngine
from pychron.pipeline.plot.editors.interpreted_age_editor import InterpretedAgeEditor
from pychron.pipeline.state import EngineState
from pychron.pipeline.tasks.actions import RunAction, SavePipelineTemplateAction, ResumeAction, ResetAction, \
    ConfigureRecallAction, GitRollbackAction, TagAction, SetInterpretedAgeAction, ClearAction
from pychron.pipeline.tasks.panes import PipelinePane, AnalysesPane
from pychron.envisage.browser.browser_task import BaseBrowserTask
from pychron.pipeline.plot.editors.figure_editor import FigureEditor
from pychron.pipeline.tasks.select_repo import SelectExperimentIDView
from pychron.processing.tasks.figures.interpreted_age_factory import InterpretedAgeFactory

class DataMenu(SMenu):
    id = 'data.menu'
    name = 'Data'

def select_experiment_repo():
    a = list_gits(paths.experiment_dataset_dir)
    v = SelectExperimentIDView(available=a)
    info = v.edit_traits()
    if info.result:
        return v.selected

class PipelineTask(BaseBrowserTask):
    name = 'Pipeline Processing'
    engine = Instance(PipelineEngine, ())
    tool_bars = [SToolBar(RunAction(),
                          ResumeAction(),
                          ResetAction(),
                          ClearAction(),
                          ConfigureRecallAction(),
                          SavePipelineTemplateAction()),
                 SToolBar(GitRollbackAction()),
                 SToolBar(TagAction(),
                          SetInterpretedAgeAction())]

    state = Instance(EngineState)
    resume_enabled = Bool(False)
    run_enabled = Bool(True)
    set_interpreted_enabled = Bool(False)
    run_to = None

    modified = False
    dbmodified = False
    projects = None

    _temp_state = None
    # reset_enabled = Bool(False)

    def run(self):
        self._run_pipeline()

    def activated(self):
        super(PipelineTask, self).activated()

        self.engine.dvc = self.dvc
        self.engine.browser_model = self.browser_model

        self.engine.add_data()

    def _debug(self):
        # self.engine.add_data()
        self.engine.select_default()
        self.engine.set_template('ideogram')
        # self.engine.set_template('gain')
        # self.engine.set_template('series')
        # self.engine.set_template('blanks')
        # self.engine.set_template('flux')
        # self.engine.add_is
        # self.engine.add_grouping(run=False)
        # self.engine.add_test_filter()
        # self.engine.add_ideogram(run=False)
        # self.engine.add_series(run=False)

        # self.engine.add_test_filter()
        # self.engine.add_ideogram()
        # self.engine.add_pdf_figure_node()
        # self.engine.add_spectrum()

        # self.run()

    def prepare_destroy(self):
        pass

    def create_dock_panes(self):
        panes = [PipelinePane(model=self.engine),
                 AnalysesPane(model=self.engine)]
        return panes

    # toolbar actions
    def set_interpreted_age(self):
        ias = self.active_editor.get_interpreted_ages()
        iaf = InterpretedAgeFactory(groups=ias)
        info = iaf.edit_traits()
        if info.result:
            self._add_interpreted_ages(ias)

    def git_rollback(self):
        # select experiment
        expid = select_experiment_repo()
        if expid:
            self.dvc.rollback_experiment_repo(expid)

    def clear(self):
        self.reset()
        self.engine.clear()
        self.close_all()

    def reset(self):
        self.run_enabled = True
        self.resume_enabled = False
        self._temp_state = None
        self.state = None
        self.engine.reset()

    def save_pipeline_template(self):
        path = self.save_file_dialog(default_directory=paths.pipeline_template_dir)
        # path = '/Users/ross/Sandbox/template.yaml'
        # path = os.path.join(paths.pipeline_template_dir, 'test.yaml')
        if path:
            self.engine.save_pipeline_template(path)

    # action handlers
    def set_ideogram_template(self):
        self.engine.set_template('ideogram')
        self.run()

    def set_spectrum_template(self):
        self.engine.set_template('spectrum')
        self.run()

    def set_isochron_template(self):
        self.engine.set_template('isochron')
        self.run()

    def set_series_template(self):
        print 'set series template'
        self.engine.set_template('series')
        self.run()

    # private
    def _add_interpreted_ages(self, ias):
        dvc = self.dvc
        db = dvc.db
        with db.session_ctx():
            for ia in ias:
                if ia.use:
                    dvc.add_interpreted_age(ia)

    def _close_editor(self, editor):
        for e in self.editor_area.editors:
            if e.name == editor.name:
                self.close_editor(e)
                break

    def _run_pipeline(self):
        self.debug('run pipeline')
        if self.state:
            self.debug('using previous state')
            state = self.state
        else:
            state = EngineState()
            self.close_all()

        self.state = state
        self._temp_state = state

        # if not self.engine.pre_run(state, self.run_to):
        #     self.state = None
        #     self._temp_state = None

        if not self.engine.run(state, self.run_to):
            self._toggle_run(True)
        else:
            self.engine.post_run(state)

            self._toggle_run(False)
            self.state = None
            self.engine.state = None

        for editor in state.editors:
            self._open_editor(editor)

        # self.engine.selected = None
        # self.engine.update_needed = True
        # self.engine.update_needed = True
        # self.engine.refresh_analyses()

        if state.dbmodified:
            self.dbmodified = True

    def _toggle_run(self, v):
        self.resume_enabled = v
        self.run_enabled = not v

    def _sa_factory(self, path, factory, **kw):
        return SchemaAddition(path=path, factory=factory, **kw)

    # defaults
    def _default_layout_default(self):
        return TaskLayout(left=Splitter(PaneItem('pychron.pipeline.pane',
                                                 width=200),
                                        PaneItem('pychron.pipeline.analyses',
                                                 width=200)))

    def _extra_actions_default(self):
        sas = (('MenuBar/data.menu', RunAction, {}),)
        return [self._sa_factory(path, factory, **kw) for path, factory, kw in sas]

    # handlers
    @on_trait_change('engine:reset_event')
    def _handle_reset(self):
        self.reset()

    def _active_editor_changed(self, new):
        if new:
            self.engine.select_node_by_editor(new)

        self.set_interpreted_enabled = isinstance(new, InterpretedAgeEditor)

    @on_trait_change('active_editor:save_needed')
    def _handle_save_needed(self):
        self.engine.run_persist(self._temp_state)

    @on_trait_change('engine:[tag_event, invalid_event, recall_event]')
    def _handle_analysis_tagging(self, name, new):
        if name == 'tag_event':
            self.set_tag(items=new)
        elif name == 'invalid_event':
            tag = {'name': 'invalid', 'omit_ideo': True, 'omit_series': True,
                   'omit_spec': True, 'omit_iso': True}
            self.set_tag(tag=tag, items=new, warn=True)
        elif name == 'recall_event':
            self.recall(new)

    @on_trait_change('engine:run_needed')
    def _handle_run_needed(self, new):
        self.debug('run needed for {}'.format(new))
        self.run()

    @on_trait_change('engine:recall_analyses_needed')
    def _handle_recall(self, new):
        self.recall(new)

    def _prompt_for_save(self):
        ret = True
        ps = self.engine.get_experiment_ids()

        if ps:
            changed = experiment_has_staged(ps)
            self.debug('task has changes to {}'.format(changed))
            if changed:
                m = 'You have changes to analyses. Would you like to share them?'
                ret = self._handle_prompt_for_save(m, 'Share Changes')
                if ret == 'save':
                    push_experiments(changed)

        return ret

    def _opened_hook(self):
        super(PipelineTask, self)._opened_hook()
        if globalv.pipeline_debug:
            self._debug()

    def set_tag(self, tag=None, items=None, use_filter=True, warn=False):
        """
            set tag for either
            analyses selected in unknowns pane
            or
            analyses selected in figure e.g temp_status!=0

        """
        if items is None:
            items = self._get_selection()
            if not items:
                if warn:
                    self.warning_dialog('No analyses selected to Tag')
                    return

        if items:
            name = None
            if tag is None:
                a = self._get_tagname(items)
                if a:
                    tag, items, use_filter = a
                    if tag:
                        name = tag.name
            else:
                name = tag.name

            # set tags for items
            if name and items:
                dvc = self.dvc
                db = dvc.db
                key = lambda x: x.experiment_identifier

                for expid, ans in groupby(sorted(items, key=key), key=key):
                    # repo = dvc.get_experiment_repo(expid)
                    # with dvc.git_session_ctx(expid, 'Updated tags'):
                    cs = []
                    with db.session_ctx():
                        for it in ans:
                            self.debug('setting {} tag= {}'.format(it.record_id, name))
                            db.set_analysis_tag(it.uuid, name)

                            it.set_tag(tag)
                            if dvc.update_tag(it):
                                cs.append(it)
                                # it.refresh_view()
                    if cs:
                        cc = [c.record_id for c in cs]
                        if len(cc) > 1:
                            cstr = '{} - {}'.format(cc[0], cc[-1])
                        else:
                            cstr = cc[0]
                        dvc.experiment_commit(expid, '<TAG> {:<6s} {}'.format(name, cstr))
                        for ci in cs:
                            ci.refresh_view()

                if use_filter:
                    for e in self.editor_area.editors:
                        if isinstance(e, FigureEditor):
                            e.set_items([ai for ai in e.analyses if ai.tag != 'invalid'])
                #
                if self.active_editor:
                    self.active_editor.refresh_needed = True

                self.browser_model.analysis_table.refresh_needed = True
                self.engine.refresh_table_needed = True
        else:
            # edit tags
            self._get_tagname([])

    def _get_selection(self):
        items = None
        unks = self.engine.selected_unknowns
        if unks:
            if not items:
                items = unks

            if not items:
                items = [i for i in unks if i.is_temp_omitted()]
                self.debug('Temp omitted analyses {}'.format(len(items)))

        if not items:
            items = self.browser_model.analysis_table.selected
        return items

    def _get_tagname(self, items):
        from pychron.processing.tagging.analysis_tags import AnalysisTagModel
        from pychron.processing.tagging.views import AnalysisTagView

        # tv = self._tag_table_view
        # if not tv:
        tv = AnalysisTagView(model=AnalysisTagModel())

        db = self.dvc.db
        with db.session_ctx():
            # tv = TagTableView(items=items)
            tv.model.db = db
            tv.model.items = items
            tv.model.load()

        info = tv.edit_traits()
        if info.result:
            tag = tv.model.selected
            # self._tag_table_view = tv
            return tag, tv.model.items, tv.model.use_filter

    def _get_dr_tagname(self, items):
        from pychron.processing.tagging.data_reduction_tags import DataReductionTagModel
        from pychron.processing.tagging.views import DataReductionTagView

        tv = DataReductionTagView(model=DataReductionTagModel(items=items))
        info = tv.edit_traits()
        if info.result:
            return tv.model

# ============= EOF =============================================
#  _delete_flag = False
#     _delete_cnt = 0
#     _cnt = 0
#
#     def _handle_items(self, sel, items):
#         if self.active_editor:
#             if not self._delete_flag:
#                 self._delete_cnt = len(sel) + 1
#                 self._cnt = 1
#                 refresh = False
#                 self._delete_flag = True
#             else:
#                 self._cnt += 1
#                 refresh = self._cnt >= self._delete_cnt
#
#             if refresh:
#                 self.active_editor.set_items(items)
#                 self.active_editor.refresh_needed = True
#                 self._delete_flag = False
#                 self._delete_cnt = 0
# @on_trait_change('engine:unknowns')
# def _handle_unknowns(self, obj, name, old, new):
#     print 'ffsaafa', name, old, new
#     if name == 'unknowns_items':
#         self._handle_items(self.engine.selected_unknowns, self.engine.unknowns)
#     self.engine.update_detectors()

# @on_trait_change('engine:references')
# def _handle_references(self, name, old, new):
#     if name == 'references_items':
#         self._handle_items(self.engine.selected_references, self.engine.references)
#     self.engine.update_detectors()
# if self.active_editor:
#     # only update if deletion
#     if not new:
#         self.active_editor.set_references(self.engine.references)
#         self.active_editor.refresh_needed = True
# self.engine.update_detectors()
<commit_msg>fixed issue #574<commit_after># ===============================================================================
# Copyright 2015 Jake Ross
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================

# ============= enthought library imports =======================
from pyface.tasks.action.schema import SToolBar, SMenu
from pyface.tasks.action.schema_addition import SchemaAddition
from pyface.tasks.task_layout import TaskLayout, PaneItem, Splitter
from traits.api import Instance, Bool, on_trait_change
# ============= standard library imports ========================
from itertools import groupby
# ============= local library imports  ==========================
from pychron.core.helpers.filetools import list_gits
from pychron.dvc.dvc import experiment_has_staged, push_experiments
from pychron.globals import globalv
from pychron.paths import paths
from pychron.pipeline.engine import PipelineEngine
from pychron.pipeline.plot.editors.interpreted_age_editor import InterpretedAgeEditor
from pychron.pipeline.state import EngineState
from pychron.pipeline.tasks.actions import RunAction, SavePipelineTemplateAction, ResumeAction, ResetAction, \
    ConfigureRecallAction, GitRollbackAction, TagAction, SetInterpretedAgeAction, ClearAction
from pychron.pipeline.tasks.panes import PipelinePane, AnalysesPane
from pychron.envisage.browser.browser_task import BaseBrowserTask
from pychron.pipeline.plot.editors.figure_editor import FigureEditor
from pychron.pipeline.tasks.select_repo import SelectExperimentIDView
from pychron.processing.tasks.figures.interpreted_age_factory import InterpretedAgeFactory

class DataMenu(SMenu):
    id = 'data.menu'
    name = 'Data'

def select_experiment_repo():
    a = list_gits(paths.experiment_dataset_dir)
    v = SelectExperimentIDView(available=a)
    info = v.edit_traits()
    if info.result:
        return v.selected

class PipelineTask(BaseBrowserTask):
    name = 'Pipeline Processing'
    engine = Instance(PipelineEngine, ())
    tool_bars = [SToolBar(RunAction(),
                          ResumeAction(),
                          ResetAction(),
                          ClearAction(),
                          ConfigureRecallAction(),
                          SavePipelineTemplateAction()),
                 SToolBar(GitRollbackAction()),
                 SToolBar(TagAction(),
                          SetInterpretedAgeAction())]

    state = Instance(EngineState)
    resume_enabled = Bool(False)
    run_enabled = Bool(True)
    set_interpreted_enabled = Bool(False)
    run_to = None

    modified = False
    dbmodified = False
    projects = None

    _temp_state = None
    # reset_enabled = Bool(False)

    def run(self):
        self._run_pipeline()

    def activated(self):
        super(PipelineTask, self).activated()

        self.engine.dvc = self.dvc
        self.engine.browser_model = self.browser_model

        self.engine.add_data()

    def _debug(self):
        # self.engine.add_data()
        self.engine.select_default()
        self.engine.set_template('ideogram')
        # self.engine.set_template('gain')
        # self.engine.set_template('series')
        # self.engine.set_template('blanks')
        # self.engine.set_template('flux')
        # self.engine.add_is
        # self.engine.add_grouping(run=False)
        # self.engine.add_test_filter()
        # self.engine.add_ideogram(run=False)
        # self.engine.add_series(run=False)

        # self.engine.add_test_filter()
        # self.engine.add_ideogram()
        # self.engine.add_pdf_figure_node()
        # self.engine.add_spectrum()

        # self.run()

    def prepare_destroy(self):
        pass

    def create_dock_panes(self):
        panes = [PipelinePane(model=self.engine),
                 AnalysesPane(model=self.engine)]
        return panes

    # toolbar actions
    def set_interpreted_age(self):
        ias = self.active_editor.get_interpreted_ages()
        iaf = InterpretedAgeFactory(groups=ias)
        info = iaf.edit_traits()
        if info.result:
            self._add_interpreted_ages(ias)

    def git_rollback(self):
        # select experiment
        expid = select_experiment_repo()
        if expid:
            self.dvc.rollback_experiment_repo(expid)

    def clear(self):
        self.reset()
        self.engine.clear()
        self.close_all()

    def reset(self):
        self.run_enabled = True
        self.resume_enabled = False
        self._temp_state = None
        self.state = None
        self.engine.reset()

    def save_pipeline_template(self):
        path = self.save_file_dialog(default_directory=paths.pipeline_template_dir)
        # path = '/Users/ross/Sandbox/template.yaml'
        # path = os.path.join(paths.pipeline_template_dir, 'test.yaml')
        if path:
            self.engine.save_pipeline_template(path)

    # action handlers
    def set_ideogram_template(self):
        self.engine.set_template('ideogram')
        self.run()

    def set_spectrum_template(self):
        self.engine.set_template('spectrum')
        self.run()

    def set_isochron_template(self):
        self.engine.set_template('isochron')
        self.run()

    def set_series_template(self):
        print 'set series template'
        self.engine.set_template('series')
        self.run()

    # private
    def _add_interpreted_ages(self, ias):
        dvc = self.dvc
        db = dvc.db
        with db.session_ctx():
            for ia in ias:
                if ia.use:
                    dvc.add_interpreted_age(ia)

    def _close_editor(self, editor):
        for e in self.editor_area.editors:
            if e.name == editor.name:
                self.close_editor(e)
                break

    def _run_pipeline(self):
        self.debug('run pipeline')
        if self.state:
            self.debug('using previous state')
            state = self.state
        else:
            state = EngineState()
            self.close_all()

        self.state = state
        self._temp_state = state

        # if not self.engine.pre_run(state, self.run_to):
        #     self.state = None
        #     self._temp_state = None

        if not self.engine.run(state, self.run_to):
            self._toggle_run(True)
        else:
            self.engine.post_run(state)

            self._toggle_run(False)
            self.state = None
            self.engine.state = None

        for editor in state.editors:
            self._open_editor(editor)

        # self.engine.selected = None
        # self.engine.update_needed = True
        # self.engine.update_needed = True
        # self.engine.refresh_analyses()

        if state.dbmodified:
            self.dbmodified = True

    def _toggle_run(self, v):
        self.resume_enabled = v
        self.run_enabled = not v

    def _sa_factory(self, path, factory, **kw):
        return SchemaAddition(path=path, factory=factory, **kw)

    # defaults
    def _default_layout_default(self):
        return TaskLayout(left=Splitter(PaneItem('pychron.pipeline.pane',
                                                 width=200),
                                        PaneItem('pychron.pipeline.analyses',
                                                 width=200)))

    def _extra_actions_default(self):
        sas = (('MenuBar/data.menu', RunAction, {}),)
        return [self._sa_factory(path, factory, **kw) for path, factory, kw in sas]

    # handlers
    @on_trait_change('engine:reset_event')
    def _handle_reset(self):
        self.reset()

    def _active_editor_changed(self, new):
        if new:
            self.engine.select_node_by_editor(new)

        self.set_interpreted_enabled = isinstance(new, InterpretedAgeEditor)

    @on_trait_change('active_editor:save_needed')
    def _handle_save_needed(self):
        self.engine.run_persist(self._temp_state)

    @on_trait_change('engine:[tag_event, invalid_event, recall_event]')
    def _handle_analysis_tagging(self, name, new):
        if name == 'tag_event':
            self.set_tag(items=new)
        elif name == 'invalid_event':
            tag = {'name': 'invalid', 'omit_ideo': True, 'omit_series': True,
                   'omit_spec': True, 'omit_iso': True}
            self.set_tag(tag=tag, items=new, warn=True)
        elif name == 'recall_event':
            self.recall(new)

    @on_trait_change('engine:run_needed')
    def _handle_run_needed(self, new):
        self.debug('run needed for {}'.format(new))
        self.run()

    @on_trait_change('engine:recall_analyses_needed')
    def _handle_recall(self, new):
        self.recall(new)

    def _prompt_for_save(self):
        ret = True
        ps = self.engine.get_experiment_ids()

        if ps:
            changed = experiment_has_staged(ps)
            self.debug('task has changes to {}'.format(changed))
            if changed:
                m = 'You have changes to analyses. Would you like to share them?'
                ret = self._handle_prompt_for_save(m, 'Share Changes')
                if ret == 'save':
                    push_experiments(changed)

        return ret

    def _opened_hook(self):
        super(PipelineTask, self)._opened_hook()
        if globalv.pipeline_debug:
            self._debug()

    def set_tag(self, tag=None, items=None, use_filter=True, warn=False):
        """
            set tag for either
            analyses selected in unknowns pane
            or
            analyses selected in figure e.g temp_status!=0

        """
        if items is None:
            items = self._get_selection()
            if not items:
                if warn:
                    self.warning_dialog('No analyses selected to Tag')
                    return

        if items:
            name = None
            if tag is None:
                a = self._get_tagname(items)
                if a:
                    tag, items, use_filter = a
                    if tag:
                        name = tag.name
            else:
                name = tag['name']

            # set tags for items
            if name and items:
                dvc = self.dvc
                db = dvc.db
                key = lambda x: x.experiment_identifier

                for expid, ans in groupby(sorted(items, key=key), key=key):
                    # repo = dvc.get_experiment_repo(expid)
                    # with dvc.git_session_ctx(expid, 'Updated tags'):
                    cs = []
                    with db.session_ctx():
                        for it in ans:
                            self.debug('setting {} tag= {}'.format(it.record_id, name))
                            db.set_analysis_tag(it.uuid, name)

                            it.set_tag(tag)
                            if dvc.update_tag(it):
                                cs.append(it)
                                # it.refresh_view()
                    if cs:
                        cc = [c.record_id for c in cs]
                        if len(cc) > 1:
                            cstr = '{} - {}'.format(cc[0], cc[-1])
                        else:
                            cstr = cc[0]
                        dvc.experiment_commit(expid, '<TAG> {:<6s} {}'.format(name, cstr))
                        for ci in cs:
                            ci.refresh_view()

                if use_filter:
                    for e in self.editor_area.editors:
                        if isinstance(e, FigureEditor):
                            e.set_items([ai for ai in e.analyses if ai.tag != 'invalid'])
                #
                if self.active_editor:
                    self.active_editor.refresh_needed = True

                self.browser_model.analysis_table.refresh_needed = True
                self.engine.refresh_table_needed = True
        else:
            # edit tags
            self._get_tagname([])

    def _get_selection(self):
        items = None
        unks = self.engine.selected_unknowns
        if unks:
            if not items:
                items = unks

            if not items:
                items = [i for i in unks if i.is_temp_omitted()]
                self.debug('Temp omitted analyses {}'.format(len(items)))

        if not items:
            items = self.browser_model.analysis_table.selected
        return items

    def _get_tagname(self, items):
        from pychron.processing.tagging.analysis_tags import AnalysisTagModel
        from pychron.processing.tagging.views import AnalysisTagView

        # tv = self._tag_table_view
        # if not tv:
        tv = AnalysisTagView(model=AnalysisTagModel())

        db = self.dvc.db
        with db.session_ctx():
            # tv = TagTableView(items=items)
            tv.model.db = db
            tv.model.items = items
            tv.model.load()

        info = tv.edit_traits()
        if info.result:
            tag = tv.model.selected
            # self._tag_table_view = tv
            return tag, tv.model.items, tv.model.use_filter

    def _get_dr_tagname(self, items):
        from pychron.processing.tagging.data_reduction_tags import DataReductionTagModel
        from pychron.processing.tagging.views import DataReductionTagView

        tv = DataReductionTagView(model=DataReductionTagModel(items=items))
        info = tv.edit_traits()
        if info.result:
            return tv.model

# ============= EOF =============================================
#  _delete_flag = False
#     _delete_cnt = 0
#     _cnt = 0
#
#     def _handle_items(self, sel, items):
#         if self.active_editor:
#             if not self._delete_flag:
#                 self._delete_cnt = len(sel) + 1
#                 self._cnt = 1
#                 refresh = False
#                 self._delete_flag = True
#             else:
#                 self._cnt += 1
#                 refresh = self._cnt >= self._delete_cnt
#
#             if refresh:
#                 self.active_editor.set_items(items)
#                 self.active_editor.refresh_needed = True
#                 self._delete_flag = False
#                 self._delete_cnt = 0
# @on_trait_change('engine:unknowns')
# def _handle_unknowns(self, obj, name, old, new):
#     print 'ffsaafa', name, old, new
#     if name == 'unknowns_items':
#         self._handle_items(self.engine.selected_unknowns, self.engine.unknowns)
#     self.engine.update_detectors()

# @on_trait_change('engine:references')
# def _handle_references(self, name, old, new):
#     if name == 'references_items':
#         self._handle_items(self.engine.selected_references, self.engine.references)
#     self.engine.update_detectors()
# if self.active_editor:
#     # only update if deletion
#     if not new:
#         self.active_editor.set_references(self.engine.references)
#         self.active_editor.refresh_needed = True
# self.engine.update_detectors()
<|endoftext|>


<commit_before>#!/usr/bin/env python

from common import *
from operator import add
from collections import MutableMapping

from scipy.sparse import block_diag

from pydigree.pedigree import Pedigree

class PedigreeCollection(MutableMapping):

    def __init__(self):
        self.pedigrees = {}

    # Things I have to implement for the ABC
    ###
    def __iter__(self):
        return (x for x in self.pedigrees.values())

    def __getitem__(self, key):
        if isinstance(key, tuple) or isinstance(key, list):
            return self.pedigrees[key[0]][key[1]]
        return self.pedigrees[key]

    def __contains__(self, item):
        return item in self.pedigrees.values()

    def __len__(self):
        return len(self.pedigrees)

    def __setitem__(self, key, value):
        self.pedigrees[key] = value

    def __delitem__(self, key):
        del self.pedigrees[key]

    def keys(self):
        return self.pedigrees.keys()

    def add_pedigree(self, ped):
        if not isinstance(ped, Pedigree):
            raise ValueError('{} not of type Pedigree')
        elif ped.label in self.keys():
            raise ValueError('A pedigree labeled {} already in collection'.format(ped.label))
        else:
            self[ped.label] = ped

    @property
    def individuals(self):
        '''
        Returns a list of the individuals represented by all pedigrees,
        sorted by pedigree label, id label
        '''
        inds = []
        for pedigree in sorted(self, key=lambda x: x.label):
            inds.extend(sorted((x for x in pedigree), key=lambda x: x.label))
        return inds

    def founders(self):
        ''' Returns a list of founder individuals across all pedigrees '''
        return [x for x in self.individuals if x.is_founder()]

    def nonfounders(self):
        ''' Returns a list of founder individuals across all pedigrees '''
        return [x for x in self.individuals if not x.is_founder()]

    def _getindividual(self, label):
        for x in self.individuals:
            if x.label == label:
                return x
        raise KeyError('Individual not in collection')

    def phenotypes(self):
        """ Returns the available phenotypes for analysis """
        return set(reduce(add, [x.phenotypes.keys() for x in
                                self.individuals]))

    @property
    def chromosomes(self):
        k = self.pedigrees.keys()[0]
        return self.pedigrees[k].chromosomes

    def add_chromosome(self, chrom):
        for x in self:
            x.add_chromosome(chrom)

    def clear_genotypes(self):
        for x in self.individuals:
            x.clear_genotypes()

    def get_founder_genotypes(self):
        for x in self:
            x.get_founder_genotypes()

    def get_genotypes(self):
        for x in self:
            x.get_genotypes()

    def merge(self, pop):
        for ped in self:
            ped.merge(pop)

    # Matrix functions
    ###
    def additive_relationship_matrix(self, ids=None):
        """
        Returns a block diagonal matrix of additive relationships
        for each pedigree.

        See notes on Pedigree.additive_relationship_matrix
        """
        return block_diag([x.additive_relationship_matrix(ids) for x in
                           sorted(self, key=lambda x: x.label)], format='bsr')

    def dominance_relationship_matrix(self, ids=None):
        """
        Returns a block diagonal matrix of dominance relationships
        for each pedigree.

        See notes on Pedigree.dominance_relationship_matrix
        """
        return block_diag([x.dominance_relationship_matrix(ids) for x in
                           sorted(self, key=lambda x: x.label)], format='bsr')

    def mitochondrial_relationship_matrix(self, ids=None):
        """
        Returns a block diagonal matrix of mitochondrial relationships
        for each pedigree.

        See notes on Pedigree.mitochondrial_relationship_matrix
        """
        return block_diag([x.mitochondrial_relationship_matrix(ids) for x in
                           sorted(self, key=lambda x: x.label)], format='bsr')
<commit_msg>bugfix for when some pedigrees are skipped completely<commit_after>#!/usr/bin/env python

from common import *
from operator import add
from collections import MutableMapping

from scipy.sparse import block_diag

from pydigree.pedigree import Pedigree

class PedigreeCollection(MutableMapping):

    def __init__(self):
        self.pedigrees = {}

    # Things I have to implement for the ABC
    ###
    def __iter__(self):
        return (x for x in self.pedigrees.values())

    def __getitem__(self, key):
        if isinstance(key, tuple) or isinstance(key, list):
            return self.pedigrees[key[0]][key[1]]
        return self.pedigrees[key]

    def __contains__(self, item):
        return item in self.pedigrees.values()

    def __len__(self):
        return len(self.pedigrees)

    def __setitem__(self, key, value):
        self.pedigrees[key] = value

    def __delitem__(self, key):
        del self.pedigrees[key]

    def keys(self):
        return self.pedigrees.keys()

    def add_pedigree(self, ped):
        if not isinstance(ped, Pedigree):
            raise ValueError('{} not of type Pedigree')
        elif ped.label in self.keys():
            raise ValueError('A pedigree labeled {} already in collection'.format(ped.label))
        else:
            self[ped.label] = ped

    @property
    def individuals(self):
        '''
        Returns a list of the individuals represented by all pedigrees,
        sorted by pedigree label, id label
        '''
        inds = []
        for pedigree in sorted(self, key=lambda x: x.label):
            inds.extend(sorted((x for x in pedigree), key=lambda x: x.label))
        return inds

    def founders(self):
        ''' Returns a list of founder individuals across all pedigrees '''
        return [x for x in self.individuals if x.is_founder()]

    def nonfounders(self):
        ''' Returns a list of founder individuals across all pedigrees '''
        return [x for x in self.individuals if not x.is_founder()]

    def _getindividual(self, label):
        for x in self.individuals:
            if x.label == label:
                return x
        raise KeyError('Individual not in collection')

    def phenotypes(self):
        """ Returns the available phenotypes for analysis """
        return set(reduce(add, [x.phenotypes.keys() for x in
                                self.individuals]))

    @property
    def chromosomes(self):
        k = self.pedigrees.keys()[0]
        return self.pedigrees[k].chromosomes

    def add_chromosome(self, chrom):
        for x in self:
            x.add_chromosome(chrom)

    def clear_genotypes(self):
        for x in self.individuals:
            x.clear_genotypes()

    def get_founder_genotypes(self):
        for x in self:
            x.get_founder_genotypes()

    def get_genotypes(self):
        for x in self:
            x.get_genotypes()

    def merge(self, pop):
        for ped in self:
            ped.merge(pop)

    # Matrix functions
    ###
    def additive_relationship_matrix(self, ids=None):
        """
        Returns a block diagonal matrix of additive relationships
        for each pedigree.

        See notes on Pedigree.additive_relationship_matrix
        """
        mats = [x.additive_relationship_matrix(ids) for x in
                sorted(self, key=lambda x: x.label)]
        mats = [x for x in mats if x.size > 0]
        return block_diag(mats, format='bsr')

    def dominance_relationship_matrix(self, ids=None):
        """
        Returns a block diagonal matrix of dominance relationships
        for each pedigree.

        See notes on Pedigree.dominance_relationship_matrix
        """
        mats = [x.dominance_relationship_matrix(ids) for x in
                sorted(self, key=lambda x: x.label)]
        mats = [x for x in mats if x.size > 0]
        return block_diag(mats, format='bsr')

    def mitochondrial_relationship_matrix(self, ids=None):
        """
        Returns a block diagonal matrix of mitochondrial relationships
        for each pedigree.

        See notes on Pedigree.mitochondrial_relationship_matrix
        """
        mats = [x.mitochondrial_relationship_matrix(ids) for x in
                sorted(self, key=lambda x: x.label)]
        return block_diag(mats, format='bsr')
<|endoftext|>


<commit_before># No shebang line, this module is meant to be imported
#
# Copyright 2014 Ambient Entertainment GmbH & Co. KG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Job Queues
----------

This module defines an API for managing and querying job queues
"""
try:
    from httplib import OK, CREATED, CONFLICT, NOT_FOUND
except ImportError:  # pragma: no cover
    from http.client import OK, CREATED, CONFLICT, NOT_FOUND

from flask import g
from flask.views import MethodView

from pyfarm.core.logger import getLogger
from pyfarm.core.enums import STRING_TYPES

from pyfarm.models.job import Job
from pyfarm.models.jobqueue import JobQueue
from pyfarm.master.application import db
from pyfarm.master.utility import jsonify, validate_with_model

logger = getLogger("api.jobqueues")

def schema():
    """
    Returns the basic schema of :class:`.JobQueue`

    .. http:get:: /api/v1/jobqueues/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/jobqueues/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "id": "INTEGER",
                "name": VARCHAR(255)",
                "minimum_agents": "INTEGER",
                "maximum_agents": "INTEGER",
                "priority": "INTEGER",
                "weight": "INTEGER",
                "parent_jobqueue_id": "INTEGER"
            }

    :statuscode 200: no error
    """
    return jsonify(JobQueue.to_schema()), OK

class JobQueueIndexAPI(MethodView):
    @validate_with_model(JobQueue)
    def post(self):
        """
        A ``POST`` to this endpoint will create a new job queue.

        .. http:post:: /api/v1/jobqueues/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                POST /api/v1/jobqueues/ HTTP/1.1
                Accept: application/json

                {
                    "name": "Test Queue"
                }

            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 CREATED
                Content-Type: application/json

                {
                    "weight": 10,
                    "jobs": [],
                    "minimum_agents": null,
                    "priority": 5,
                    "name": "Test Queue",
                    "maximum_agents": null,
                    "id": 1,
                    "parent": [],
                    "parent_jobqueue_id": null
                }

        :statuscode 201: a new job queue was created
        :statuscode 400: there was something wrong with the request (such as
                            invalid columns being included)
        :statuscode 409: a job queue with that name already exists
        """
        jobqueue = JobQueue.query.filter_by(name=g.json["name"]).first()
        if jobqueue:
            return (jsonify(error="Job queue %s already exixts" %
                            g.json["name"]), CONFLICT)

        jobqueue = JobQueue(**g.json)
        db.session.add(jobqueue)
        db.session.commit()

        jobqueue_data = jobqueue.to_dict()
        logger.info("created job queue %s: %r", jobqueue.name, jobqueue_data)

        return jsonify(jobqueue_data), CREATED

    def get(self):
        """
        A ``GET`` to this endpoint will return a list of known job queues.

        .. http:get:: /api/v1/jobqueues/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobqueues/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "priority": 5,
                        "weight": 10,
                        "parent_jobqueue_id": null,
                        "name": "Test Queue",
                        "minimum_agents": null,
                        "id": 1,
                        "maximum_agents": null
                    },
                    {
                        "priority": 5,
                        "weight": 10,
                        "parent_jobqueue_id": null,
                        "name": "Test Queue 2",
                        "minimum_agents": null,
                        "id": 2,
                        "maximum_agents": null
                    }
                ]

        :statuscode 200: no error
        """
        out = []
        for jobqueue in JobQueue.query:
            out.append(jobqueue.to_dict(unpack_relationships=False))

        return jsonify(out), OK

class SingleJobQueueAPI(MethodView):
    def get(self, queue_rq):
        """
        A ``GET`` to this endpoint will return the requested job queue

        .. http:get:: /api/v1/jobqueues/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/software/Test%20Queue HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 1,
                    "parent": [],
                    "jobs": [],
                    "weight": 10,
                    "parent_jobqueue_id": null,
                    "priority": 5,
                    "minimum_agents": null,
                    "name": "Test Queue",
                    "maximum_agents": null
                }

        :statuscode 200: no error
        :statuscode 404: the requested job queue was not found
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(error="Requested job queue not found"), NOT_FOUND

        return jsonify(jobqueue.to_dict()), OK

    def post(self, queue_rq):
        """
        A ``POST`` to this endpoint will update the specified queue with the data
        in the request.  Columns not specified in the request will be left as
        they are.

        .. http:post:: /api/v1/jobqueues/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                PUT /api/v1/jobs/Test%20Queue HTTP/1.1
                Accept: application/json

                {
                    "priority": 6
                }

            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 OK
                Content-Type: application/json

                {
                    "id": 1,
                    "parent": [],
                    "jobs": [],
                    "weight": 10,
                    "parent_jobqueue_id": null,
                    "priority": 6,
                    "minimum_agents": null,
                    "name": "Test Queue",
                    "maximum_agents": null
                }

        :statuscode 200: the job queue was updated
        :statuscode 400: there was something wrong with the request (such as
                            invalid columns being included)
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(error="Requested job queue not found"), NOT_FOUND

        # This would allow users to create circles in the job queue tree
        if "parent_jobqueue_id" in g.json:
            return (jsonify(error="The parent queue cannot be changed"),
                    BAD_REQUEST)

        for name in JobQueue.types().columns:
            if name in g.json:
                expected_type = JobQueue.types().mappings[name]
                value = g.json.pop(name)
                if not isinstance(value, expected_type):
                    return (jsonify(error="Column `%s` is of type %r, but we "
                                    "expected %r" % (name,
                                                     type(value),
                                                     expected_type)),
                                    BAD_REQUEST)
                setattr(jobqueue, name, value)

        db.session.add(jobqueue)
        db.session.commit()

        jobqueue_data = jobqueue.to_dict()
        logger.info("updated job queue %s: %r", jobqueue.name, jobqueue_data)

        return jsonify(jobqueue_data), OK

    def delete(self, queue_rq):
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(), OK

        num_sub_queues = JobQueue.query.filter_by(parent=jobqueue).count()
        if num_sub_queues > 0:
            return (jsonify(error="Cannot delete: job queue has child queues"),
                    CONFLICT)

        num_jobs = Job.query.filter_by(queue=jobqueue).count()
        if num_jobs > 0:
            return (jsonify(error="Cannot delete: job queue has jobs assigned"),
                    CONFLICT)

        db.session.delete(jobqueue)
        db.session.commit()
        logger.info("deleted job queue %s", jobqueue.name)

        return jsonify(), OK
<commit_msg>Get mappings for JobQueue model only once<commit_after># No shebang line, this module is meant to be imported
#
# Copyright 2014 Ambient Entertainment GmbH & Co. KG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Job Queues
----------

This module defines an API for managing and querying job queues
"""
try:
    from httplib import OK, CREATED, CONFLICT, NOT_FOUND
except ImportError:  # pragma: no cover
    from http.client import OK, CREATED, CONFLICT, NOT_FOUND

from flask import g
from flask.views import MethodView

from pyfarm.core.logger import getLogger
from pyfarm.core.enums import STRING_TYPES

from pyfarm.models.job import Job
from pyfarm.models.jobqueue import JobQueue
from pyfarm.master.application import db
from pyfarm.master.utility import jsonify, validate_with_model

logger = getLogger("api.jobqueues")

# Load model mappings once per process
JOBQUEUE_MODEL_MAPPINGS = JobQueue.types().mappings

def schema():
    """
    Returns the basic schema of :class:`.JobQueue`

    .. http:get:: /api/v1/jobqueues/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/jobqueues/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "id": "INTEGER",
                "name": VARCHAR(255)",
                "minimum_agents": "INTEGER",
                "maximum_agents": "INTEGER",
                "priority": "INTEGER",
                "weight": "INTEGER",
                "parent_jobqueue_id": "INTEGER"
            }

    :statuscode 200: no error
    """
    return jsonify(JobQueue.to_schema()), OK

class JobQueueIndexAPI(MethodView):
    @validate_with_model(JobQueue)
    def post(self):
        """
        A ``POST`` to this endpoint will create a new job queue.

        .. http:post:: /api/v1/jobqueues/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                POST /api/v1/jobqueues/ HTTP/1.1
                Accept: application/json

                {
                    "name": "Test Queue"
                }

            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 CREATED
                Content-Type: application/json

                {
                    "weight": 10,
                    "jobs": [],
                    "minimum_agents": null,
                    "priority": 5,
                    "name": "Test Queue",
                    "maximum_agents": null,
                    "id": 1,
                    "parent": [],
                    "parent_jobqueue_id": null
                }

        :statuscode 201: a new job queue was created
        :statuscode 400: there was something wrong with the request (such as
                            invalid columns being included)
        :statuscode 409: a job queue with that name already exists
        """
        jobqueue = JobQueue.query.filter_by(name=g.json["name"]).first()
        if jobqueue:
            return (jsonify(error="Job queue %s already exixts" %
                            g.json["name"]), CONFLICT)

        jobqueue = JobQueue(**g.json)
        db.session.add(jobqueue)
        db.session.commit()

        jobqueue_data = jobqueue.to_dict()
        logger.info("created job queue %s: %r", jobqueue.name, jobqueue_data)

        return jsonify(jobqueue_data), CREATED

    def get(self):
        """
        A ``GET`` to this endpoint will return a list of known job queues.

        .. http:get:: /api/v1/jobqueues/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobqueues/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "priority": 5,
                        "weight": 10,
                        "parent_jobqueue_id": null,
                        "name": "Test Queue",
                        "minimum_agents": null,
                        "id": 1,
                        "maximum_agents": null
                    },
                    {
                        "priority": 5,
                        "weight": 10,
                        "parent_jobqueue_id": null,
                        "name": "Test Queue 2",
                        "minimum_agents": null,
                        "id": 2,
                        "maximum_agents": null
                    }
                ]

        :statuscode 200: no error
        """
        out = []
        for jobqueue in JobQueue.query:
            out.append(jobqueue.to_dict(unpack_relationships=False))

        return jsonify(out), OK

class SingleJobQueueAPI(MethodView):
    def get(self, queue_rq):
        """
        A ``GET`` to this endpoint will return the requested job queue

        .. http:get:: /api/v1/jobqueues/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/software/Test%20Queue HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 1,
                    "parent": [],
                    "jobs": [],
                    "weight": 10,
                    "parent_jobqueue_id": null,
                    "priority": 5,
                    "minimum_agents": null,
                    "name": "Test Queue",
                    "maximum_agents": null
                }

        :statuscode 200: no error
        :statuscode 404: the requested job queue was not found
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(error="Requested job queue not found"), NOT_FOUND

        return jsonify(jobqueue.to_dict()), OK

    def post(self, queue_rq):
        """
        A ``POST`` to this endpoint will update the specified queue with the data
        in the request.  Columns not specified in the request will be left as
        they are.

        .. http:post:: /api/v1/jobqueues/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                PUT /api/v1/jobs/Test%20Queue HTTP/1.1
                Accept: application/json

                {
                    "priority": 6
                }

            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 OK
                Content-Type: application/json

                {
                    "id": 1,
                    "parent": [],
                    "jobs": [],
                    "weight": 10,
                    "parent_jobqueue_id": null,
                    "priority": 6,
                    "minimum_agents": null,
                    "name": "Test Queue",
                    "maximum_agents": null
                }

        :statuscode 200: the job queue was updated
        :statuscode 400: there was something wrong with the request (such as
                            invalid columns being included)
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(error="Requested job queue not found"), NOT_FOUND

        # This would allow users to create circles in the job queue tree
        if "parent_jobqueue_id" in g.json:
            return (jsonify(error="The parent queue cannot be changed"),
                    BAD_REQUEST)

        for name in JobQueue.types().columns:
            if name in g.json:
                expected_type = JOBQUEUE_MODEL_MAPPINGS[name]
                value = g.json.pop(name)
                if not isinstance(value, expected_type):
                    return (jsonify(error="Column `%s` is of type %r, but we "
                                    "expected %r" % (name,
                                                     type(value),
                                                     expected_type)),
                                    BAD_REQUEST)
                setattr(jobqueue, name, value)

        db.session.add(jobqueue)
        db.session.commit()

        jobqueue_data = jobqueue.to_dict()
        logger.info("updated job queue %s: %r", jobqueue.name, jobqueue_data)

        return jsonify(jobqueue_data), OK

    def delete(self, queue_rq):
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(), OK

        num_sub_queues = JobQueue.query.filter_by(parent=jobqueue).count()
        if num_sub_queues > 0:
            return (jsonify(error="Cannot delete: job queue has child queues"),
                    CONFLICT)

        num_jobs = Job.query.filter_by(queue=jobqueue).count()
        if num_jobs > 0:
            return (jsonify(error="Cannot delete: job queue has jobs assigned"),
                    CONFLICT)

        db.session.delete(jobqueue)
        db.session.commit()
        logger.info("deleted job queue %s", jobqueue.name)

        return jsonify(), OK
<|endoftext|>


<commit_before>import pytest

from rdflib import BNode, Literal
from unittest import mock
from ..commands import owltools as ot

pytestmark = [pytest.mark.setone, pytest.mark.working]

@pytest.yield_fixture
def owler():
    with mock.patch.object(ot, 'Owler') as mocked:
        yield mocked

# def emptygen(*args, **kwargs):
#    return
#    yield  # necessary to produce generator

# def rdfobject_generator(rdfobj_list):
#    for rdfobj in rdfobj_list:
#        yield rdfobj

@pytest.fixture
def rdf_objects():
    rdfobjs = ['testrdfobj1', 'testrdfobj2']
    return [Literal(rdfobj) for rdfobj in rdfobjs]

@pytest.fixture
def rdf_objects_2_1():
    rdfobjs = ['testrdfobj1']
    return [Literal(rdfobj) for rdfobj in rdfobjs]

@pytest.fixture
def rdf_objects_2_3():
    rdfobjs = ['testrdfobj1', 'testrdfobj2', 'testrdfobj3']
    return [Literal(rdfobj) for rdfobj in rdfobjs]

def test_get_rdfobjects_one_type_two_rdfobjs(owler, rdf_objects):
    checks = ['testrdfobj1', 'testrdfobj2']
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = rdf_objects
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        class_ = 'test_class'
        rdfobject_terms = ['1']
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert len(rdfobjects) == 2
        for rdfobj in rdfobjects:
            assert rdfobj in checks

def test_get_rdfobjects_two_types_one_rdfobj(owler, rdf_objects_2_1):
    check = 'testrdfobj1'
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = rdf_objects_2_1
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        class_ = 'test_class'
        rdfobject_terms = ['1', '2']
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert rdfobjects[0] == check

def test_get_rdfobjects_two_types_three_rdfobj(rdf_objects_2_3):
    checks = ['testrdfobj1', 'testrdfobj2', 'testrdfobj3']
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = rdf_objects_2_3
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        class_ = 'test_class'
        rdfobject_terms = ['1', '2']
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert len(rdfobjects) == 3
        for rdfobj in rdfobjects:
            assert rdfobj in checks

def test_get_rdfobjects_none_there(owler):
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = []
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        owler = ot.Owler('http://test.com')
        class_ = 'test_class'
        rdfobject_terms = []
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert not rdfobjects

def test_convert2URIRef_nostring():
    result = ot.convert2URIRef(None)
    assert result is None

def test_convert2URIRef_string():
    result = ot.convert2URIRef('test_string')
    assert ot.isURIRef(result)
    assert result.toPython() == 'test_string'

@pytest.fixture
def uri_list():
    strings = [
        'testwithoutslashes',
        'test/namespace#name',
        'test/namespace/name'
    ]
    uris = [ot.convert2URIRef(s) for s in strings]
    return strings + uris

def test_splitNameFromNamespace(uri_list):
    for i, uri in enumerate(uri_list):
        name, ns = ot.splitNameFromNamespace(uri)
        if i % 3 == 0:
            assert not name
            assert ns == uri_list[0]
        else:
            assert name == 'name'
            assert ns == 'test/namespace'

@pytest.fixture
def unsorted_uris():
    strings = [
        'test/Z',
        'test/1',
        'test#B',
        'test#a'
    ]
    uris = [ot.convert2URIRef(s) for s in strings]
    return strings + uris

def test_sortUriListByName(unsorted_uris):
    wanted = ['test/1', 'test/1', 'test#B', 'test#B', 'test/Z', 'test/Z', 'test#a', 'test#a']
    sorted_uris = ot.sortUriListByName(unsorted_uris)
    sorted_uris = [s.__str__() for s in sorted_uris]
    assert sorted_uris == wanted

@pytest.fixture
def blank_node():
    return BNode()

def test_isBlankNode_bnode(blank_node):
    assert ot.isBlankNode(blank_node)

def test_isBlankNode_non_bnode(unsorted_uris):
    assert not ot.isBlankNode(unsorted_uris[4])

@pytest.fixture
def dupe_lists():
    return [
        [[1, 2], [1, 3], [1, 4]],
        [['a', 'b', 'c'], ['a', 'a', 'b', 'b', 'b', 'c']],
        [[1, 2, 3], [1, 2, 3, 3, 3, 2, 2, 1]],
        [[None, None]],
        [None]
    ]

def test_removeDuplicates(dupe_lists):
    for i, l in enumerate(dupe_lists):
        if i == 0:
            def id_func(l):
                return l[0]
            result = ot.removeDuplicates(l, id_func)
            assert len(result) == 1
            assert result == [[1, 2]]
        else:
            for sl in l:
                result = ot.removeDuplicates(sl)
                if i == 1:
                    assert result == ['a', 'b', 'c']
                elif i == 2:
                    assert result == [1, 2, 3]
                elif i == 3:
                    assert result == [None]
                else:
                    assert result == []
<commit_msg>Do things the way CGAP does them in test_owltools.py.<commit_after>import pytest

from rdflib import BNode, Literal
from unittest import mock
from ..commands import owltools as ot

pytestmark = [pytest.mark.setone, pytest.mark.working]

@pytest.fixture
def owler():
    return mock.patch.object(ot, 'Owler')

@pytest.fixture
def rdf_objects():
    rdfobjs = ['testrdfobj1', 'testrdfobj2']
    return [Literal(rdfobj) for rdfobj in rdfobjs]

@pytest.fixture
def rdf_objects_2_1():
    rdfobjs = ['testrdfobj1']
    return [Literal(rdfobj) for rdfobj in rdfobjs]

@pytest.fixture
def rdf_objects_2_3():
    rdfobjs = ['testrdfobj1', 'testrdfobj2', 'testrdfobj3']
    return [Literal(rdfobj) for rdfobj in rdfobjs]

def test_get_rdfobjects_one_type_two_rdfobjs(owler, rdf_objects):
    checks = ['testrdfobj1', 'testrdfobj2']
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = rdf_objects
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        class_ = 'test_class'
        rdfobject_terms = ['1']
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert len(rdfobjects) == 2
        for rdfobj in rdfobjects:
            assert rdfobj in checks

def test_get_rdfobjects_two_types_one_rdfobj(owler, rdf_objects_2_1):
    check = 'testrdfobj1'
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = rdf_objects_2_1
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        class_ = 'test_class'
        rdfobject_terms = ['1', '2']
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert rdfobjects[0] == check

def test_get_rdfobjects_two_types_three_rdfobj(rdf_objects_2_3):
    checks = ['testrdfobj1', 'testrdfobj2', 'testrdfobj3']
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = rdf_objects_2_3
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        class_ = 'test_class'
        rdfobject_terms = ['1', '2']
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert len(rdfobjects) == 3
        for rdfobj in rdfobjects:
            assert rdfobj in checks

def test_get_rdfobjects_none_there(owler):
    with mock.patch('encoded.commands.owltools.ConjunctiveGraph') as graph:
        graph.objects.return_value = []
        owler = ot.Owler('http://test.com')
        owler.rdfGraph = graph
        owler = ot.Owler('http://test.com')
        class_ = 'test_class'
        rdfobject_terms = []
        rdfobjects = ot.getObjectLiteralsOfType(class_, owler, rdfobject_terms)
        assert not rdfobjects

def test_convert2URIRef_nostring():
    result = ot.convert2URIRef(None)
    assert result is None

def test_convert2URIRef_string():
    result = ot.convert2URIRef('test_string')
    assert ot.isURIRef(result)
    assert result.toPython() == 'test_string'

@pytest.fixture
def uri_list():
    strings = [
        'testwithoutslashes',
        'test/namespace#name',
        'test/namespace/name'
    ]
    uris = [ot.convert2URIRef(s) for s in strings]
    return strings + uris

def test_splitNameFromNamespace(uri_list):
    for i, uri in enumerate(uri_list):
        name, ns = ot.splitNameFromNamespace(uri)
        if i % 3 == 0:
            assert not name
            assert ns == uri_list[0]
        else:
            assert name == 'name'
            assert ns == 'test/namespace'

@pytest.fixture
def unsorted_uris():
    strings = [
        'test/Z',
        'test/1',
        'test#B',
        'test#a'
    ]
    uris = [ot.convert2URIRef(s) for s in strings]
    return strings + uris

def test_sortUriListByName(unsorted_uris):
    wanted = ['test/1', 'test/1', 'test#B', 'test#B', 'test/Z', 'test/Z', 'test#a', 'test#a']
    sorted_uris = ot.sortUriListByName(unsorted_uris)
    sorted_uris = [s.__str__() for s in sorted_uris]
    assert sorted_uris == wanted

@pytest.fixture
def blank_node():
    return BNode()

def test_isBlankNode_bnode(blank_node):
    assert ot.isBlankNode(blank_node)

def test_isBlankNode_non_bnode(unsorted_uris):
    assert not ot.isBlankNode(unsorted_uris[4])

@pytest.fixture
def dupe_lists():
    return [
        [[1, 2], [1, 3], [1, 4]],
        [['a', 'b', 'c'], ['a', 'a', 'b', 'b', 'b', 'c']],
        [[1, 2, 3], [1, 2, 3, 3, 3, 2, 2, 1]],
        [[None, None]],
        [None]
    ]

def test_removeDuplicates(dupe_lists):
    for i, l in enumerate(dupe_lists):
        if i == 0:
            def id_func(l):
                return l[0]
            result = ot.removeDuplicates(l, id_func)
            assert len(result) == 1
            assert result == [[1, 2]]
        else:
            for sl in l:
                result = ot.removeDuplicates(sl)
                if i == 1:
                    assert result == ['a', 'b', 'c']
                elif i == 2:
                    assert result == [1, 2, 3]
                elif i == 3:
                    assert result == [None]
                else:
                    assert result == []
<|endoftext|>


<commit_before><commit_msg>swap bytes in place<commit_after><|endoftext|>


<commit_before>#!/usr/bin/python3

import random
import numpy as np
from enum import Enum

class WrongException(Exception):
	pass

class Visibility(Enum):
	hidden = 0
	flag = 1
	visible = 2

	open = visible
	flagged = flag
	unopened = hidden

class Cell(object):
	MINE_STR = 'X'
	ZERO_STR = ' '

	def __init__(self, is_mine, neighbors=None, visible=None):
		self.is_mine = is_mine

		if neighbors is None:
			self._neighbors = None
		else:
			self.neighbors = neighbors  # will go through setter

		if visible is None:
			self._visible = None
		else:
			self._visible = visible

	def __bool__(self):
		return self.is_mine

	__nonzero__ = __bool__

	def __str__(self):
		if self.is_mine:
			return Cell.MINE_STR
		if self.neighbors is None:
			error = NotImplementedError('Incorrectly initialized Cell')
			# error.x, error.y = self. #oops cells don't know their x, y
			raise error
		if self.neighbors is 0:
			return Cell.ZERO_STR
		return str(self.neighbors)

	__repr__ = __str__

	@property
	def neighbors(self):
		return self._neighbors

	@neighbors.setter
	def neighbors(self, value):
		value = int(value)
		if 0 <= value <= 8:
			self._neighbors = value
		else:
			raise ValueError('Neighbors must be between 0 and 8')

	@property
	def visible(self):
		return self._visible

	@visible.setter
	def visible(self, value):
		self._visible = Visibility(value)  # automatically does checking for Visibility.x or 1, 2, 3

	@property
	def is_mine(self):
		return self._is_mine

	@is_mine.setter
	def is_mine(self, value):
		if value is None:
			raise ValueError('Must not be None. Must be truthy or falsy value.')
		self._is_mine = bool(value)

class Grid(object): # Todo: join mines and grid creation in one loop
	USE_CELL = True # Implement __deepcopy__ for Grid and Cell

	def __init__(self, mines):
		try:
			self._mines, self._grid, self.x_size, self.y_size = self._parse_args(mines)
		except (AssertionError, WrongException):
			raise TypeError("Sorry, mines didn't parse correctly.")

		self.reindex()

		self._visible = False

	@staticmethod
	def _parse_args(mines):
		try:
			mines = list(mines)
			mines = [[bool(y) for y in x] for x in mines]
		except TypeError:
			raise WrongException('TypeError on parsing mines.')

		# ok so now is 2d tuple of True, False
		# check to see if same dimensions

		assert isinstance(mines, list), "Not a tuple"

		# print(mines)

		x_size = len(mines)

		for x in mines:
			assert isinstance(x, list), "Not a list"
			for y in x:
				assert isinstance(y, bool), "Not a bool %s" % str(y)

		try:
			y_size = len(mines[0])
		except IndexError:
			raise WrongException('IndexError on getting "len" so did not tuple correctly')

		for x in mines:
			assert len(x) == y_size, "Not same length"
		# Done sanity checking # might not be necessary

		grid = []
		for x in range(x_size):
			row = []
			for y in range(y_size):
				temp = mines[x][y]
				temp = bool(temp)
				temp = Cell(is_mine=temp, visible=False)
				row.append(temp)
			# grid[x][y] = temp
			grid.append(row)

		return mines, grid, x_size, y_size

	@property
	def mines(self):
		return self._mines

	def grid(self, x=None, y=None):
		if None in (x, y):
			return self._grid
		return self._grid[x][y]

	def _str_1margin(self):
		s = ''
		for x in range(self.x_size):
			if x is 0:
				s += ' ' + ('----' * self.y_size)[:-1]
			else:
				s += '|' + ('----' * self.y_size)[:-1] + '|'
			s += '\n'

			for y in range(self.y_size):
				s += '| ' + str(self.grid(x, y)) + ' '
			s += '|\n'
		s += ' ' + ('----' * self.y_size)[:-1]
		return s

	def _str_0margin(self):
		s = ''
		for x in range(self.x_size):
			s += ' -' * self.y_size
			s += '\n'
			for y in range(self.y_size):
				s += '|'
				now = str(self.grid(x, y))
				s += now
				s += ''
			s += '|\n'
		s += ' -' * self.y_size
		return s

	def _str_numpy(self):
		return str(np.array(self.grid()))

	__str__ = _str_1margin  # This is changed

	__repr__ = __str__

	def reindex(self):
		for x in range(self.x_size):
			for y in range(self.x_size):
				if not self.grid(x, y).is_mine:
					self.grid(x, y).neighbors = self.get_neighbors(x, y)

	def get_neighbors(self, x, y) -> list:
		neighbors = 0

		for x_ in [-1, 0, 1]:
			# print('_x=%s'%str(x_))
			for y_ in [-1, 0, 1]:

				if (x_ is 0) and (y is 0):  # faster than if not (x_ or y_)
					continue  # avoid 0, 0 aka center square
				try:
					X = x_ + x
					Y = y_ + y

					if (X is -1) or (Y is -1):
						raise IndexError  # -1 index is invalid, so just skip this (x, y) pair

					neighbor = self.grid(X, Y).is_mine
					neighbors += bool(neighbor)
				except IndexError:
					# We are on edge or corner, so no cell here
					pass

		return neighbors

def get_random() -> random.Random:  # assume random module is imported as random
	return random.random.__self__  # __self__ gets instance a bound method belongs to. Basically, get default random.Random()

def randomize(x_size, y_size, mines, rand=None):
	if rand is None:
		rand = get_random()

	l = []
	grid = []
	for x in range(x_size):
		m = []
		for y in range(y_size):
			m.append(False)
			l.append((x, y))
		grid.append(m)

	l = rand.sample(l, mines)  # better than random.shuffle(l); l = l[:mines]

	# mines = []
	for x in range(mines):
		grid[l[x][0]][l[x][1]] = True

	return grid

class RandomGridGenerator(object):
	def __init__(self, x, y, mines, state=None):
		if state:
			self.rand = random.Random(state)
		else:
			self.rand = get_random()

		self.x, self.y, self.mines = x, y, mines

	def __iter__(self):
		return self

	def __next__(self):
		return self.next()

	def next(self):
		return randomize(self.x, self.y, self.mines, self.rand)

class Game(object):  # NOT IMPLEMENTED
	def __init__(self, grid, player):
		raise NotImplementedError()
		assert isinstance(grid, Grid)
		assert isinstance(player, Player)
		self.grid = grid # Should make copy.deepcopy of grid
		self.player = player
		self.x_size = self.grid.x_size
		self.y_size = self.grid.y_size

		self.mask = Grid(self.grid.mines)  # Create copy

class Move(object):  # All grid manipulation logic here
	def __init__(self, move_type, x, y):
		self.type = MoveType(move_type)
		x, y = int(x), int(y)
		if (x < 0) or (y < 0):
			raise ValueError('Out of range ints')
		self.x, self.y = x, y

	def apply(self, grid):
		if not isinstance(grid, Grid):
			raise TypeError('grid must be type Grid')
		if (self.x >= grid.x_size) or (self.y >= grid.y_size):
			raise ValueError('Coordinates out of bounds for Grid')

		cell = grid.grid(self.x, self.y)

		if self.type == MoveType.open:
			cell.visible = Visibility.visible
			if cell.is_mine:
				raise Explosion(self.x, self.y)
		elif self.type == MoveType.flag:
			if cell.visible == Visibility.open:
				return  # invalid command. Cannot flag if opened
			elif cell.visible == Visibility.flagged:  # unflag if flagged
				cell.visible = Visibility.hidden
			elif cell.visible == Visibility.hidden:
				cell.visible = Visibility.flag
			else:
				raise NotImplementedError()
		else:
			raise NotImplementedError()
		return

class Explosion(Exception):
	def __init__(self, x, y, *a, **k):
		self.x, self.y = x, y  # Store (x, y) for Game to use
		super().__init__(*a, **k)

class MoveType(Enum):
	open = 1
	flag = 2

class Player(object):
	def __init__(self):  # Override me
		pass

	def _move(self, grid):
		raise NotImplementedError()  # Override me

	def move(self, grid):  # Do Not Override
		if not isinstance(grid, Grid):
			raise TypeError()
		choice = self._move(grid)

		return MoveType(choice)

'''
def main():
	import sys
	while True:
		try:
			i = input(">>> ")
			i = "print(" + i + ")"
			exec(i)
		except:
			#print("exception")
			print(sys.exc_info())
'''

def main():
	'''g = Grid([[0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1, 0],
			  [0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0],
			  [0, 0, 1, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]])
	g = Grid(randomize(9,9,10))
	'''

	while True:
		i = input("? ")
		i = int(i)

		r = RandomGridGenerator(9, 9, 10, state=i)

		for x in range(3):
			g = Grid(r.next())			#print(np.array(g.grid()))
			#g = Grid([[0, 0, 1, 0], [1, 1, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0]])
			print(g)
			print('\n')
		#break

if __name__ == "__main__":
	main()
	input()
<commit_msg>Code Cleanup. Simplify Grid._parse_args. Implement copy for Cell and Grid.<commit_after>#!/usr/bin/python3

import random
import numpy as np
from enum import Enum

class Visibility(Enum):
	hidden = 0
	flag = 1
	visible = 2

	open = visible
	flagged = flag
	unopened = hidden

class Cell(object):
	MINE_STR = 'X'
	ZERO_STR = ' '

	def __init__(self, is_mine, neighbors=None, visible=None):
		self.is_mine = is_mine

		if neighbors is None:
			self._neighbors = None
		else:
			self.neighbors = neighbors  # will go through setter

		if visible is None:
			self._visible = None
		else:
			self._visible = visible

	def copy(self, *a, **k):
		return self.__class__(self.is_mine, self.neighbors, self.visible)

	__copy__ = copy
	__deepcopy__ = copy

	def __bool__(self):
		return self.is_mine

	__nonzero__ = __bool__

	@property
	def neighbors(self):
		return self._neighbors

	@neighbors.setter
	def neighbors(self, value):
		value = int(value)
		if 0 <= value <= 8:
			self._neighbors = value
		else:
			raise ValueError('Neighbors must be between 0 and 8')

	@property
	def visible(self):
		return self._visible

	@visible.setter
	def visible(self, value):
		self._visible = Visibility(value)  # automatically does checking for Visibility.x or 1, 2, 3

	@property
	def is_mine(self):
		return self._is_mine

	@is_mine.setter
	def is_mine(self, value):
		if value is None:
			raise ValueError('Must not be None. Must be truthy or falsy value.')
		self._is_mine = bool(value)

	def __str__(self):
		if self.is_mine:
			return Cell.MINE_STR
		if self.neighbors is None:
			error = NotImplementedError('Incorrectly initialized Cell')
			# error.x, error.y = self. #oops cells don't know their x, y
			raise error
		if self.neighbors is 0:
			return Cell.ZERO_STR
		return str(self.neighbors)

	def __repr__(self):
			return str(self) + ' ' + str(self.neighbors) + ' ' + str(self.visible)

class Grid(object):  # Todo: join mines and grid creation in one loop
	USE_CELL = True  # Implement __deepcopy__ for Grid and Cell

	def __init__(self, mines):
		self._grid, self.x_size, self.y_size = self._parse_args(mines)

		self.reindex()

	@staticmethod
	def _parse_args(mines):
		try:
			x_size = len(mines)  # Total num. of objects in mine
			y_size = len(mines[0])  # Num. of objects in first object in mine

			grid = []
			for x in range(x_size):
				assert len(mines[x]) == y_size  # make sure equal num of objects in every object of mine
				row = []
				for y in range(y_size):
					tmp = bool(mines[x][y])
					row.append(Cell(is_mine=tmp, visible=False))
				grid.append(row)

		except (TypeError, IndexError, AssertionError):
			# If any error, then raise error, so everything can be in one big try
			raise ValueError('Could not parse mines.')

		return grid, x_size, y_size

	@property
	def mines(self):
		return [[bool(y) for y in x] for x in self.grid()]

	def grid(self, x=None, y=None):
		if None in (x, y):
			return self._grid
		return self._grid[x][y]

	def _str_1margin(self):
		s = ''
		for x in range(self.x_size):
			if x is 0:
				s += ' ' + ('----' * self.y_size)[:-1]
			else:
				s += '|' + ('----' * self.y_size)[:-1] + '|'
			s += '\n'

			for y in range(self.y_size):
				s += '| ' + str(self.grid(x, y)) + ' '
			s += '|\n'
		s += ' ' + ('----' * self.y_size)[:-1]
		return s

	def _str_0margin(self):
		s = ''
		for x in range(self.x_size):
			s += ' -' * self.y_size
			s += '\n'
			for y in range(self.y_size):
				s += '|'
				now = str(self.grid(x, y))
				s += now
				s += ''
			s += '|\n'
		s += ' -' * self.y_size
		return s

	def _str_numpy(self):
		return str(np.array(self.grid()))

	__str__ = _str_1margin  # This is changed

	__repr__ = __str__

	def copy(self):
		new = self.__class__.__new__(self.__class__)

		new._grid = [[self.grid(x, y).copy() for y in range(self.y_size)] for x in range(self.x_size)]

	def reindex(self):
		for x in range(self.x_size):
			for y in range(self.x_size):
				if not self.grid(x, y).is_mine:
					self.grid(x, y).neighbors = self.get_neighbors(x, y)

	def get_neighbors(self, x, y) -> list:
		neighbors = 0

		for x_ in [-1, 0, 1]:
			# print('_x=%s'%str(x_))
			for y_ in [-1, 0, 1]:
				if (x_ is 0) and (y is 0):  # faster than if not (x_ or y_)
					continue  # avoid 0, 0 aka center square
				try:
					X = x_ + x
					Y = y_ + y

					if (X is -1) or (Y is -1):
						raise IndexError  # -1 index is invalid, so just skip this (x, y) pair

					neighbor = self.grid(X, Y).is_mine
					neighbors += bool(neighbor)
				except IndexError:
					# We are on edge or corner, so no cell here
					pass

		return neighbors

def get_random() -> random.Random:  # assume random module is imported as random
	return random.random.__self__  # Gets default random.Random()

def randomize(x_size, y_size, mines, rand=None):
	if rand is None:
		rand = get_random()

	l = []
	grid = []
	for x in range(x_size):
		m = []
		for y in range(y_size):
			m.append(False)
			l.append((x, y))
		grid.append(m)

	l = rand.sample(l, mines)  # better than random.shuffle(l); l = l[:mines]

	# mines = []
	for x in range(mines):
		grid[l[x][0]][l[x][1]] = True

	return grid

class RandomGridGenerator(object):
	def __init__(self, x, y, mines, state=None):
		if state:
			self.rand = random.Random(state)
		else:
			self.rand = get_random()

		self.x, self.y, self.mines = x, y, mines

	def __iter__(self):
		return self

	def __next__(self):
		return self.next()

	def next(self):
		return randomize(self.x, self.y, self.mines, self.rand)

class Game(object):  # NOT IMPLEMENTED
	def __init__(self, grid, player):
		raise NotImplementedError()
		assert isinstance(grid, Grid)
		assert isinstance(player, Player)
		self.grid = grid  # Should make copy.deepcopy of grid
		self.player = player
		self.x_size = self.grid.x_size
		self.y_size = self.grid.y_size

		self.mask = Grid(self.grid.mines)  # Create copy

class Move(object):  # All grid manipulation logic here
	def __init__(self, move_type, x, y):
		self.type = MoveType(move_type)
		x, y = int(x), int(y)
		if (x < 0) or (y < 0):
			raise ValueError('Out of range ints')
		self.x, self.y = x, y

	def apply(self, grid):
		if not isinstance(grid, Grid):
			raise TypeError('grid must be type Grid')
		if (self.x >= grid.x_size) or (self.y >= grid.y_size):
			raise ValueError('Coordinates out of bounds for Grid')

		cell = grid.grid(self.x, self.y)

		if self.type == MoveType.open:
			cell.visible = Visibility.visible
			if cell.is_mine:
				raise Explosion(self.x, self.y)
		elif self.type == MoveType.flag:
			if cell.visible == Visibility.open:
				return  # invalid command. Cannot flag if opened
			elif cell.visible == Visibility.flagged:  # unflag if flagged
				cell.visible = Visibility.hidden
			elif cell.visible == Visibility.hidden:
				cell.visible = Visibility.flag
			else:
				raise NotImplementedError()
		else:
			raise NotImplementedError()
		return

class Explosion(Exception):
	def __init__(self, x, y, *a, **k):
		self.x, self.y = x, y  # Store (x, y) for Game to use
		super().__init__(*a, **k)

class MoveType(Enum):
	open = 1
	flag = 2

class Player(object):
	def __init__(self):  # Override me
		pass

	def _move(self, grid):
		raise NotImplementedError()  # Override me

	def move(self, grid):  # Do Not Override
		if not isinstance(grid, Grid):
			raise TypeError()
		choice = self._move(grid)

		return MoveType(choice)

'''
def main():
	import sys
	while True:
		try:
			i = input(">>> ")
			i = "print(" + i + ")"
			exec(i)
		except:
			#print("exception")
			print(sys.exc_info())
'''

def main():
	'''g = Grid([[0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1, 0],
			  [0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0],
			  [0, 0, 1, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]])
	g = Grid(randomize(9,9,10))
	'''

	while True:
		i = input("? ")
		i = int(i)

		r = RandomGridGenerator(9, 9, 10, state=i)

		for x in range(3):
			g = Grid(r.next())  # print(np.array(g.grid()))
			# g = Grid([[0, 0, 1, 0], [1, 1, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0]])
			print(g)
			print('\n')
		# break

if __name__ == "__main__":
	main()
	input()
<|endoftext|>