<commit_before># PhenoTipsBot
# Framework for manipulating patient data on a PhenoTips server
#
# Copyright 2015 University of Utah
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
# USA

import io
import json
import requests
from base64 import b64encode
from selenium import webdriver
from xml.etree import ElementTree

class PhenoTipsBot:
    TIMEOUT = 20 #seconds

    driver = None

    def __init__(self, base_url, username, password, ssl_verify=False):
        self.base = base_url
        self.auth = (username, password)
        self.ssl_verify = ssl_verify

    def create(self, patient_obj=None, study=None, pedigree=None):
        r = requests.post(self.base + '/rest/patients', auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()
        patient_id = r.headers['location']
        patient_id = patient_id[patient_id.rfind('/')+1:]
        if patient_obj:
            self.set(patient_id, patient_obj)
        if study:
            self.set_study(patient_id, study)
        if pedigree:
            self.set_pedigree(patient_id, pedigree)
        #the mandatory PhenoTips.VCF object is not added until someone visits the edit page
        url = self.base + '/bin/edit/data/' + patient_id
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify);
        r.raise_for_status()
        return patient_id

    def delete(self, patient_id):
        r = requests.delete(self.base + '/rest/patients/' + patient_id, auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()

    def export_pedigree_ped(self, patient_id, id_generation='external'):
        self.init_phantom()
        url = self.base + '/bin/' + patient_id + '?sheet=PhenoTips.PedigreeEditor'
        self.driver.get(url)
        self.driver.find_element_by_css_selector('#canvas svg') #wait for the page to load
        return self.driver.execute_script('return window.PedigreeExport.exportAsPED(window.editor.getGraph().DG, ' + json.dumps(id_generation) + ');')

    def get(self, patient_id):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.PatientClass/0/properties'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()
        root = ElementTree.fromstring(r.text)
        ret = {}
        for prop in root.iter('{http://www.xwiki.org}property'):
            ret[prop.attrib['name']] = prop.find('{http://www.xwiki.org}value').text
        return ret

    def get_pedigree(self, patient_id):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.PedigreeClass/0'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()
        tree = ElementTree.parse(io.StringIO(r.text))
        el = tree.find('{http://www.xwiki.org}property[@name="data"]/{http://www.xwiki.org}value')
        return json.loads(el.text)

    def get_study(self, patient_id):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.StudyBindingClass/0'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        if r.status_code == 404:
            return None
        else:
            r.raise_for_status()
            tree = ElementTree.parse(io.StringIO(r.text))
            el = tree.find('{http://www.xwiki.org}property[@name="studyReference"]/{http://www.xwiki.org}value')
            return el.text[len('xwiki:Studies.'):]

    def import_pedigree_ped(self, patient_id, pedigree_str, mark_evaluated=False, external_id_mark=True, accept_unknown_phenotypes=True):
        self.init_phantom()
        url = self.base + '/bin/' + patient_id + '?sheet=PhenoTips.PedigreeEditor'
        data = json.dumps(pedigree_str)
        import_options = json.dumps({
            'markEvaluated': mark_evaluated,
            'externalIdMark': external_id_mark,
            'acceptUnknownPhenotypes': accept_unknown_phenotypes
        })
        self.driver.get(url)
        self.driver.find_element_by_css_selector('#canvas svg') #wait for the page to load
        self.driver.execute_script('window.editor.getSaveLoadEngine().createGraphFromImportData(' + data + ', "ped", ' + import_options + ');')
        self.driver.execute_script('window.editor.getSaveLoadEngine().save();')
        self.driver.find_element_by_css_selector('#action-save.menu-item') #wait for the image to be saved

    def init_phantom(self):
        if not self.driver:
            authorization = 'Basic ' + b64encode((self.auth[0] + ':' + self.auth[1]).encode('utf-8')).decode('utf-8')
            webdriver.DesiredCapabilities.PHANTOMJS['phantomjs.page.customHeaders.authorization'] = authorization
            self.driver = webdriver.PhantomJS()
            self.driver.set_window_size(1920, 1080) #big enough to not cut off any elements
            self.driver.implicitly_wait(PhenoTipsBot.TIMEOUT)

    def list(self):
        url = self.base + '/rest/patients?number=10000000'
        r = requests.get(url, auth=self.auth, headers={'Content-Type': 'application/xml'}, verify=self.ssl_verify)
        r.raise_for_status()
        root = ElementTree.fromstring(r.text)
        id_elements = root.findall('./{https://phenotips.org/}patientSummary/{https://phenotips.org/}id')
        return list(map(lambda el: el.text, id_elements))

    def set(self, patient_id, patient_obj):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.PatientClass/0'
        data = {}
        for key in patient_obj:
            data['property#' + key] = patient_obj[key]
        r = requests.put(url, auth=self.auth, data=data, verify=self.ssl_verify)
        r.raise_for_status()

    def set_pedigree(self, patient_id, pedigree_obj):
        #the SVG is not automatically updated if the JSON is changed via the REST API
        self.init_phantom()
        url = self.base + '/bin/' + patient_id + '?sheet=PhenoTips.PedigreeEditor'
        data = json.dumps(json.dumps(pedigree_obj, sort_keys=True))
        self.driver.get(url)
        self.driver.find_element_by_css_selector('#canvas svg') #wait for the page to load
        self.driver.execute_script('window.editor.getSaveLoadEngine().createGraphFromSerializedData(' + data + ');')
        self.driver.execute_script('window.editor.getSaveLoadEngine().save();')
        self.driver.find_element_by_css_selector('#action-save.menu-item') #wait for the image to be saved

    def set_study(self, patient_id, study):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.StudyBindingClass/0'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        if r.status_code == 404:
            if study == None:
                return
            else:
                url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects'
                data = {
                    'className': 'PhenoTips.StudyBindingClass',
                    'property#studyReference': 'xwiki:Studies.' + study,
                }
                r = requests.post(url, auth=self.auth, data=data, verify=self.ssl_verify)
                r.raise_for_status()
        else:
            r.raise_for_status()
            if study == None:
                requests.delete(url, auth=self.auth, verify=self.ssl_verify)
                r.raise_for_status()
            else:
                r = requests.put(url, auth=self.auth, data={studyReference: 'xwiki:Studies.' + study}, verify=self.ssl_verify)
                r.raise_for_status()

<commit_msg>Remove StringIO<commit_after># PhenoTipsBot
# Framework for manipulating patient data on a PhenoTips server
#
# Copyright 2015 University of Utah
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
# USA

import json
import requests
from base64 import b64encode
from selenium import webdriver
from xml.etree import ElementTree

class PhenoTipsBot:
    TIMEOUT = 20 #seconds

    driver = None

    def __init__(self, base_url, username, password, ssl_verify=False):
        self.base = base_url
        self.auth = (username, password)
        self.ssl_verify = ssl_verify

    def create(self, patient_obj=None, study=None, pedigree=None):
        r = requests.post(self.base + '/rest/patients', auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()
        patient_id = r.headers['location']
        patient_id = patient_id[patient_id.rfind('/')+1:]
        if patient_obj:
            self.set(patient_id, patient_obj)
        if study:
            self.set_study(patient_id, study)
        if pedigree:
            self.set_pedigree(patient_id, pedigree)
        #the mandatory PhenoTips.VCF object is not added until someone visits the edit page
        url = self.base + '/bin/edit/data/' + patient_id
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify);
        r.raise_for_status()
        return patient_id

    def delete(self, patient_id):
        r = requests.delete(self.base + '/rest/patients/' + patient_id, auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()

    def export_pedigree_ped(self, patient_id, id_generation='external'):
        self.init_phantom()
        url = self.base + '/bin/' + patient_id + '?sheet=PhenoTips.PedigreeEditor'
        self.driver.get(url)
        self.driver.find_element_by_css_selector('#canvas svg') #wait for the page to load
        return self.driver.execute_script('return window.PedigreeExport.exportAsPED(window.editor.getGraph().DG, ' + json.dumps(id_generation) + ');')

    def get(self, patient_id):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.PatientClass/0/properties'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()
        root = ElementTree.fromstring(r.text)
        ret = {}
        for prop in root.iter('{http://www.xwiki.org}property'):
            ret[prop.attrib['name']] = prop.find('{http://www.xwiki.org}value').text
        return ret

    def get_pedigree(self, patient_id):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.PedigreeClass/0'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        r.raise_for_status()
        root = ElementTree.fromstring(r.text)
        el = root.find('{http://www.xwiki.org}property[@name="data"]/{http://www.xwiki.org}value')
        return json.loads(el.text)

    def get_study(self, patient_id):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.StudyBindingClass/0'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        if r.status_code == 404:
            return None
        else:
            r.raise_for_status()
            root = ElementTree.fromstring(r.text)
            el = root.find('{http://www.xwiki.org}property[@name="studyReference"]/{http://www.xwiki.org}value')
            return el.text[len('xwiki:Studies.'):]

    def import_pedigree_ped(self, patient_id, pedigree_str, mark_evaluated=False, external_id_mark=True, accept_unknown_phenotypes=True):
        self.init_phantom()
        url = self.base + '/bin/' + patient_id + '?sheet=PhenoTips.PedigreeEditor'
        data = json.dumps(pedigree_str)
        import_options = json.dumps({
            'markEvaluated': mark_evaluated,
            'externalIdMark': external_id_mark,
            'acceptUnknownPhenotypes': accept_unknown_phenotypes
        })
        self.driver.get(url)
        self.driver.find_element_by_css_selector('#canvas svg') #wait for the page to load
        self.driver.execute_script('window.editor.getSaveLoadEngine().createGraphFromImportData(' + data + ', "ped", ' + import_options + ');')
        self.driver.execute_script('window.editor.getSaveLoadEngine().save();')
        self.driver.find_element_by_css_selector('#action-save.menu-item') #wait for the image to be saved

    def init_phantom(self):
        if not self.driver:
            authorization = 'Basic ' + b64encode((self.auth[0] + ':' + self.auth[1]).encode('utf-8')).decode('utf-8')
            webdriver.DesiredCapabilities.PHANTOMJS['phantomjs.page.customHeaders.authorization'] = authorization
            self.driver = webdriver.PhantomJS()
            self.driver.set_window_size(1920, 1080) #big enough to not cut off any elements
            self.driver.implicitly_wait(PhenoTipsBot.TIMEOUT)

    def list(self):
        url = self.base + '/rest/patients?number=10000000'
        r = requests.get(url, auth=self.auth, headers={'Content-Type': 'application/xml'}, verify=self.ssl_verify)
        r.raise_for_status()
        root = ElementTree.fromstring(r.text)
        id_elements = root.findall('./{https://phenotips.org/}patientSummary/{https://phenotips.org/}id')
        return list(map(lambda el: el.text, id_elements))

    def set(self, patient_id, patient_obj):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.PatientClass/0'
        data = {}
        for key in patient_obj:
            data['property#' + key] = patient_obj[key]
        r = requests.put(url, auth=self.auth, data=data, verify=self.ssl_verify)
        r.raise_for_status()

    def set_pedigree(self, patient_id, pedigree_obj):
        #the SVG is not automatically updated if the JSON is changed via the REST API
        self.init_phantom()
        url = self.base + '/bin/' + patient_id + '?sheet=PhenoTips.PedigreeEditor'
        data = json.dumps(json.dumps(pedigree_obj, sort_keys=True))
        self.driver.get(url)
        self.driver.find_element_by_css_selector('#canvas svg') #wait for the page to load
        self.driver.execute_script('window.editor.getSaveLoadEngine().createGraphFromSerializedData(' + data + ');')
        self.driver.execute_script('window.editor.getSaveLoadEngine().save();')
        self.driver.find_element_by_css_selector('#action-save.menu-item') #wait for the image to be saved

    def set_study(self, patient_id, study):
        url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects/PhenoTips.StudyBindingClass/0'
        r = requests.get(url, auth=self.auth, verify=self.ssl_verify)
        if r.status_code == 404:
            if study == None:
                return
            else:
                url = self.base + '/rest/wikis/xwiki/spaces/data/pages/' + patient_id + '/objects'
                data = {
                    'className': 'PhenoTips.StudyBindingClass',
                    'property#studyReference': 'xwiki:Studies.' + study,
                }
                r = requests.post(url, auth=self.auth, data=data, verify=self.ssl_verify)
                r.raise_for_status()
        else:
            r.raise_for_status()
            if study == None:
                requests.delete(url, auth=self.auth, verify=self.ssl_verify)
                r.raise_for_status()
            else:
                r = requests.put(url, auth=self.auth, data={studyReference: 'xwiki:Studies.' + study}, verify=self.ssl_verify)
                r.raise_for_status()

<|endoftext|>


<commit_before>import inspect
import os
from threading import Thread

from ._user_namespace import get_user_namespace, UserNamespace
from ._viewer import create_viewer, Viewer
from ._vis_base import get_gui, default_gui, Control, display_name, value_range, Action, VisModel, Gui
from ..field import SampledField, Scene
from ..field._scene import _slugify_filename

def show(model: VisModel or None = None, play=True, gui: Gui or str = None, keep_alive=True, **config):
    """
    Launch the registered user interface (web interface by default).

    This method may block until the GUI is closed.

    This method prepares the vis before showing it. No more fields should be added to the vis after this method is invoked.

    Also see the user interface documentation at https://tum-pbs.github.io/PhiFlow/Visualization.html

    Args:
      model: (Optional) `VisModel`, the application to display. If unspecified, searches the calling script for a subclass of App and instantiates it.
      play: If true, invokes `App.play()`. The default value is False unless "autorun" is passed as a command line argument.
      gui: (optional) class of GUI to use
      keep_alive: Whether the GUI keeps the vis alive. If `False`, the program will exit when the main script is finished.
      **config: additional GUI configuration parameters.
        For a full list of parameters, see the respective GUI documentation at https://tum-pbs.github.io/PhiFlow/Visualization.html
    """
    if model is None:
        import pylab
        pylab.show()
        return
    assert isinstance(model, VisModel), f"show() first argument must be an App instance but got {model}"
    model.prepare()
    # --- Setup Gui ---
    gui = default_gui() if gui is None else get_gui(gui)
    gui.configure(config)
    gui.setup(model)
    if play:  # this needs to be done even if model cannot progress right now
        gui.auto_play()
    if gui.asynchronous:
        display_thread = Thread(target=lambda: gui.show(True), name="AsyncGui", daemon=not keep_alive)
        display_thread.start()
    else:
        gui.show(True)  # may be blocking call

RECORDINGS = {}

def record(*fields: str or SampledField) -> Viewer:
    user_namespace = get_user_namespace(1)
    variables = _default_field_variables(user_namespace, fields)
    viewer = create_viewer(user_namespace, variables, "record", "", scene=None, asynchronous=False, controls=(), actions={}, log_performance=False)
    viewer.post_step.append(lambda viewer: print(viewer.steps, end=" "))
    viewer.progress_unavailable.append(lambda viewer: print())
    return viewer

def view(*fields: str or SampledField,
         play: bool = True,
         gui=None,
         name: str = None,
         description: str = None,
         scene: bool or Scene = False,
         keep_alive=True,
         select: str or tuple or list = '',
         framerate=None,
         **config) -> Viewer:
    """
    Show `fields` in a graphical user interface.

    `fields` may contain instances of `Field` or variable names of top-level variables (main module or Jupyter notebook).
    During loops, e.g. `view().range()`, the variable status is tracked and the GUI is updated.

    When called from a Python script, name and description may be specified in the module docstring (string before imports).
    The first line is interpreted as the name, the rest as the subtitle.
    If not specified, a generic name and description is chosen.

    Args:
        *fields: (Optional) Contents to be displayed. Either variable names or values.
            For field instances, all variables referencing the value will be shown.
            If not provided, the user namespace is searched for Field variables.
        play: Whether to immediately start executing loops.
        gui: (Optional) Name of GUI as `str` or GUI class.
            Built-in GUIs can be selected via `'dash'`, `'console'` and `'widgets'`.
            See https://tum-pbs.github.io/PhiFlow/Visualization.html
        name: (Optional) Name to display in GUI and use for the output directory if `scene=True`.
            Will be generated from the top-level script if not provided.
        description: (Optional) Description to be displayed in the GUI.
            Will be generated from the top-level script if not provided.
        scene: Existing `Scene` to write into or `bool`. If `True`, creates a new Scene in `~/phi/<name>`
        keep_alive: Whether the GUI should keep running even after the main thread finishes.
        framerate: Target frame rate in Hz. Play will not step faster than the framerate. `None` for unlimited frame rate.
        select: Dimension names along which one item to show is selected.
            Dimensions may be passed as `tuple` of `str` or as comma-separated names in a single `str`.
            For each `select` dimension, an associated selection slider will be created.
        **config: Additional GUI configuration arguments.

    Returns:
        `Viewer`
    """
    user_namespace = get_user_namespace(1)
    variables = _default_field_variables(user_namespace, fields)
    actions = _default_actions(user_namespace)
    if scene is False:
        scene = None
    elif scene is True:
        scene = Scene.create(os.path.join("~", "phi", _slugify_filename(name or user_namespace.get_reference())))
        print(f"Created scene at {scene}")
    else:
        assert isinstance(scene, Scene)
    name = name or user_namespace.get_title()
    description = description or user_namespace.get_description()
    gui = default_gui() if gui is None else get_gui(gui)
    controls = tuple(c for c in sorted(CONTROL_VARS.values(), key=lambda c: c.name) if user_namespace.get_variable(c.name) is not None)
    viewer = create_viewer(user_namespace, variables, name, description, scene, asynchronous=gui.asynchronous, controls=controls, actions=actions, log_performance=True)
    show(viewer, play=play, gui=gui, keep_alive=keep_alive, framerate=framerate, select=select, **config)
    return viewer

def _default_field_variables(user_namespace: UserNamespace, fields: tuple):
    names = []
    values = []
    if len(fields) == 0:  # view all Fields
        user_variables = user_namespace.list_variables(only_public=True, only_current_scope=True)
        for name, val in user_variables.items():
            if isinstance(val, SampledField):
                names.append(name)
                values.append(val)
    else:  # find variable names
        user_variables = user_namespace.list_variables()
        for field in fields:
            if isinstance(field, str):
                split = [n.strip() for n in field.split(',')]
                names.extend(split)
                values.extend([user_namespace.get_variable(n, default=None) for n in split])
            else:
                for name, val in user_variables.items():
                    if val is field:
                        names.append(name)
                        values.append(field)
    return {n: v for n, v in zip(names, values)}

def _default_actions(ns: UserNamespace):
    actions = {}
    for name, fun in ns.list_variables(only_public=True, only_current_scope=True).items():
        if inspect.isfunction(fun):
            signature = inspect.signature(fun)
            if not signature.parameters:
                doc = inspect.getdoc(fun)
                action = Action(name, doc)
                actions[action] = fun
    return actions

def control(value, range: tuple = None, description="", **kwargs):
    """
    Mark a variable as controllable by any GUI created via `view()`.

    Example:
    ```python
    dt = control(1.0, (0.1, 10), name="Time increment")
    ```

    The value o

    Args:
        value: Initial value. Must be either `int`, `float´, `bool` or `str`.
        range: (Optional) Specify range of possible values as `(min, max)`. Only for `int` and `float` values.
        description: Description of what the control does.
        **kwargs: Additional arguments to determine the appearance of the GUI component,
            e.g. `rows` for text fields or `log=False` for float sliders.

    Returns:
        `value`
    """
    assert type(value) in (int, float, bool, str), f"Value must be one of (int, float, bool, str) but {type(value)}"
    calling_code = inspect.stack()[1].code_context[0]
    assert 'control' in calling_code and '=' in calling_code, f"control() must be used in a variable assignment statement but context is: {calling_code}"
    calling_code = calling_code[:calling_code.index('control')]
    var_names = [var.strip() for var in calling_code.split('=')[:-1]]
    var_names = [n for n in var_names if n]
    for var_name in var_names:
        ctrl = Control(var_name, type(value), value, range, description, kwargs)
        value_range(ctrl)  # checks if valid
        CONTROL_VARS[var_name] = ctrl
    return value

CONTROL_VARS = {}
<commit_msg>[vis] Clear previous controls after view()<commit_after>import inspect
import os
from threading import Thread

from ._user_namespace import get_user_namespace, UserNamespace
from ._viewer import create_viewer, Viewer
from ._vis_base import get_gui, default_gui, Control, display_name, value_range, Action, VisModel, Gui
from ..field import SampledField, Scene
from ..field._scene import _slugify_filename

def show(model: VisModel or None = None, play=True, gui: Gui or str = None, keep_alive=True, **config):
    """
    Launch the registered user interface (web interface by default).

    This method may block until the GUI is closed.

    This method prepares the vis before showing it. No more fields should be added to the vis after this method is invoked.

    Also see the user interface documentation at https://tum-pbs.github.io/PhiFlow/Visualization.html

    Args:
      model: (Optional) `VisModel`, the application to display. If unspecified, searches the calling script for a subclass of App and instantiates it.
      play: If true, invokes `App.play()`. The default value is False unless "autorun" is passed as a command line argument.
      gui: (optional) class of GUI to use
      keep_alive: Whether the GUI keeps the vis alive. If `False`, the program will exit when the main script is finished.
      **config: additional GUI configuration parameters.
        For a full list of parameters, see the respective GUI documentation at https://tum-pbs.github.io/PhiFlow/Visualization.html
    """
    if model is None:
        import pylab
        pylab.show()
        return
    assert isinstance(model, VisModel), f"show() first argument must be an App instance but got {model}"
    model.prepare()
    # --- Setup Gui ---
    gui = default_gui() if gui is None else get_gui(gui)
    gui.configure(config)
    gui.setup(model)
    if play:  # this needs to be done even if model cannot progress right now
        gui.auto_play()
    if gui.asynchronous:
        display_thread = Thread(target=lambda: gui.show(True), name="AsyncGui", daemon=not keep_alive)
        display_thread.start()
    else:
        gui.show(True)  # may be blocking call

RECORDINGS = {}

def record(*fields: str or SampledField) -> Viewer:
    user_namespace = get_user_namespace(1)
    variables = _default_field_variables(user_namespace, fields)
    viewer = create_viewer(user_namespace, variables, "record", "", scene=None, asynchronous=False, controls=(), actions={}, log_performance=False)
    viewer.post_step.append(lambda viewer: print(viewer.steps, end=" "))
    viewer.progress_unavailable.append(lambda viewer: print())
    return viewer

def view(*fields: str or SampledField,
         play: bool = True,
         gui=None,
         name: str = None,
         description: str = None,
         scene: bool or Scene = False,
         keep_alive=True,
         select: str or tuple or list = '',
         framerate=None,
         **config) -> Viewer:
    """
    Show `fields` in a graphical user interface.

    `fields` may contain instances of `Field` or variable names of top-level variables (main module or Jupyter notebook).
    During loops, e.g. `view().range()`, the variable status is tracked and the GUI is updated.

    When called from a Python script, name and description may be specified in the module docstring (string before imports).
    The first line is interpreted as the name, the rest as the subtitle.
    If not specified, a generic name and description is chosen.

    Args:
        *fields: (Optional) Contents to be displayed. Either variable names or values.
            For field instances, all variables referencing the value will be shown.
            If not provided, the user namespace is searched for Field variables.
        play: Whether to immediately start executing loops.
        gui: (Optional) Name of GUI as `str` or GUI class.
            Built-in GUIs can be selected via `'dash'`, `'console'` and `'widgets'`.
            See https://tum-pbs.github.io/PhiFlow/Visualization.html
        name: (Optional) Name to display in GUI and use for the output directory if `scene=True`.
            Will be generated from the top-level script if not provided.
        description: (Optional) Description to be displayed in the GUI.
            Will be generated from the top-level script if not provided.
        scene: Existing `Scene` to write into or `bool`. If `True`, creates a new Scene in `~/phi/<name>`
        keep_alive: Whether the GUI should keep running even after the main thread finishes.
        framerate: Target frame rate in Hz. Play will not step faster than the framerate. `None` for unlimited frame rate.
        select: Dimension names along which one item to show is selected.
            Dimensions may be passed as `tuple` of `str` or as comma-separated names in a single `str`.
            For each `select` dimension, an associated selection slider will be created.
        **config: Additional GUI configuration arguments.

    Returns:
        `Viewer`
    """
    user_namespace = get_user_namespace(1)
    variables = _default_field_variables(user_namespace, fields)
    actions = _default_actions(user_namespace)
    if scene is False:
        scene = None
    elif scene is True:
        scene = Scene.create(os.path.join("~", "phi", _slugify_filename(name or user_namespace.get_reference())))
        print(f"Created scene at {scene}")
    else:
        assert isinstance(scene, Scene)
    name = name or user_namespace.get_title()
    description = description or user_namespace.get_description()
    gui = default_gui() if gui is None else get_gui(gui)
    controls = tuple(c for c in sorted(CONTROL_VARS.values(), key=lambda c: c.name) if user_namespace.get_variable(c.name) is not None)
    CONTROL_VARS.clear()
    viewer = create_viewer(user_namespace, variables, name, description, scene, asynchronous=gui.asynchronous, controls=controls, actions=actions, log_performance=True)
    show(viewer, play=play, gui=gui, keep_alive=keep_alive, framerate=framerate, select=select, **config)
    return viewer

def _default_field_variables(user_namespace: UserNamespace, fields: tuple):
    names = []
    values = []
    if len(fields) == 0:  # view all Fields
        user_variables = user_namespace.list_variables(only_public=True, only_current_scope=True)
        for name, val in user_variables.items():
            if isinstance(val, SampledField):
                names.append(name)
                values.append(val)
    else:  # find variable names
        user_variables = user_namespace.list_variables()
        for field in fields:
            if isinstance(field, str):
                split = [n.strip() for n in field.split(',')]
                names.extend(split)
                values.extend([user_namespace.get_variable(n, default=None) for n in split])
            else:
                for name, val in user_variables.items():
                    if val is field:
                        names.append(name)
                        values.append(field)
    return {n: v for n, v in zip(names, values)}

def _default_actions(ns: UserNamespace):
    actions = {}
    for name, fun in ns.list_variables(only_public=True, only_current_scope=True).items():
        if inspect.isfunction(fun):
            signature = inspect.signature(fun)
            if not signature.parameters:
                doc = inspect.getdoc(fun)
                action = Action(name, doc)
                actions[action] = fun
    return actions

def control(value, range: tuple = None, description="", **kwargs):
    """
    Mark a variable as controllable by any GUI created via `view()`.

    Example:
    ```python
    dt = control(1.0, (0.1, 10), name="Time increment")
    ```

    The value o

    Args:
        value: Initial value. Must be either `int`, `float´, `bool` or `str`.
        range: (Optional) Specify range of possible values as `(min, max)`. Only for `int` and `float` values.
        description: Description of what the control does.
        **kwargs: Additional arguments to determine the appearance of the GUI component,
            e.g. `rows` for text fields or `log=False` for float sliders.

    Returns:
        `value`
    """
    assert type(value) in (int, float, bool, str), f"Value must be one of (int, float, bool, str) but {type(value)}"
    calling_code = inspect.stack()[1].code_context[0]
    assert 'control' in calling_code and '=' in calling_code, f"control() must be used in a variable assignment statement but context is: {calling_code}"
    calling_code = calling_code[:calling_code.index('control')]
    var_names = [var.strip() for var in calling_code.split('=')[:-1]]
    var_names = [n for n in var_names if n]
    for var_name in var_names:
        ctrl = Control(var_name, type(value), value, range, description, kwargs)
        value_range(ctrl)  # checks if valid
        CONTROL_VARS[var_name] = ctrl
    return value

CONTROL_VARS = {}
<|endoftext|>


<commit_before>
#  IATI Data Quality, tools for Data QA on IATI-formatted  publications
#  by Mark Brough, Martin Keegan, Ben Webb and Jennifer Smith
#
#  Copyright (C) 2013  Publish What You Fund
#
#  This programme is free software; you may redistribute and/or modify
#  it under the terms of the GNU Affero General Public License v3.0

import os

from flask import render_template, flash, request, Markup, redirect, url_for, abort
from flask_login import current_user
import markdown

from . import app, usermanagement
from iatidq import dqindicators, dqorganisations, dqusers, donorresponse, util
import iatidq.survey.data as dqsurveys
import iatidq.survey.mapping
from iatidq.models import Organisation

@app.route("/surveys/admin/")
@usermanagement.perms_required()
def surveys_admin():
    surveys = dqsurveys.surveys()
    workflows = dqsurveys.workflowsAll()
    publishedstatuses=dqsurveys.publishedStatus()
    admin = usermanagement.check_perms('admin')
    loggedinuser = current_user

    return render_template("surveys/surveys_admin.html",
                           **locals())

@app.route("/surveys/create/", methods=["GET", "POST"])
@app.route("/surveys/<organisation_code>/create/", methods=["GET", "POST"])
def create_survey(organisation_code=None):
    return "You're trying to create a survey"

def completion_percentage(survey):
    stages = ['researcher', 'send', 'donorreview', 'pwyfreview',
              'cso', 'donorcomments', 'pwyffinal', 'finalised']

    # can ValueError; used to raise NameError
    idx = stages.index(survey.workflow.name)

    return 100. * idx / (len(stages) - 1)

@app.route("/organisations/<organisation_code>/survey/repair/")
@usermanagement.perms_required('survey', 'view')
def organisation_survey_repair(organisation_code):
    status = dqsurveys.repairSurveyData(organisation_code)
    if status['changes'] == True:
        indicators = ", ".join(status['changed_indicators'])
        flash('Survey successfully repaired indicators '+indicators, 'success')
    else:
        flash('Survey could not be repaired', 'error')
    return redirect(url_for('organisation_survey', organisation_code=organisation_code))

@app.route("/organisations/<organisation_code>/survey/")
@usermanagement.perms_required('survey', 'view')
def organisation_survey(organisation_code=None):
    organisation = dqorganisations.organisations(organisation_code)
    # make sure survey exists
    survey = dqsurveys.getOrCreateSurveyById(organisation.id)

    surveydata = dqsurveys.getSurveyDataAllWorkflows(organisation_code)
    workflows = dqsurveys.workflowsAll()
    pct_complete = completion_percentage(survey)
    users = dqusers.surveyPermissions(organisation_code)
    admin = usermanagement.check_perms('admin')
    loggedinuser = current_user
    checksurveyOK = dqsurveys.checkSurveyData(organisation_code)

    return render_template("surveys/survey.html",
        survey=survey,
        surveydata=surveydata,
        workflows=workflows,
        pct_complete=pct_complete,
        users=users,
        organisation=organisation,
        checksurveyOK=checksurveyOK,

        loggedinuser=loggedinuser,
        admin=admin,
    )

def getTimeRemainingNotice(deadline):
    # Skip this for now
    return ""
    remaining = deadline.date() - datetime.utcnow().date()

    if remaining.days > 1:
        return "You have %d days to submit your response." % remaining.days
    else:
        return "Today is the last day for making any changes to your survey."

def __survey_process(organisation, workflow, request,
                     organisationsurvey, published_accepted):

    indicators = dqindicators.indicators(app.config["INDICATOR_GROUP"])
    form_indicators = map(int, request.form.getlist('indicator'))

    workflow_id = workflow.Workflow.id
    currentworkflow_deadline = organisationsurvey.currentworkflow_deadline

    for indicator in indicators:
        data = {
            'organisationsurvey_id': organisationsurvey.id,
            'indicator_id': str(indicator.id),
            'workflow_id': workflow_id,
        }

        if indicator.id not in form_indicators:
            # It's an IATI indicator...
            data['published_status'] = dqsurveys.publishedStatusByName('always').id
            data['published_format'] = dqsurveys.publishedFormatByName('iati').id
        else:
            data['published_status'] = request.form.get(str(indicator.id)+"-published")

            if indicator.indicator_noformat:
                data['published_format'] = dqsurveys.publishedFormatByName('document').id
            else:
                data['published_format'] = request.form.get(str(indicator.id) + "-publishedformat")

            if indicator.indicator_ordinal:
                data['ordinal_value'] = request.form.get(str(indicator.id) + "-ordinal_value")
            else:
                data['ordinal_value'] = None

        data['published_comment'] = request.form.get(str(indicator.id)+"-comments")
        data['published_source'] = request.form.get(str(indicator.id)+"-source")
        data['published_accepted'] = published_accepted(str(indicator.id))

        surveydata = dqsurveys.addSurveyData(data)

    if 'submit' in request.form:
        if workflow.Workflow.id == organisationsurvey.currentworkflow_id:
        # save data, advance currentworkflow_id to next workflow
            dqsurveys.advanceSurvey(organisationsurvey)
            flash('Successfully submitted survey data', 'success')
        else:
            flash("Your survey data was updated.", 'warning')
    else:
        time_remaining_notice = getTimeRemainingNotice(
            organisationsurvey.currentworkflow_deadline)

        flash('Note: your survey has not yet been submitted. '
              + time_remaining_notice, 'warning')

none = lambda i: None
add_agree = lambda indicator: request.form.get(indicator + "-agree")

def _survey_process_collect(organisation, workflow,
                            request, organisationsurvey):
    return __survey_process(organisation, workflow,
                            request, organisationsurvey, none)

def _survey_process_review(organisation, workflow,
                           request, organisationsurvey):
    return __survey_process(organisation, workflow,
                            request, organisationsurvey, none)

def _survey_process_finalreview(organisation, workflow,
                                request, organisationsurvey):
    return __survey_process(organisation, workflow, request,
                            organisationsurvey,
                            add_agree)

def _survey_process_comment(organisation, workflow,
                            request, organisationsurvey):
    return __survey_process(organisation, workflow,
                            request, organisationsurvey,
                            add_agree)

def _survey_process_send(organisation, workflow, request, organisationsurvey):
    indicators = request.form.getlist('indicator')

    #FIXME: need to actually send

    dqsurveys.advanceSurvey(organisationsurvey)
    flash('Successfully sent survey to donor.', 'success')

def get_old_publication_status():
    pses = [
        ('4', 'Always published', 'success'),
        ('3', 'Sometimes published', 'warning'),
        ('2', 'Collected', 'important'),
        ('1', 'Not collected', 'inverse'),
        ('',  'Unknown', ''),
        ('iati', 'Published to IATI', 'success'),
        ('always', 'Always published', 'success'),
        ('sometimes', 'Sometimes published', 'warning'),
        ('not published', 'Not published', 'important'),
        ]
    struct = lambda ps: (ps[0], {
            "text": ps[1],
            "class": ps[2]
            })
    return dict(map(struct, pses))

def get_ordinal_values_years():
    years = [
        (3, '3 years ahead', 'success'),
        (2, '2 years ahead', 'warning'),
        (1, '1 year ahead', 'important'),
        (0, 'No forward data', 'inverse'),
        (None, 'Unknown', '')
        ]
    struct = lambda yr: (yr[0], ({
            "text": yr[1],
            "class": yr[2]
            }))
    return map(struct, years)

id_tuple = lambda p: (p.id, p)

def organisation_survey_view(organisation_code, workflow,
                             workflow_name, organisationsurvey,
                             allowed_to_edit):
    organisation = Organisation.query.filter_by(
        organisation_code=organisation_code).first_or_404()

    # the next line may be being called for its side effects
    dqsurveys.getSurveyData(organisation_code, workflow_name)

    surveydata = dqsurveys.getSurveyDataAllWorkflows(organisation_code)
    try:
        print surveydata['cso']
    except Exception:
        pass

    indicators = dqindicators.indicators(app.config["INDICATOR_GROUP"])
    org_indicators = dqorganisations._organisation_indicators_split(
        organisation, 2)

    twentytwelvedata = iatidq.survey.mapping.get_organisation_results(
        organisation_code,
        [i[1]["indicator"]["name"] for i in org_indicators["zero"].items()]
        )

    publishedstatuses = dict(map(id_tuple, dqsurveys.publishedStatus()))
    publishedformats  = dict(map(id_tuple, dqsurveys.publishedFormatAll()))
    years = get_ordinal_values_years()
    year_data = dict(years)
    years.pop()
    donorresponses = donorresponse.RESPONSE_IDS

    old_publication_status = get_old_publication_status()

    admin = usermanagement.check_perms('admin')
    loggedinuser = current_user

    org_indicators['commitment'] = util.resort_sqlalchemy_indicator(org_indicators['commitment'])
    org_indicators['zero'] = util.resort_dict_indicator(org_indicators['zero'])

    ati_year = app.config['ATI_YEAR']

    return render_template(
        "surveys/_survey_%s.html" % workflow.WorkflowType.name,
        **locals())

@app.route("/organisations/<organisation_code>/survey/<workflow_name>/", methods=["GET", "POST"])
def organisation_survey_edit(organisation_code=None, workflow_name=None):

    workflow = dqsurveys.workflowByName(workflow_name)
    if not workflow:
        flash('That workflow does not exist.', 'error')
        return abort(404)

    organisation = dqorganisations.organisations(organisation_code)
    organisationsurvey = dqsurveys.getOrCreateSurveyById(organisation.id)

    def allowed(method):
        permission_name = "survey_" + workflow_name
        permission_value = {'organisation_code': organisation_code}
        return usermanagement.check_perms(permission_name,
                                          method,
                                          permission_value)
    allowed_to_edit = allowed("edit")
    allowed_to_view = allowed("view")

    def no_permission():
        # If not logged in, redirect to login page

        if not current_user.is_authenticated():
            flash('You must log in to access that page.', 'error')
            return redirect(url_for('login', next=request.path))

        # Otherwise, redirect to previous page and warn user
        # they don't have permissions to access the survey.
        flash("Sorry, you do not have permission to view that survey", 'error')
        if request.referrer is not None:
            redir_to = request.referrer
        else:
            redir_to = url_for('home')
        return redirect(redir_to)

    if not allowed_to_view:
        return no_permission()

    if request.method != 'POST':
        return organisation_survey_view(
            organisation_code, workflow,
            workflow_name, organisationsurvey, allowed_to_edit)

    if not allowed_to_edit:
        return no_permission()

    handlers = {
        "collect": _survey_process_collect,
        "send": _survey_process_send,
        "review": _survey_process_review,
        "comment": _survey_process_comment,
        "finalreview": _survey_process_finalreview
        }

    workflow_name = workflow.WorkflowType.name

    if workflow_name == "send":
        if workflow.Workflow.id == organisationsurvey.currentworkflow_id:
            _survey_process_send(
                organisation_code, workflow, request, organisationsurvey)
        else:
            flash("Not possible to send survey to donor because it's "
                  "not at the current stage in the workflow. "
                  "Maybe you didn't submit the data, or maybe you "
                  "already sent it to the donor?", 'error')
    elif workflow_name in handlers:
        handlers[workflow_name](
            organisation, workflow, request, organisationsurvey)
    elif workflow_name == 'finalised':
        return "finalised"
    return redirect(url_for("organisations",
                            organisation_code=organisation_code))

def render_markdown(filename, **kwargs):
    path = os.path.join(os.path.dirname(__file__), 'docs', filename)
    with file(path) as f:
        plaintext = f.read()
        def _wrapped():
            content = Markup(markdown.markdown(plaintext))
            loggedinuser = current_user
            kwargs.update(locals())
            return render_template("about_generic.html", **kwargs)
        return _wrapped()

@app.route('/info/datacol')
def about_data_collection():
    return render_markdown('data_collection_guide.md')

@app.route('/info/independent')
def about_independent():
    return render_markdown('independent_guide.md')

@app.route('/info/donor')
def about_donor():
    return render_markdown('donor_guide.md')

<commit_msg>is_authenticated is now a property<commit_after>
#  IATI Data Quality, tools for Data QA on IATI-formatted  publications
#  by Mark Brough, Martin Keegan, Ben Webb and Jennifer Smith
#
#  Copyright (C) 2013  Publish What You Fund
#
#  This programme is free software; you may redistribute and/or modify
#  it under the terms of the GNU Affero General Public License v3.0

import os

from flask import render_template, flash, request, Markup, redirect, url_for, abort
from flask_login import current_user
import markdown

from . import app, usermanagement
from iatidq import dqindicators, dqorganisations, dqusers, donorresponse, util
import iatidq.survey.data as dqsurveys
import iatidq.survey.mapping
from iatidq.models import Organisation

@app.route("/surveys/admin/")
@usermanagement.perms_required()
def surveys_admin():
    surveys = dqsurveys.surveys()
    workflows = dqsurveys.workflowsAll()
    publishedstatuses=dqsurveys.publishedStatus()
    admin = usermanagement.check_perms('admin')
    loggedinuser = current_user

    return render_template("surveys/surveys_admin.html",
                           **locals())

@app.route("/surveys/create/", methods=["GET", "POST"])
@app.route("/surveys/<organisation_code>/create/", methods=["GET", "POST"])
def create_survey(organisation_code=None):
    return "You're trying to create a survey"

def completion_percentage(survey):
    stages = ['researcher', 'send', 'donorreview', 'pwyfreview',
              'cso', 'donorcomments', 'pwyffinal', 'finalised']

    # can ValueError; used to raise NameError
    idx = stages.index(survey.workflow.name)

    return 100. * idx / (len(stages) - 1)

@app.route("/organisations/<organisation_code>/survey/repair/")
@usermanagement.perms_required('survey', 'view')
def organisation_survey_repair(organisation_code):
    status = dqsurveys.repairSurveyData(organisation_code)
    if status['changes'] == True:
        indicators = ", ".join(status['changed_indicators'])
        flash('Survey successfully repaired indicators '+indicators, 'success')
    else:
        flash('Survey could not be repaired', 'error')
    return redirect(url_for('organisation_survey', organisation_code=organisation_code))

@app.route("/organisations/<organisation_code>/survey/")
@usermanagement.perms_required('survey', 'view')
def organisation_survey(organisation_code=None):
    organisation = dqorganisations.organisations(organisation_code)
    # make sure survey exists
    survey = dqsurveys.getOrCreateSurveyById(organisation.id)

    surveydata = dqsurveys.getSurveyDataAllWorkflows(organisation_code)
    workflows = dqsurveys.workflowsAll()
    pct_complete = completion_percentage(survey)
    users = dqusers.surveyPermissions(organisation_code)
    admin = usermanagement.check_perms('admin')
    loggedinuser = current_user
    checksurveyOK = dqsurveys.checkSurveyData(organisation_code)

    return render_template("surveys/survey.html",
        survey=survey,
        surveydata=surveydata,
        workflows=workflows,
        pct_complete=pct_complete,
        users=users,
        organisation=organisation,
        checksurveyOK=checksurveyOK,

        loggedinuser=loggedinuser,
        admin=admin,
    )

def getTimeRemainingNotice(deadline):
    # Skip this for now
    return ""
    remaining = deadline.date() - datetime.utcnow().date()

    if remaining.days > 1:
        return "You have %d days to submit your response." % remaining.days
    else:
        return "Today is the last day for making any changes to your survey."

def __survey_process(organisation, workflow, request,
                     organisationsurvey, published_accepted):

    indicators = dqindicators.indicators(app.config["INDICATOR_GROUP"])
    form_indicators = map(int, request.form.getlist('indicator'))

    workflow_id = workflow.Workflow.id
    currentworkflow_deadline = organisationsurvey.currentworkflow_deadline

    for indicator in indicators:
        data = {
            'organisationsurvey_id': organisationsurvey.id,
            'indicator_id': str(indicator.id),
            'workflow_id': workflow_id,
        }

        if indicator.id not in form_indicators:
            # It's an IATI indicator...
            data['published_status'] = dqsurveys.publishedStatusByName('always').id
            data['published_format'] = dqsurveys.publishedFormatByName('iati').id
        else:
            data['published_status'] = request.form.get(str(indicator.id)+"-published")

            if indicator.indicator_noformat:
                data['published_format'] = dqsurveys.publishedFormatByName('document').id
            else:
                data['published_format'] = request.form.get(str(indicator.id) + "-publishedformat")

            if indicator.indicator_ordinal:
                data['ordinal_value'] = request.form.get(str(indicator.id) + "-ordinal_value")
            else:
                data['ordinal_value'] = None

        data['published_comment'] = request.form.get(str(indicator.id)+"-comments")
        data['published_source'] = request.form.get(str(indicator.id)+"-source")
        data['published_accepted'] = published_accepted(str(indicator.id))

        surveydata = dqsurveys.addSurveyData(data)

    if 'submit' in request.form:
        if workflow.Workflow.id == organisationsurvey.currentworkflow_id:
        # save data, advance currentworkflow_id to next workflow
            dqsurveys.advanceSurvey(organisationsurvey)
            flash('Successfully submitted survey data', 'success')
        else:
            flash("Your survey data was updated.", 'warning')
    else:
        time_remaining_notice = getTimeRemainingNotice(
            organisationsurvey.currentworkflow_deadline)

        flash('Note: your survey has not yet been submitted. '
              + time_remaining_notice, 'warning')

none = lambda i: None
add_agree = lambda indicator: request.form.get(indicator + "-agree")

def _survey_process_collect(organisation, workflow,
                            request, organisationsurvey):
    return __survey_process(organisation, workflow,
                            request, organisationsurvey, none)

def _survey_process_review(organisation, workflow,
                           request, organisationsurvey):
    return __survey_process(organisation, workflow,
                            request, organisationsurvey, none)

def _survey_process_finalreview(organisation, workflow,
                                request, organisationsurvey):
    return __survey_process(organisation, workflow, request,
                            organisationsurvey,
                            add_agree)

def _survey_process_comment(organisation, workflow,
                            request, organisationsurvey):
    return __survey_process(organisation, workflow,
                            request, organisationsurvey,
                            add_agree)

def _survey_process_send(organisation, workflow, request, organisationsurvey):
    indicators = request.form.getlist('indicator')

    #FIXME: need to actually send

    dqsurveys.advanceSurvey(organisationsurvey)
    flash('Successfully sent survey to donor.', 'success')

def get_old_publication_status():
    pses = [
        ('4', 'Always published', 'success'),
        ('3', 'Sometimes published', 'warning'),
        ('2', 'Collected', 'important'),
        ('1', 'Not collected', 'inverse'),
        ('',  'Unknown', ''),
        ('iati', 'Published to IATI', 'success'),
        ('always', 'Always published', 'success'),
        ('sometimes', 'Sometimes published', 'warning'),
        ('not published', 'Not published', 'important'),
        ]
    struct = lambda ps: (ps[0], {
            "text": ps[1],
            "class": ps[2]
            })
    return dict(map(struct, pses))

def get_ordinal_values_years():
    years = [
        (3, '3 years ahead', 'success'),
        (2, '2 years ahead', 'warning'),
        (1, '1 year ahead', 'important'),
        (0, 'No forward data', 'inverse'),
        (None, 'Unknown', '')
        ]
    struct = lambda yr: (yr[0], ({
            "text": yr[1],
            "class": yr[2]
            }))
    return map(struct, years)

id_tuple = lambda p: (p.id, p)

def organisation_survey_view(organisation_code, workflow,
                             workflow_name, organisationsurvey,
                             allowed_to_edit):
    organisation = Organisation.query.filter_by(
        organisation_code=organisation_code).first_or_404()

    # the next line may be being called for its side effects
    dqsurveys.getSurveyData(organisation_code, workflow_name)

    surveydata = dqsurveys.getSurveyDataAllWorkflows(organisation_code)
    try:
        print surveydata['cso']
    except Exception:
        pass

    indicators = dqindicators.indicators(app.config["INDICATOR_GROUP"])
    org_indicators = dqorganisations._organisation_indicators_split(
        organisation, 2)

    twentytwelvedata = iatidq.survey.mapping.get_organisation_results(
        organisation_code,
        [i[1]["indicator"]["name"] for i in org_indicators["zero"].items()]
        )

    publishedstatuses = dict(map(id_tuple, dqsurveys.publishedStatus()))
    publishedformats  = dict(map(id_tuple, dqsurveys.publishedFormatAll()))
    years = get_ordinal_values_years()
    year_data = dict(years)
    years.pop()
    donorresponses = donorresponse.RESPONSE_IDS

    old_publication_status = get_old_publication_status()

    admin = usermanagement.check_perms('admin')
    loggedinuser = current_user

    org_indicators['commitment'] = util.resort_sqlalchemy_indicator(org_indicators['commitment'])
    org_indicators['zero'] = util.resort_dict_indicator(org_indicators['zero'])

    ati_year = app.config['ATI_YEAR']

    return render_template(
        "surveys/_survey_%s.html" % workflow.WorkflowType.name,
        **locals())

@app.route("/organisations/<organisation_code>/survey/<workflow_name>/", methods=["GET", "POST"])
def organisation_survey_edit(organisation_code=None, workflow_name=None):

    workflow = dqsurveys.workflowByName(workflow_name)
    if not workflow:
        flash('That workflow does not exist.', 'error')
        return abort(404)

    organisation = dqorganisations.organisations(organisation_code)
    organisationsurvey = dqsurveys.getOrCreateSurveyById(organisation.id)

    def allowed(method):
        permission_name = "survey_" + workflow_name
        permission_value = {'organisation_code': organisation_code}
        return usermanagement.check_perms(permission_name,
                                          method,
                                          permission_value)
    allowed_to_edit = allowed("edit")
    allowed_to_view = allowed("view")

    def no_permission():
        # If not logged in, redirect to login page

        if not current_user.is_authenticated:
            flash('You must log in to access that page.', 'error')
            return redirect(url_for('login', next=request.path))

        # Otherwise, redirect to previous page and warn user
        # they don't have permissions to access the survey.
        flash("Sorry, you do not have permission to view that survey", 'error')
        if request.referrer is not None:
            redir_to = request.referrer
        else:
            redir_to = url_for('home')
        return redirect(redir_to)

    if not allowed_to_view:
        return no_permission()

    if request.method != 'POST':
        return organisation_survey_view(
            organisation_code, workflow,
            workflow_name, organisationsurvey, allowed_to_edit)

    if not allowed_to_edit:
        return no_permission()

    handlers = {
        "collect": _survey_process_collect,
        "send": _survey_process_send,
        "review": _survey_process_review,
        "comment": _survey_process_comment,
        "finalreview": _survey_process_finalreview
        }

    workflow_name = workflow.WorkflowType.name

    if workflow_name == "send":
        if workflow.Workflow.id == organisationsurvey.currentworkflow_id:
            _survey_process_send(
                organisation_code, workflow, request, organisationsurvey)
        else:
            flash("Not possible to send survey to donor because it's "
                  "not at the current stage in the workflow. "
                  "Maybe you didn't submit the data, or maybe you "
                  "already sent it to the donor?", 'error')
    elif workflow_name in handlers:
        handlers[workflow_name](
            organisation, workflow, request, organisationsurvey)
    elif workflow_name == 'finalised':
        return "finalised"
    return redirect(url_for("organisations",
                            organisation_code=organisation_code))

def render_markdown(filename, **kwargs):
    path = os.path.join(os.path.dirname(__file__), 'docs', filename)
    with file(path) as f:
        plaintext = f.read()
        def _wrapped():
            content = Markup(markdown.markdown(plaintext))
            loggedinuser = current_user
            kwargs.update(locals())
            return render_template("about_generic.html", **kwargs)
        return _wrapped()

@app.route('/info/datacol')
def about_data_collection():
    return render_markdown('data_collection_guide.md')

@app.route('/info/independent')
def about_independent():
    return render_markdown('independent_guide.md')

@app.route('/info/donor')
def about_donor():
    return render_markdown('donor_guide.md')

<|endoftext|>


<commit_before>"""
Dummy database backend for Django.

Django uses this if the database ENGINE setting is empty (None or empty string).

Each of these API functions, except connection.close(), raises
ImproperlyConfigured.
"""

from django.core.exceptions import ImproperlyConfigured
from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.base.client import BaseDatabaseClient
from django.db.backends.base.creation import BaseDatabaseCreation
from django.db.backends.base.introspection import BaseDatabaseIntrospection
from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.backends.base.validation import BaseDatabaseValidation
from django.db.backends.dummy.features import DummyDatabaseFeatures

def complain(*args, **kwargs):
    raise ImproperlyConfigured("settings.DATABASES is improperly configured. "
                               "Please supply the ENGINE value. Check "
                               "settings documentation for more details.")

def ignore(*args, **kwargs):
    pass

class DatabaseError(Exception):
    pass

class IntegrityError(DatabaseError):
    pass

class DatabaseOperations(BaseDatabaseOperations):
    quote_name = complain

class DatabaseClient(BaseDatabaseClient):
    runshell = complain

class DatabaseCreation(BaseDatabaseCreation):
    create_test_db = ignore
    destroy_test_db = ignore

class DatabaseIntrospection(BaseDatabaseIntrospection):
    get_table_list = complain
    get_table_description = complain
    get_relations = complain
    get_indexes = complain
    get_key_columns = complain

class DatabaseWrapper(BaseDatabaseWrapper):
    operators = {}
    # Override the base class implementations with null
    # implementations. Anything that tries to actually
    # do something raises complain; anything that tries
    # to rollback or undo something raises ignore.
    _cursor = complain
    ensure_connection = complain
    _commit = complain
    _rollback = ignore
    _close = ignore
    _savepoint = ignore
    _savepoint_commit = complain
    _savepoint_rollback = ignore
    _set_autocommit = complain

    def __init__(self, *args, **kwargs):
        super(DatabaseWrapper, self).__init__(*args, **kwargs)

        self.features = DummyDatabaseFeatures(self)
        self.ops = DatabaseOperations(self)
        self.client = DatabaseClient(self)
        self.creation = DatabaseCreation(self)
        self.introspection = DatabaseIntrospection(self)
        self.validation = BaseDatabaseValidation(self)

    def is_usable(self):
        return True
<commit_msg>base config for dummy db<commit_after>"""
Each of these API functions, except connection.close(), raises
ImproperlyConfigured.
"""

from ibu.backends.base.base import ImproperlyConfigured
from ibu.backends.base.base import BaseDatabaseWrapper
from ibu.backends.base.client import BaseDatabaseClient
from ibu.backends.base.creation import BaseDatabaseCreation
from ibu.backends.base.introspection import BaseDatabaseIntrospection
from ibu.backends.base.operations import BaseDatabaseOperations
from ibu.backends.base.validation import BaseDatabaseValidation
from ibu.backends.dummy.features import DummyDatabaseFeatures

def complain(*args, **kwargs):
    raise ImproperlyConfigured("settings.DATABASES is improperly configured. "
                               "Please supply the ENGINE value. Check "
                               "settings documentation for more details.")

def ignore(*args, **kwargs):
    pass

class DatabaseError(Exception):
    pass

class IntegrityError(DatabaseError):
    pass

class DatabaseOperations(BaseDatabaseOperations):
    quote_name = complain

class DatabaseClient(BaseDatabaseClient):
    runshell = complain

class DatabaseCreation(BaseDatabaseCreation):
    create_test_db = ignore
    destroy_test_db = ignore

class DatabaseIntrospection(BaseDatabaseIntrospection):
    get_table_list = complain
    get_table_description = complain
    get_relations = complain
    get_indexes = complain
    get_key_columns = complain

class DatabaseWrapper(BaseDatabaseWrapper):
    operators = {}
    # Override the base class implementations with null
    # implementations. Anything that tries to actually
    # do something raises complain; anything that tries
    # to rollback or undo something raises ignore.
    _cursor = complain
    ensure_connection = complain
    _commit = complain
    _rollback = ignore
    _close = ignore
    _savepoint = ignore
    _savepoint_commit = complain
    _savepoint_rollback = ignore
    _set_autocommit = complain

    def __init__(self, *args, **kwargs):
        super(DatabaseWrapper, self).__init__(*args, **kwargs)

        self.features = DummyDatabaseFeatures(self)
        self.ops = DatabaseOperations(self)
        self.client = DatabaseClient(self)
        self.creation = DatabaseCreation(self)
        self.introspection = DatabaseIntrospection(self)
        self.validation = BaseDatabaseValidation(self)

    def is_usable(self):
        return True
<|endoftext|>


<commit_before>#!/usr/bin/env python
# encoding: utf-8
"""
Compute the differences in between images.

Take a list of files in a directory with BEFORE AFTER naming
and compute the difference according to a ratio.
"""

import argparse
import difflib
import os

def cli():
    "Parse the arguments on the commandline."
    parser = argparse.ArgumentParser(description="Compute image differences.")
    parser.add_argument('-p', action='store', dest='dir_path',
                        help='Directory path where are the files')
    args = parser.parse_args()
    return args

def images_list(dir_path):
    """Create an image list of the directory content.

    The list will collect only the filename prefix.
    """
    prefix_list = [filename[:-11] for filename in os.listdir(dir_path)
                   if filename.endswith('-BEFORE.png')]
    return prefix_list

def diff_ratio(s1, s2):
    """Compute the diff ratio in between two images."""
    s = difflib.SequenceMatcher(None, s1, s2)
    return s.quick_ratio()

def main():
    """Main."""
    # Let's parse the command line
    args = cli()
    # we get the directory
    dir_path = args.dir_path
    prefix_list = images_list(dir_path)

if __name__ == "__main__":
    main()
<commit_msg>getting the path of the two images<commit_after>#!/usr/bin/env python
# encoding: utf-8
"""
Compute the differences in between images.

Take a list of files in a directory with BEFORE AFTER naming
and compute the difference according to a ratio.
"""

import argparse
import difflib
import os

def cli():
    "Parse the arguments on the commandline."
    parser = argparse.ArgumentParser(description="Compute image differences.")
    parser.add_argument('-p', action='store', dest='dir_path',
                        help='Directory path where are the files')
    args = parser.parse_args()
    return args

def images_list(dir_path):
    """Create an image list of the directory content.

    The list will collect only the filename prefix.
    """
    prefix_list = [filename[:-11] for filename in os.listdir(dir_path)
                   if filename.endswith('-BEFORE.png')]
    return prefix_list

def diff_ratio(s1, s2):
    """Compute the diff ratio in between two images."""
    s = difflib.SequenceMatcher(None, s1, s2)
    return s.quick_ratio()

def image_tuple(prefix, dir_path):
    """Create a tuple of full path for images."""

    path_before = os.path.join(dir_path, prefix + '-BEFORE.png')
    path_after = os.path.join(dir_path, prefix + '-AFTER.png')
    return path_before, path_after

def main():
    """Main."""
    # Let's parse the command line
    args = cli()
    # we get the directory
    dir_path = args.dir_path
    prefix_list = images_list(dir_path)

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


<commit_before>#!/usr/bin/python2

def get_argument_parser():
    parser = argparse.ArgumentParser(
        description="RCAS provides intuitive reports and publication ready graphics"
        " from input peak intervals in BED foramt,"
        " which are detected in clip-seq data."
        )

    parser.add_argument('--version',
                        action='version',
                        version='%(prog)s 0.1')

    parser.add_argument("BED",
                        metavar="BED",
                        nargs="*",
                        help="Target intervals in BED format.")

    parser.add_argument("--RCAS_path", "-r",
                        metavar="path/to/RCAS",
                        required=True,
                        help="Path to RCAS.")

    parser.add_argument("--genome", "-g",
                        metavar="FILE",
                        required=True,
                        help="Reference genome whose version should conform with"
                        " generation of input peak invervals.")

    parser.add_argument("--gff3", "-f",
                        metavar="FILE",
                        required=True,
                        help="Annotation reference in gff3 format.")

    parser.add_argument("--run_motif", "-m",
                        default="False",
                        choices=["True", "False"],
                        help="True: run motif search."
                        " False (default): not run.")

    parser.add_argument("--run_PATHrich", "-p",
                        default="False",
                        choices=["True", "False"],
                        help="True: run pathway enrichment."
                        " False (default): not run.")

    parser.add_argument("--run_GOrich", "-t",
                        default="False",
                        choices=["True", "False"],
                        help="True: run GO-term enrichment."
                        " False (default): not run.")

    parser.add_argument("--run_coverage", "-c",
                        default="False",
                        choices=["True", "False"],
                        help="True: run coverage profile."
                        " False (default): not run.")

    return parser

def extract_key(filename):
    base = os.path.basename(filename)
    key = base.split(".")[:-1]
    key = ".".join(key)
    return key

def generate_config(args):
    BED_files = args.BED
    infiles = {}

    for BED in BED_files:
        infiles[extract_key(BED)] = BED

    config = {
      "RCAS_path": os.path.abspath(args.RCAS_path),

      "gff3": args.gff3,

      "genome": args.genome,

      "infile": infiles,

      "switch": {
        "run_motif": args.run_motif,
        "run_PATHrich": args.run_PATHrich,
        "run_GOrich": args.run_GOrich,
        "run_coverage": args.run_coverage
      }
    }

    # Writing JSON data
    with open('config.json', 'w') as f:
         json.dump(config, f, sort_keys=True, indent=4)

    print "wrote config.json.\n"

def call_snakemake(RCAS_path):
    print "start snakemake:\n"

    subprocess.call("snakemake -p -s %s/src/RCAS.snakefile" % RCAS_path, shell=True)

if __name__ == '__main__':
    import argparse
    import json
    import os
    import subprocess

    #process commandline Arguments
    parser = get_argument_parser()
    args = parser.parse_args()

    #dump argument to config.json
    generate_config(args)

    #run snakemake
    call_snakemake(args.RCAS_path)
<commit_msg>introduce species option and forcerun option to RCAS.py<commit_after>#!/usr/bin/python2

def get_argument_parser():
    parser = argparse.ArgumentParser(
        description="RCAS provides intuitive reports and publication ready graphics"
        " from input peak intervals in BED foramt,"
        " which are detected in clip-seq data."
        )

    parser.add_argument('--version',
                        action='version',
                        version='%(prog)s 0.1')

    parser.add_argument("BED",
                        metavar="BED",
                        nargs="*",
                        help="Target intervals in BED format.")

    parser.add_argument("--RCAS_path", "-r",
                        metavar="path/to/RCAS",
                        required=True,
                        help="Path to RCAS.")

    parser.add_argument("--genome", "-g",
                        metavar="FILE",
                        required=True,
                        help="Reference genome whose version should conform with"
                        " generation of input peak invervals.")

    parser.add_argument("--gff3", "-f",
                        metavar="FILE",
                        required=True,
                        help="Annotation reference in gff3 format"
                        " whose version should conform with"
                        " genome version.")

    parser.add_argument("--run_motif", "-m",
                        default="False",
                        choices=["True", "False"],
                        help="True: run motif search."
                        " False (default): not run.")

    parser.add_argument("--run_PATHrich", "-p",
                        default="False",
                        choices=["True", "False"],
                        help="True: run pathway enrichment."
                        " False (default): not run.")

    parser.add_argument("--run_GOrich", "-t",
                        default="False",
                        choices=["True", "False"],
                        help="True: run GO-term enrichment."
                        " False (default): not run.")

    parser.add_argument("--run_coverage", "-c",
                        default="False",
                        choices=["True", "False"],
                        help="True: run coverage profile."
                        " False (default): not run.")

    parser.add_argument("--species", "-s",
                        default="human",
                        choices=["human", "fly", "worm", "mouse"],
                        help="Required for running GO-term or pathway enrichment."
                        " Default: human")

    parser.add_argument("--forcerun", "-F",
                        action='store_true',
                        help=("Force the re-execution of snakemake."
                              " Use this option if you want to have all "
                              "output in your workflow updated."))

    return parser

def extract_key(filename):
    base = os.path.basename(filename)
    key = base.split(".")[:-1]
    key = ".".join(key)
    return key

def generate_config(args):
    BED_files = args.BED
    infiles = {}

    for BED in BED_files:
        infiles[extract_key(BED)] = BED

    config = {
      "RCAS_path": os.path.abspath(args.RCAS_path),

      "gff3": args.gff3,

      "genome": args.genome,

      "species": args.species,

      "infile": infiles,

      "switch": {
        "run_motif": args.run_motif,
        "run_PATHrich": args.run_PATHrich,
        "run_GOrich": args.run_GOrich,
        "run_coverage": args.run_coverage
      }
    }

    # Writing JSON data
    with open('config.json', 'w') as f:
         json.dump(config, f, sort_keys=True, indent=4)

    print "wrote config.json.\n"

def call_snakemake(RCAS_path):
    print "start snakemake:\n"

    if args.forcerun:
        forcerun = "F"
    else:
        forcerun = ""

    subprocess.call("snakemake -%sp -s %s/src/RCAS.snakefile" % (forcerun, RCAS_path), shell=True)

if __name__ == '__main__':
    import argparse
    import json
    import os
    import subprocess

    #process commandline Arguments
    parser = get_argument_parser()
    args = parser.parse_args()

    #dump argument to config.json
    generate_config(args)

    #run snakemake
    call_snakemake(args.RCAS_path)
<|endoftext|>


<commit_before>
import bottle
import json
import random

from . import DatabaseManager
from .product import Product
import recommender.vector.arithmetic
import recommender.rocchio.algorithm

@bottle.route('/product/get/<doc_id:int>')
def product_get(doc_id):
    d = product_manager.get_product(doc_id).as_dictionary()
    result = {'result': d}
    return result

@bottle.route('/product/all')
def product_get_all():
    l = [ p.as_dictionary() for p in product_manager.get_all_products()]
    result = {'result': l}
    #bottle.response.content_type = 'application/json'
    return result

@bottle.route('/product/random/<count:int>')
def product_random(count):
    products = product_manager.get_all_products()
    rands = []
    while len(rands) < count:
        index = random_generator.randint(0, len(products)-1)

        rands.append(products[index].as_dictionary())

        products.remove(products[index])
        pass
    result = {'result': rands};
    return result

@bottle.route('/product/insert', method='POST')
def product_insert():
    """
    curl -X POST -d "product={'image_name':'img.jpg','terms':{'a':1,'b':3}}"
    """
    product_json = bottle.request.forms.get('product')
    product_dict = json.loads(product_json)

    p = Product()
    p.image_name = product_dict['image_name']
    p.terms = product_dict['terms']

    product_manager.add_document(p)
    pass

@bottle.route('/vector/default/<doc_id:int>')
def vector_default(doc_id):
    d = (product_vector_manager
        .get_vector_for_document_id(doc_id)
        .as_dictionary()
        )
    result = {'result': d}
    return result

@bottle.route('/vector/df')
def vector_df():
    d = (
        product_vector_manager
        .get_document_frequency_vector()
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/idf')
def vector_idf():
    d = (
        product_vector_manager
        .get_inverse_document_frequency_vector()
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/tf/<doc_id:int>')
def vector_tf(doc_id):
    d = (
        product_vector_manager
        .get_term_frequency_vector(doc_id)
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/tfidf/<doc_id:int>')
def vector_tfidf(doc_id):
    d = (
        product_vector_manager
        .get_tfidf_vector(doc_id)
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/user/<user_id:int>')
def vector_user_by_id(user_id):
    d = (
        user_vector_manager
            .get_user_vector_for_id(user_id)
            .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/user/<user_name>')
def vector_user_by_name(user_name):
    d = (
        user_vector_manager
            .get_user_vector_for_name(user_name)
            .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/user/create/<user_name>')
def create_user_by_name(user_name):
    user_vector_manager.create_user(user_name)
    pass

@bottle.route('/user/exists/<user_name>')
def exists_user_by_name(user_name):
    d = {}
    d['exists'] = user_vector_manager.has_user_with_name(user_name)
    result = {'result': d}
    return result

@bottle.route('/user/setpreference/<user_name>/<product_id:int>')
def add_preference_to_user(user_name, product_id):
    user_id = user_vector_manager.get_user_id_for_name(user_name)
    user_vector_manager.set_user_preference(user_id, product_id, True)

    update_user(user_id)
    pass

@bottle.route('/user/setnopreference/<user_name>/<product_id:int>')
def add_preference_to_user(user_name, product_id):
    user_id = user_vector_manager.get_user_id_for_name(user_name)
    user_vector_manager.set_user_preference(user_id, product_id, False)

    update_user(user_id)
    pass

@bottle.route('/recommendations/<user_name>/<k:int>')
def get_recommendation(user_name, k):
    vector = user_vector_manager.get_user_vector_for_name(user_name)
    others = product_vector_manager.get_all_vectors()
    recommendations = vector_arithmetic.k_nearest_neighbours(k, vector, others)
    products = [
        product_manager.get_product(doc_id).as_dictionary()
        for distance, doc_id in recommendations
    ]
    result = {'result': products}
    return result

database_manager = None
product_manager = None
product_vector_manager = None
document_manager = None
user_vector_manager = None
term_manager = None

random_generator = None

vector_arithmetic = recommender.vector.arithmetic

def run(database_path, host, port):
    _init(database_path)
    bottle.run(host=host, port=port, debug=True)

def _init(database_path):
    global database_manager
    global product_manager
    global product_vector_manager
    global document_manager
    global user_vector_manager
    global term_manager

    global random_generator

    database_manager = DatabaseManager(database_path)
    product_manager = database_manager.get_product_manager()
    product_vector_manager = database_manager.get_product_vector_manager()
    document_manager = database_manager.get_document_manager()
    user_vector_manager  = database_manager.get_user_vector_manager()
    term_manager = database_manager.get_term_manager()

    random_generator = random.Random()

def update_user(user_id):
    user_vector = user_vector_manager.get_user_vector_for_id(user_id)
    relevant = user_vector_manager.get_relevant_document_vector_list(user_id)
    non_relevant = user_vector_manager.get_non_relevant_document_vector_list(user_id)

    rocchio_constants = recommender.rocchio.RocchioConstant()
    #rocchio_constants.a = 0.5
    #rocchio_constants.a = 0.5
    #rocchio_constants.a = 0.5

    uvector = recommender.rocchio.algorithm.calculate(user_vector, relevant, non_relevant, rocchio_constants)
    user_vector_manager.update_user_vector(user_id, uvector);
    pass
<commit_msg>testing rocchio behaviour for different rocchio-variable values<commit_after>
import bottle
import json
import random

from . import DatabaseManager
from .product import Product
import recommender.vector.arithmetic
import recommender.rocchio.algorithm

@bottle.route('/product/get/<doc_id:int>')
def product_get(doc_id):
    d = product_manager.get_product(doc_id).as_dictionary()
    result = {'result': d}
    return result

@bottle.route('/product/all')
def product_get_all():
    l = [ p.as_dictionary() for p in product_manager.get_all_products()]
    result = {'result': l}
    #bottle.response.content_type = 'application/json'
    return result

@bottle.route('/product/random/<count:int>')
def product_random(count):
    products = product_manager.get_all_products()
    rands = []
    while len(rands) < count:
        index = random_generator.randint(0, len(products)-1)

        rands.append(products[index].as_dictionary())

        products.remove(products[index])
        pass
    result = {'result': rands};
    return result

@bottle.route('/product/insert', method='POST')
def product_insert():
    """
    curl -X POST -d "product={'image_name':'img.jpg','terms':{'a':1,'b':3}}"
    """
    product_json = bottle.request.forms.get('product')
    product_dict = json.loads(product_json)

    p = Product()
    p.image_name = product_dict['image_name']
    p.terms = product_dict['terms']

    product_manager.add_document(p)
    pass

@bottle.route('/vector/default/<doc_id:int>')
def vector_default(doc_id):
    d = (product_vector_manager
        .get_vector_for_document_id(doc_id)
        .as_dictionary()
        )
    result = {'result': d}
    return result

@bottle.route('/vector/df')
def vector_df():
    d = (
        product_vector_manager
        .get_document_frequency_vector()
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/idf')
def vector_idf():
    d = (
        product_vector_manager
        .get_inverse_document_frequency_vector()
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/tf/<doc_id:int>')
def vector_tf(doc_id):
    d = (
        product_vector_manager
        .get_term_frequency_vector(doc_id)
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/tfidf/<doc_id:int>')
def vector_tfidf(doc_id):
    d = (
        product_vector_manager
        .get_tfidf_vector(doc_id)
        .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/user/<user_id:int>')
def vector_user_by_id(user_id):
    d = (
        user_vector_manager
            .get_user_vector_for_id(user_id)
            .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/vector/user/<user_name>')
def vector_user_by_name(user_name):
    d = (
        user_vector_manager
            .get_user_vector_for_name(user_name)
            .as_dictionary()
    )
    result = {'result': d}
    return result

@bottle.route('/user/create/<user_name>')
def create_user_by_name(user_name):
    user_vector_manager.create_user(user_name)
    pass

@bottle.route('/user/exists/<user_name>')
def exists_user_by_name(user_name):
    d = {}
    d['exists'] = user_vector_manager.has_user_with_name(user_name)
    result = {'result': d}
    return result

@bottle.route('/user/setpreference/<user_name>/<product_id:int>')
def add_preference_to_user(user_name, product_id):
    user_id = user_vector_manager.get_user_id_for_name(user_name)
    user_vector_manager.set_user_preference(user_id, product_id, True)

    update_user(user_id)
    pass

@bottle.route('/user/setnopreference/<user_name>/<product_id:int>')
def add_preference_to_user(user_name, product_id):
    user_id = user_vector_manager.get_user_id_for_name(user_name)
    user_vector_manager.set_user_preference(user_id, product_id, False)

    update_user(user_id)
    pass

@bottle.route('/recommendations/<user_name>/<k:int>')
def get_recommendation(user_name, k):
    vector = user_vector_manager.get_user_vector_for_name(user_name)
    others = product_vector_manager.get_all_vectors()
    recommendations = vector_arithmetic.k_nearest_neighbours(k, vector, others)
    products = [
        product_manager.get_product(doc_id).as_dictionary()
        for distance, doc_id in recommendations
    ]
    result = {'result': products}
    return result

database_manager = None
product_manager = None
product_vector_manager = None
document_manager = None
user_vector_manager = None
term_manager = None

random_generator = None

vector_arithmetic = recommender.vector.arithmetic

def run(database_path, host, port):
    _init(database_path)
    bottle.run(host=host, port=port, debug=True)

def _init(database_path):
    global database_manager
    global product_manager
    global product_vector_manager
    global document_manager
    global user_vector_manager
    global term_manager

    global random_generator

    database_manager = DatabaseManager(database_path)
    product_manager = database_manager.get_product_manager()
    product_vector_manager = database_manager.get_product_vector_manager()
    document_manager = database_manager.get_document_manager()
    user_vector_manager  = database_manager.get_user_vector_manager()
    term_manager = database_manager.get_term_manager()

    random_generator = random.Random()

def update_user(user_id):
    user_vector = user_vector_manager.get_user_vector_for_id(user_id)
    relevant = user_vector_manager.get_relevant_document_vector_list(user_id)
    non_relevant = user_vector_manager.get_non_relevant_document_vector_list(user_id)

    rocchio_constants = recommender.rocchio.RocchioConstant()
    rocchio_constants.a = 0.5
    #rocchio_constants.a = 0.5
    #rocchio_constants.a = 0.5

    uvector = recommender.rocchio.algorithm.calculate(user_vector, relevant, non_relevant, rocchio_constants)
    user_vector_manager.update_user_vector(user_id, uvector);
    pass
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    Daniel Reis
#    2011
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

{
    'name': 'Import data from SQL and ODBC data sources.',
    'version': '1.3',
    'category': 'Tools',
    'description': """
Import data directly from other databases.

Installed in the Administration module, menu Configuration -> Import from SQL.

Features:
 * Fetched data from the databases are used to build lines equivalent to
   regular import files. These are imported using the standard "import_data()"
   ORM method, benefiting from all its features, including xml_ids.
 * Each table import is defined by an SQL statement, used to build the
   equivalent for an import file. Each column's name should match the column
   names you would use in an import file. The first column must provide an
   unique identifier for the record, and will be used to build its xml_id.
 * SQL columns named "none" are ignored. This can be used for the first column
   of the SQL, so that it's used to build the XML Id but it's not imported to
   any OpenERP field.
 * The last sync date is the last successfull execution can be used in the SQL
   using "%(sync)s", or ":sync" in the case of Oracle.
 * When errors are found, only the record with the error fails import. The
   other correct records are commited. However, the "last sync date" will only
   be automaticaly updated when no errors are found.
 * The import execution can be scheduled to run automatically.

Examples:
 * Importing suppliers to res.partner:
      SELECT distinct
            [SUPPLIER_CODE] as "ref"
          , [SUPPLIER_NAME] as "name"
          , 1 as "is_supplier"
          , [INFO] as "comment"
        FROM T_SUPPLIERS
       WHERE INACTIVE_DATE IS NULL and DATE_CHANGED >= %(sync)s

 * Importing products to product.product:
      SELECT PRODUCT_CODE as "ref"
           , PRODUCT_NAME as "name"
           , 'res_partner_id_'+SUPPLIER_ID as "partner_id/id"
        FROM T_PRODUCTS
       WHERE DATE_CHANGED >= %(sync)s

Improvements ideas waiting for a contributor:
 * Allow to import many2one fields (currently not supported). Done by adding a
   second SQL sentence to get child record list?
 * Allow "import sets" that can be executed at different time intervals using
   different scheduler jobs.
 * Allow to inactivate/delete OpenERP records when not present in an SQL
   result set.

Contributors
============

* Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com>
    """,
    'author': "Daniel Reis,Odoo Community Association (OCA)",
    'website': 'http://launchpad.net/addons-tko',
    'license': 'AGPL-3',
    'images': [
        'images/snapshot1.png',
        'images/snapshot2.png',
    ],
    'depends': [
        'base',
        'base_external_dbsource',
    ],
    'data': [
        'import_odbc_view.xml',
        'security/ir.model.access.csv',
    ],
    'demo': [
        'import_odbc_demo.xml',
    ],
    'test': [],
    'installable': False,
    'active': False,
}

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>Make module installable<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    Daniel Reis
#    2011
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

{
    'name': 'Import data from SQL and ODBC data sources.',
    'version': '1.3',
    'category': 'Tools',
    'description': """
Import data directly from other databases.

Installed in the Administration module, menu Configuration -> Import from SQL.

Features:
 * Fetched data from the databases are used to build lines equivalent to
   regular import files. These are imported using the standard "import_data()"
   ORM method, benefiting from all its features, including xml_ids.
 * Each table import is defined by an SQL statement, used to build the
   equivalent for an import file. Each column's name should match the column
   names you would use in an import file. The first column must provide an
   unique identifier for the record, and will be used to build its xml_id.
 * SQL columns named "none" are ignored. This can be used for the first column
   of the SQL, so that it's used to build the XML Id but it's not imported to
   any OpenERP field.
 * The last sync date is the last successfull execution can be used in the SQL
   using "%(sync)s", or ":sync" in the case of Oracle.
 * When errors are found, only the record with the error fails import. The
   other correct records are commited. However, the "last sync date" will only
   be automaticaly updated when no errors are found.
 * The import execution can be scheduled to run automatically.

Examples:
 * Importing suppliers to res.partner:
      SELECT distinct
            [SUPPLIER_CODE] as "ref"
          , [SUPPLIER_NAME] as "name"
          , 1 as "is_supplier"
          , [INFO] as "comment"
        FROM T_SUPPLIERS
       WHERE INACTIVE_DATE IS NULL and DATE_CHANGED >= %(sync)s

 * Importing products to product.product:
      SELECT PRODUCT_CODE as "ref"
           , PRODUCT_NAME as "name"
           , 'res_partner_id_'+SUPPLIER_ID as "partner_id/id"
        FROM T_PRODUCTS
       WHERE DATE_CHANGED >= %(sync)s

Improvements ideas waiting for a contributor:
 * Allow to import many2one fields (currently not supported). Done by adding a
   second SQL sentence to get child record list?
 * Allow "import sets" that can be executed at different time intervals using
   different scheduler jobs.
 * Allow to inactivate/delete OpenERP records when not present in an SQL
   result set.

Contributors
============

* Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com>
    """,
    'author': "Daniel Reis,Odoo Community Association (OCA)",
    'website': 'http://launchpad.net/addons-tko',
    'license': 'AGPL-3',
    'images': [
        'images/snapshot1.png',
        'images/snapshot2.png',
    ],
    'depends': [
        'base',
        'base_external_dbsource',
    ],
    'data': [
        'import_odbc_view.xml',
        'security/ir.model.access.csv',
    ],
    'demo': [
        'import_odbc_demo.xml',
    ],
    'test': [],
    'installable': True,
    'auto_install': False,
}

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>