<commit_before># vim:nowrap:textwidth=0
#
# Sheared -- non-blocking network programming library for Python
# Copyright (C) 2003  Sune Kirkeby <sune@mel.interspace.dk>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
import stackless, sys, fcntl, socket, os, errno, select, types, traceback, time

from sheared.python import queue
from sheared.reactor import transport

class ReactorFile:
    def __init__(self, fd, reactor):
        self.fd = fd
        self.reactor = reactor
    def fileno(self):
        return self.fd
    def read(self, max=4096):
        return self.reactor.read(self, max)
    def write(self, data):
        self.reactor.write(self, data)
    def seek(self, o, i):
        return os.seek(self.fd, o, i)
    def close(self):
        os.close(self.fd)

class ReactorShutdown(Exception):
    pass

class Reactor:
    def safe_send(self, ch, *what):
        if self.channel_tasklet.has_key(id(ch)):
            apply(ch.send, what)
        else:
            print 'send to dead channel intercepted.'

    def shutdown(self, err=None):
        self.stop = 1
        self.error = err

    def buildselectable(self):
        r, w = [], []
        for fd, (handler, file, channel, argv) in self.waiting.items():
            if handler in [self.handleread, self.handleaccept]:
                r.append(fd)
            elif handler in [self.handlewrite, self.handleconnect]:
                w.append(fd)
            else:
                raise InternalError, ('unknown handler', handler)
        return r, w, r + w

    def flushunselectabels(self, r, w):
        try:
            for fd in r:
                select.select([fd], [], [], 0)
            for fd in w:
                select.select([], [fd], [], 0)
        except select.error, why:
            handler, file, channel, argv = self.waiting[fd]
            del self.waiting[fd]
            self.safe_send(channel, why)

    def handleread(self, file, channel, (max)):
        del self.waiting[file.fileno()]
        self.safe_send(channel, os.read(file.fileno(), max))

    def handlewrite(self, file, channel, argv):
        count = os.write(file.fileno(), argv)
        argv = argv[count :]
        if argv:
            self.waiting[file.fileno()] = self.handlewrite, file, channel, argv
        else:
            del self.waiting[file.fileno()]
            self.safe_send(channel, None)

    def handleaccept(self, file, channel, argv):
        sock, addr = file.accept()
        del self.waiting[file.fileno()]
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        self.safe_send(channel, (sock, addr))

    def handleconnect(self, file, channel, argv):
        del self.waiting[file.fileno()]
        err = file.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
        if err:
            self.safe_send(channel, socket.error(err, os.strerror(err)))
        else:
            self.safe_send(channel, None)

    def wait(self, file, op, argv):
        channel = self.tasklet_channel[id(stackless.getcurrent())]
        self.waiting[file.fileno()] = op, file, channel, argv
        return channel.receive()

    def accept(self, factory, sock):
        try:
            while 1:
                fd, addr = self.wait(sock, self.handleaccept, ())
                t = transport.ReactorTransport(self, fd, addr)
                self.createtasklet(self.startup, (factory, t))

        finally:
            try:
                self.close(sock)
            except:
                traceback.print_exc()

    def startup(self, factory, transport):
        try:
            factory.startup(transport)
        finally:
            transport.close()

    def connectSocket(self, family, type, addr):
        sock = socket.socket(family, type)
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        #if not self.from_address is None:
        #    self.socket.bind(self.from_address)

        try:
            sock.connect(addr)
        except socket.error, (eno, _):
            if not eno == errno.EINPROGRESS:
                raise

        self.wait(sock, self.handleconnect, ())
        return transport.ReactorTransport(self, sock, addr)

    ### public reactor interface ###
    def __init__(self):
        self.tasklet = stackless.tasklet()

        self.tasklet_channel = {}
        self.channel_tasklet = {}

        self.waiting = {}
        self.sleeping = queue.MinQueue()

        self.started = 0
        self.stopped = 0
        self.stop = 0
        self.error = None
        self.exc_info = None

    def wake_sleepers(self):
        now = self.time()
        while (not self.sleeping.empty()) and (self.sleeping.minkey() <= now):
            channel = self.sleeping.getmin()
            self.safe_send(channel, None)

    def start(self):
        self.started = 1
        self.tasklet.become()

        try:
            while not self.stop:
                self.wake_sleepers()
                while stackless.getruncount() > 1:
                    stackless.schedule()
                    self.wake_sleepers()

                r, w, e = self.buildselectable()
                if self.sleeping.empty():
                    if not (r or w or e):
                        break

                if not self.sleeping.empty():
                    now = self.time()
                    timeout = max(0.0, self.sleeping.minkey() - now)
                else:
                    timeout = None

                try:
                    r, w, e = select.select(r, w, e, timeout)
                except select.error, (eno, emsg):
                    self.flushunselectabels(r, w)

                self.wake_sleepers()

                fds = r + w
                for fd in e:
                    if not fd in fds:
                        fds.append(fd)

                for fd in fds:
                    if not self.waiting.has_key(fd):
                        continue
                    handler, file, channel, argv = self.waiting[fd]
                    try:
                        handler(file, channel, argv)
                    except socket.error, (eno, estr):
                        if not eno in (errno.EINTR, errno.EAGAIN):
                            self.safe_send(channel, sys.exc_info()[1])
                    except:
                        self.safe_send(channel, sys.exc_info()[1])

        except:
            self.exc_info = sys.exc_info()
        self.stopped = 1

    def run(self):
        if not stackless.getmain() is stackless.getcurrent():
            raise RuntimeError, 'reactor not started from main tasklet'
        if not self.started:
            self.start()
        if self.stopped:
            raise RuntimeError, 'reactor already stopped'
        stackless.run()

        if self.exc_info:
            raise self.exc_info[0], self.exc_info[1], self.exc_info[2]
        else:
            return self.error

    def createtasklet(self, func, args=(), kwargs={}):
        t = stackless.tasklet()
        c = stackless.channel()
        self.tasklet_channel[id(t)] = c
        self.channel_tasklet[id(c)] = t

        t.become(c)
        try:
            apply(func, args, kwargs)
        except:
            traceback.print_exc()

        del self.channel_tasklet[id(c)]
        del self.tasklet_channel[id(t)]

    def open(self, path, mode):
        f = open(path, mode)
        fcntl.fcntl(f.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        return f

    def prepareFile(self, file):
        if isinstance(file, types.IntType):
            file = ReactorFile(file, self)
        fcntl.fcntl(file.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        return file

    def read(self, file, max=4096):
        return self.wait(file, self.handleread, (max))

    def write(self, file, data):
        return self.wait(file, self.handlewrite, (data))

    def sendfile(self, i, o):
        while 1:
            d = self.read(i, 4096)
            if d == '':
                break
            self.write(o, d)

    def close(self, file):
        file.close()

    def sleep(self, seconds):
        channel = self.tasklet_channel[id(stackless.getcurrent())]
        self.sleeping.insert(self.time() + seconds, channel)
        return channel.receive()

    def schedule(self):
        if self.started and not self.stopped:
            channel = self.tasklet_channel[id(stackless.getcurrent())]
            self.sleeping.insert(0, channel)
            return channel.receive()
        else:
            stackless.schedule()

    def time(self):
        return time.time()

    def connectTCP(self, addr):
        return self.connectSocket(socket.AF_INET, socket.SOCK_STREAM, addr)
    def connectUNIX(self, addr):
        return self.connectSocket(socket.AF_UNIX, socket.SOCK_STREAM, addr)

    def listenTCP(self, factory, addr, backlog=5):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        # we do not want 'address already in use' because of TCP-
        # connections in TIME_WAIT state
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        sock.bind(addr)
        sock.listen(backlog)

        self.createtasklet(self.accept, (factory, sock))

    def listenUNIX(self, factory, addr, backlog=5):
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        # we do not want 'address already in use' because of TCP-
        # connections in TIME_WAIT state; does this call make any
        # sense on UNIX sockets?!
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        sock.bind(addr)
        sock.listen(backlog)

        self.createtasklet(self.accept, (factory, sock))

    def createTransport(self, file, other):
        return transport.ReactorTransport(self, self.prepareFile(file), other)
<commit_msg>Gracefully handle ValueErrors from select.<commit_after># vim:nowrap:textwidth=0
#
# Sheared -- non-blocking network programming library for Python
# Copyright (C) 2003  Sune Kirkeby <sune@mel.interspace.dk>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
import stackless, sys, fcntl, socket, os, errno, select, types, traceback, time

from sheared.python import queue
from sheared.reactor import transport

class ReactorFile:
    def __init__(self, fd, reactor):
        self.fd = fd
        self.reactor = reactor
    def fileno(self):
        return self.fd
    def read(self, max=4096):
        return self.reactor.read(self, max)
    def write(self, data):
        self.reactor.write(self, data)
    def seek(self, o, i):
        return os.seek(self.fd, o, i)
    def close(self):
        os.close(self.fd)

class ReactorShutdown(Exception):
    pass

class Reactor:
    def safe_send(self, ch, *what):
        if self.channel_tasklet.has_key(id(ch)):
            apply(ch.send, what)
        else:
            print 'send to dead channel intercepted.'

    def shutdown(self, err=None):
        self.stop = 1
        self.error = err

    def buildselectable(self):
        r, w = [], []
        for fd, (handler, file, channel, argv) in self.waiting.items():
            if handler in [self.handleread, self.handleaccept]:
                r.append(fd)
            elif handler in [self.handlewrite, self.handleconnect]:
                w.append(fd)
            else:
                raise InternalError, ('unknown handler', handler)
        return r, w, r + w

    def flushunselectabels(self, r, w):
        why = None
        try:
            for fd in r:
                select.select([fd], [], [], 0)
            for fd in w:
                select.select([], [fd], [], 0)
        except select.error, why:
            pass
        except ValueError, why:
            pass

        if why:
            handler, file, channel, argv = self.waiting[fd]
            del self.waiting[fd]
            self.safe_send(channel, why)

    def handleread(self, file, channel, (max)):
        del self.waiting[file.fileno()]
        self.safe_send(channel, os.read(file.fileno(), max))

    def handlewrite(self, file, channel, argv):
        count = os.write(file.fileno(), argv)
        argv = argv[count :]
        if argv:
            self.waiting[file.fileno()] = self.handlewrite, file, channel, argv
        else:
            del self.waiting[file.fileno()]
            self.safe_send(channel, None)

    def handleaccept(self, file, channel, argv):
        sock, addr = file.accept()
        del self.waiting[file.fileno()]
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        self.safe_send(channel, (sock, addr))

    def handleconnect(self, file, channel, argv):
        del self.waiting[file.fileno()]
        err = file.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
        if err:
            self.safe_send(channel, socket.error(err, os.strerror(err)))
        else:
            self.safe_send(channel, None)

    def wait(self, file, op, argv):
        channel = self.tasklet_channel[id(stackless.getcurrent())]
        self.waiting[file.fileno()] = op, file, channel, argv
        return channel.receive()

    def accept(self, factory, sock):
        try:
            while 1:
                fd, addr = self.wait(sock, self.handleaccept, ())
                t = transport.ReactorTransport(self, fd, addr)
                self.createtasklet(self.startup, (factory, t))

        finally:
            try:
                self.close(sock)
            except:
                traceback.print_exc()

    def startup(self, factory, transport):
        try:
            factory.startup(transport)
        finally:
            transport.close()

    def connectSocket(self, family, type, addr):
        sock = socket.socket(family, type)
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        #if not self.from_address is None:
        #    self.socket.bind(self.from_address)

        try:
            sock.connect(addr)
        except socket.error, (eno, _):
            if not eno == errno.EINPROGRESS:
                raise

        self.wait(sock, self.handleconnect, ())
        return transport.ReactorTransport(self, sock, addr)

    ### public reactor interface ###
    def __init__(self):
        self.tasklet = stackless.tasklet()

        self.tasklet_channel = {}
        self.channel_tasklet = {}

        self.waiting = {}
        self.sleeping = queue.MinQueue()

        self.started = 0
        self.stopped = 0
        self.stop = 0
        self.error = None
        self.exc_info = None

    def wake_sleepers(self):
        now = self.time()
        while (not self.sleeping.empty()) and (self.sleeping.minkey() <= now):
            channel = self.sleeping.getmin()
            self.safe_send(channel, None)

    def start(self):
        self.started = 1
        self.tasklet.become()

        try:
            while not self.stop:
                self.wake_sleepers()
                while stackless.getruncount() > 1:
                    stackless.schedule()
                    self.wake_sleepers()

                r, w, e = self.buildselectable()
                if self.sleeping.empty():
                    if not (r or w or e):
                        break

                if not self.sleeping.empty():
                    now = self.time()
                    timeout = max(0.0, self.sleeping.minkey() - now)
                else:
                    timeout = None

                try:
                    r, w, e = select.select(r, w, e, timeout)
                except select.error, (eno, emsg):
                    self.flushunselectabels(r, w)
                except ValueError:
                    self.flushunselectabels(r, w)

                self.wake_sleepers()

                fds = r + w
                for fd in e:
                    if not fd in fds:
                        fds.append(fd)

                for fd in fds:
                    if not self.waiting.has_key(fd):
                        continue
                    handler, file, channel, argv = self.waiting[fd]
                    try:
                        handler(file, channel, argv)
                    except socket.error, (eno, estr):
                        if not eno in (errno.EINTR, errno.EAGAIN):
                            self.safe_send(channel, sys.exc_info()[1])
                    except:
                        self.safe_send(channel, sys.exc_info()[1])

        except:
            self.exc_info = sys.exc_info()
        self.stopped = 1

    def run(self):
        if not stackless.getmain() is stackless.getcurrent():
            raise RuntimeError, 'reactor not started from main tasklet'
        if not self.started:
            self.start()
        if self.stopped:
            raise RuntimeError, 'reactor already stopped'
        stackless.run()

        if self.exc_info:
            raise self.exc_info[0], self.exc_info[1], self.exc_info[2]
        else:
            return self.error

    def createtasklet(self, func, args=(), kwargs={}):
        t = stackless.tasklet()
        c = stackless.channel()
        self.tasklet_channel[id(t)] = c
        self.channel_tasklet[id(c)] = t

        t.become(c)
        try:
            apply(func, args, kwargs)
        except:
            traceback.print_exc()

        del self.channel_tasklet[id(c)]
        del self.tasklet_channel[id(t)]

    def open(self, path, mode):
        f = open(path, mode)
        fcntl.fcntl(f.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        return f

    def prepareFile(self, file):
        if isinstance(file, types.IntType):
            file = ReactorFile(file, self)
        fcntl.fcntl(file.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        return file

    def read(self, file, max=4096):
        return self.wait(file, self.handleread, (max))

    def write(self, file, data):
        return self.wait(file, self.handlewrite, (data))

    def sendfile(self, i, o):
        while 1:
            d = self.read(i, 4096)
            if d == '':
                break
            self.write(o, d)

    def close(self, file):
        file.close()

    def sleep(self, seconds):
        channel = self.tasklet_channel[id(stackless.getcurrent())]
        self.sleeping.insert(self.time() + seconds, channel)
        return channel.receive()

    def schedule(self):
        if self.started and not self.stopped:
            channel = self.tasklet_channel[id(stackless.getcurrent())]
            self.sleeping.insert(0, channel)
            return channel.receive()
        else:
            stackless.schedule()

    def time(self):
        return time.time()

    def connectTCP(self, addr):
        return self.connectSocket(socket.AF_INET, socket.SOCK_STREAM, addr)
    def connectUNIX(self, addr):
        return self.connectSocket(socket.AF_UNIX, socket.SOCK_STREAM, addr)

    def listenTCP(self, factory, addr, backlog=5):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        # we do not want 'address already in use' because of TCP-
        # connections in TIME_WAIT state
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        sock.bind(addr)
        sock.listen(backlog)

        self.createtasklet(self.accept, (factory, sock))

    def listenUNIX(self, factory, addr, backlog=5):
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
        # we do not want 'address already in use' because of TCP-
        # connections in TIME_WAIT state; does this call make any
        # sense on UNIX sockets?!
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        sock.bind(addr)
        sock.listen(backlog)

        self.createtasklet(self.accept, (factory, sock))

    def createTransport(self, file, other):
        return transport.ReactorTransport(self, self.prepareFile(file), other)
<|endoftext|>


<commit_before>from behave import given, then
import re

@given("I visit the home page")
def impl(context):
    context.browser.get('http://localhost:8000/')

@then('I see the call to action "{call_to_action}" on the page')
def find_cta(context, call_to_action):
    element = context.browser.find_element_by_css_selector("body")
    assert call_to_action in element.text, 'Call to action "%s" was not found' % call_to_action

@then('I see the section "{heading_to_look_for}"')
def find_section(context, heading_to_look_for):
    headings = [h.text for h in
                context.browser.find_elements_by_xpath("//h1 | //h2 | //h3")
                if h.text]
    assert heading_to_look_for in headings

@then('I see a button labeled "{btn_label}"')
def find_button(context, btn_label):
    btn_labels = [btn.text for btn in context.browser.find_elements_by_class_name("btn") if btn.text]
    assert btn_label in btn_labels, 'Cannot find a button with the label "%s"' % btn_label

@then('I see some statistics about the Volunteer Planner')
def find_statistics(context):
    facts_div_containers = context.browser.find_elements_by_class_name('facts')
    stats_div = facts_div_containers[0]
    stats_reg_ex = "(^[0-9]+\n[A-Za-z ]+\n)*(^[0-9]+\n[A-Za-z ]+$)\Z"
    assert re.match(
                stats_reg_ex, stats_div.text, re.MULTILINE), "No statistics were found"

@then('I see a list of areas with their respective facilities')
def find_areas_and_facilities(context):
    facts_div_containers = context.browser.find_elements_by_class_name('facts')
    areas_facilities_div = facts_div_containers[1]

    regex_heading = '^.*\n'
    regex_one_area = u'(^[\w ]+\n([\w ]+\u2022)*[\w ]+)'
    regex_total = regex_heading + '(' + regex_one_area + '\n)*' + regex_one_area + '\Z'

    match = re.match(
                regex_total, areas_facilities_div.text, re.MULTILINE | re.UNICODE)
    assert match, "No areas and facilities were found"

@then('I see a navigation bar in the footer')
def find_nav_bar(context):
    navigation_link_labels = [link.text for link in context.browser.find_elements_by_xpath(
        '//div[@id="footer-nav"]//li//a') if link.text]
    assert navigation_link_labels, 'No navigation bar with links was found'
<commit_msg>improved error messages<commit_after>from behave import given, then
import re

@given("I visit the home page")
def impl(context):
    context.browser.get('http://localhost:8000/')

@then('I see the call to action "{call_to_action}" on the page')
def find_cta(context, call_to_action):
    element = context.browser.find_element_by_css_selector("body")
    assert call_to_action in element.text, 'Call to action "%s" was not found' % call_to_action

@then('I see the section "{heading_to_look_for}"')
def find_section(context, heading_to_look_for):
    headings = [h.text for h in
                context.browser.find_elements_by_xpath("//h1 | //h2 | //h3")
                if h.text]
    assert heading_to_look_for in headings

@then('I see a button labeled "{btn_label}"')
def find_button(context, btn_label):
    btn_labels = [btn.text for btn in context.browser.find_elements_by_class_name("btn") if btn.text]
    assert btn_label in btn_labels, 'Cannot find a button with the label "%s"' % btn_label

@then('I see some statistics about the Volunteer Planner')
def find_statistics(context):
    facts_div_containers = context.browser.find_elements_by_class_name('facts')
    stats_div = facts_div_containers[0]
    stats_reg_ex = "(^[0-9]+\n[A-Za-z ]+\n)*(^[0-9]+\n[A-Za-z ]+$)\Z"
    assert re.match(
                stats_reg_ex, stats_div.text, re.MULTILINE), "Statistics could not be found (regex didnt match)"

@then('I see a list of areas with their respective facilities')
def find_areas_and_facilities(context):
    facts_div_containers = context.browser.find_elements_by_class_name('facts')
    areas_facilities_div = facts_div_containers[1]

    regex_heading = '^.*\n'
    regex_one_area = u'(^[\w ]+\n([\w ]+\u2022)*[\w ]+)'
    regex_total = regex_heading + '(' + regex_one_area + '\n)*' + regex_one_area + '\Z'

    match = re.match(
                regex_total, areas_facilities_div.text, re.MULTILINE | re.UNICODE)
    assert match, "Areas and their facilities could not be found (regex didnt match)"

@then('I see a navigation bar in the footer')
def find_nav_bar(context):
    navigation_link_labels = [link.text for link in context.browser.find_elements_by_xpath(
        '//div[@id="footer-nav"]//li//a') if link.text]
    assert navigation_link_labels, 'No navigation bar with links was found'
<|endoftext|>


<commit_before>import uuid

from django.db import models
from django.template.defaultfilters import timesince

class Person(models.Model):

    class Meta:
        verbose_name = 'Persona'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    name = models.TextField(verbose_name='Nombre')
    surname = models.TextField(verbose_name='Apellidos')
    birthday = models.DateField(
        verbose_name='Fecha de nacimiento', blank=True, null=True)
    id_number = models.TextField(
        verbose_name='DNI/NIE', blank=True, default='')
    id_photocopy = models.TextField(
        verbose_name='Fotocopia DNI/NIE', blank=True, default='')
    ss_number = models.TextField(
        verbose_name='Tarjeta sanitaria', blank=True, default='')
    ss_photocopy = models.TextField(
        verbose_name='Fotocopia Seguridad Social', blank=True, default='')
    address_street = models.TextField(
        verbose_name='Dirección', blank=True, default='')
    address_locality = models.TextField(
        verbose_name='Localidad', blank=True, default='')
    address_region = models.TextField(
        verbose_name='Provincia', blank=True, default='')
    address_country = models.TextField(
        verbose_name='País', blank=True, default='')
    phone_number = models.TextField(
        verbose_name='Teléfono', blank=True, default='')
    mobile_number = models.TextField(
        verbose_name='Teléfono móvil', blank=True, default='')
    email = models.EmailField(
        verbose_name='Correo electrónico', blank=True, default='')
    dpa_authorization = models.TextField(verbose_name='LOPD', blank=True, default='')
    comment = models.TextField(
        verbose_name='Observaciones', blank=True, default='')
    health_warnings = models.TextField(
        verbose_name='Observaciones médicas', blank=True, default='')

    @property
    def age(self):
        return timesince(self.birthday) if self.birthday else None

    def __str__(self):
        return '{} {}'.format(self.name.capitalize(), self.surname.upper())

class Recipient(models.Model):
    """"Destinatario de las actividades de la asociación.

    Jóvenes asignados a un grupo dentro de un proyecto (destinatario). Pueden
    ser infantiles (6 a 14 años) o juveniles (14 a 29 años).
    """
    class Meta:
        verbose_name = 'Destinatario'

    CATEGORIES = [
        ('child', 'Infantil'),
        ('juvenile', 'Juvenil'),
    ]

    category = models.CharField('Tipo', choices=CATEGORIES, max_length=32)
    person = models.ForeignKey(Person)

    def __str__(self):
        return '{}'.format(self.id)

class Volunteer(models.Model):
    """Voluntarios, en general animadores con una vinculación con cierto plazo."""
    class Meta:
        verbose_name = 'Voluntario'
    lack_of_sexual_offenses_date_certificate = models.DateField(
        verbose_name='Fecha de emisión del Certificado de Delitos de Naturaleza Sexual',
        null=True,
        default=None,
    )
    person = models.ForeignKey(Person)

    def __str__(self):
        return '{} ({})'.format(self.id, self.person)

class Custodian(models.Model):
    """Padre, madre o tutor legal de un menor."""
    class Meta:
        verbose_name = 'Padre, madre, tutor'
    authorized_signature = models.TextField(verbose_name='Firma autorización', blank=True, default='')
    emergency_contact = models.TextField(verbose_name='Contacto de emergencia', blank=True, default='')
    CATEGORIES = [
        ('father', 'Padre'),
        ('mother', 'Madre'),
        ('legal', 'Tutor'),
    ]

    category = models.CharField('Tipo', choices=CATEGORIES, max_length=32)
    person = models.ForeignKey(Person)
    minor = models.ForeignKey(Recipient)

    def __str__(self):
        return '{}'.format(self.id)

class Group(models.Model):

    class Meta:
        verbose_name = 'Grupo'

    group_name = models.TextField(verbose_name='Nombre grupo')

    def __str__(self):
        return self.group_name

class Enrolment(models.Model):

    class Meta:
        verbose_name = 'Inscripción'
        verbose_name_plural = 'Inscripciones'
        unique_together = (('person', 'group'),)

    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)

    created = models.DateTimeField(
        auto_now_add=True, verbose_name='Fecha de inscripción')

class Membership(models.Model):
    """Membresía."""
    class Meta:
        verbose_name = 'Membresía'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    type_of_membership = models.TextField(verbose_name='Modalidad', blank=True, default='')

    payment_status = models.TextField(verbose_name='Estado del pago', blank=True, default='')

    membership_fee = models.DecimalField(
        verbose_name='Cuota de membresia',
        decimal_places=2,
        max_digits=6,
    )

    membership_status = models.TextField(verbose_name='Estado de socio', blank=True, default='')

class Member(models.Model):
    """Socio."""
    class Meta:
        verbose_name = 'Socio'

    CATEGORY = [
        ('child', 'Infantil'),
        ('juvenile', 'Juvenil'),
        ('volunteer', 'Voluntario'),
        ('contributor', 'Colaborador'),
        ('family', 'Familiar'),
    ]
    category = models.CharField('Tipo de socio', choices=CATEGORY, max_length=32)

    # Documentation
    id_card_status = models.TextField(verbose_name='DNI/NIE', blank=True, default='')
    ss_card_status = models.TextField(verbose_name='Tarjeta sanitaria', blank=True, default='')
    photo_status = models.TextField(verbose_name='Foto', blank=True, default='')
    dpa_status = models.TextField(verbose_name='LOPD', blank=True, default='')
    card_status = models.TextField(verbose_name='Estado del carnet', blank=True, default='')

    photo = models.ImageField(verbose_name='Fotografía', upload_to='media', blank=True, null=True)

    person = models.ForeignKey(Person)
    membership = models.ForeignKey(Membership)

    @property
    def documentation_correct(self):
        return 'no' in [self.id_card_status, self.ss_card_status, self.photo_status, self.dpa_status]

    def __str__(self):
        return '{}'.format(self.id)
<commit_msg>Modifica atributo por defecto de Member.photo<commit_after>import uuid

from django.db import models
from django.template.defaultfilters import timesince

class Person(models.Model):

    class Meta:
        verbose_name = 'Persona'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    name = models.TextField(verbose_name='Nombre')
    surname = models.TextField(verbose_name='Apellidos')
    birthday = models.DateField(
        verbose_name='Fecha de nacimiento', blank=True, null=True)
    id_number = models.TextField(
        verbose_name='DNI/NIE', blank=True, default='')
    id_photocopy = models.TextField(
        verbose_name='Fotocopia DNI/NIE', blank=True, default='')
    ss_number = models.TextField(
        verbose_name='Tarjeta sanitaria', blank=True, default='')
    ss_photocopy = models.TextField(
        verbose_name='Fotocopia Seguridad Social', blank=True, default='')
    address_street = models.TextField(
        verbose_name='Dirección', blank=True, default='')
    address_locality = models.TextField(
        verbose_name='Localidad', blank=True, default='')
    address_region = models.TextField(
        verbose_name='Provincia', blank=True, default='')
    address_country = models.TextField(
        verbose_name='País', blank=True, default='')
    phone_number = models.TextField(
        verbose_name='Teléfono', blank=True, default='')
    mobile_number = models.TextField(
        verbose_name='Teléfono móvil', blank=True, default='')
    email = models.EmailField(
        verbose_name='Correo electrónico', blank=True, default='')
    dpa_authorization = models.TextField(verbose_name='LOPD', blank=True, default='')
    comment = models.TextField(
        verbose_name='Observaciones', blank=True, default='')
    health_warnings = models.TextField(
        verbose_name='Observaciones médicas', blank=True, default='')

    @property
    def age(self):
        return timesince(self.birthday) if self.birthday else None

    def __str__(self):
        return '{} {}'.format(self.name.capitalize(), self.surname.upper())

class Recipient(models.Model):
    """"Destinatario de las actividades de la asociación.

    Jóvenes asignados a un grupo dentro de un proyecto (destinatario). Pueden
    ser infantiles (6 a 14 años) o juveniles (14 a 29 años).
    """
    class Meta:
        verbose_name = 'Destinatario'

    CATEGORIES = [
        ('child', 'Infantil'),
        ('juvenile', 'Juvenil'),
    ]

    category = models.CharField('Tipo', choices=CATEGORIES, max_length=32)
    person = models.ForeignKey(Person)

    def __str__(self):
        return '{}'.format(self.id)

class Volunteer(models.Model):
    """Voluntarios, en general animadores con una vinculación con cierto plazo."""
    class Meta:
        verbose_name = 'Voluntario'
    lack_of_sexual_offenses_date_certificate = models.DateField(
        verbose_name='Fecha de emisión del Certificado de Delitos de Naturaleza Sexual',
        null=True,
        default=None,
    )
    person = models.ForeignKey(Person)

    def __str__(self):
        return '{} ({})'.format(self.id, self.person)

class Custodian(models.Model):
    """Padre, madre o tutor legal de un menor."""
    class Meta:
        verbose_name = 'Padre, madre, tutor'
    authorized_signature = models.TextField(verbose_name='Firma autorización', blank=True, default='')
    emergency_contact = models.TextField(verbose_name='Contacto de emergencia', blank=True, default='')
    CATEGORIES = [
        ('father', 'Padre'),
        ('mother', 'Madre'),
        ('legal', 'Tutor'),
    ]

    category = models.CharField('Tipo', choices=CATEGORIES, max_length=32)
    person = models.ForeignKey(Person)
    minor = models.ForeignKey(Recipient)

    def __str__(self):
        return '{}'.format(self.id)

class Group(models.Model):

    class Meta:
        verbose_name = 'Grupo'

    group_name = models.TextField(verbose_name='Nombre grupo')

    def __str__(self):
        return self.group_name

class Enrolment(models.Model):

    class Meta:
        verbose_name = 'Inscripción'
        verbose_name_plural = 'Inscripciones'
        unique_together = (('person', 'group'),)

    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)

    created = models.DateTimeField(
        auto_now_add=True, verbose_name='Fecha de inscripción')

class Membership(models.Model):
    """Membresía."""
    class Meta:
        verbose_name = 'Membresía'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    type_of_membership = models.TextField(verbose_name='Modalidad', blank=True, default='')

    payment_status = models.TextField(verbose_name='Estado del pago', blank=True, default='')

    membership_fee = models.DecimalField(
        verbose_name='Cuota de membresia',
        decimal_places=2,
        max_digits=6,
    )

    membership_status = models.TextField(verbose_name='Estado de socio', blank=True, default='')

class Member(models.Model):
    """Socio."""
    class Meta:
        verbose_name = 'Socio'

    CATEGORY = [
        ('child', 'Infantil'),
        ('juvenile', 'Juvenil'),
        ('volunteer', 'Voluntario'),
        ('contributor', 'Colaborador'),
        ('family', 'Familiar'),
    ]
    category = models.CharField('Tipo de socio', choices=CATEGORY, max_length=32)

    # Documentation
    id_card_status = models.TextField(verbose_name='DNI/NIE', blank=True, default='')
    ss_card_status = models.TextField(verbose_name='Tarjeta sanitaria', blank=True, default='')
    photo_status = models.TextField(verbose_name='Foto', blank=True, default='')
    dpa_status = models.TextField(verbose_name='LOPD', blank=True, default='')
    card_status = models.TextField(verbose_name='Estado del carnet', blank=True, default='')

    photo = models.ImageField(verbose_name='Fotografía', upload_to='members', default=None)

    person = models.ForeignKey(Person)
    membership = models.ForeignKey(Membership)

    @property
    def documentation_correct(self):
        return 'no' in [self.id_card_status, self.ss_card_status, self.photo_status, self.dpa_status]

    def __str__(self):
        return '{}'.format(self.id)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import pytest
import copy
import pymongo
import logging
import datetime
from pprint import pprint as pp

from scout.constants import INDEXES
from scout.exceptions import (IntegrityError)

logger = logging.getLogger(__name__)

def test_add_cases(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.case_collection.find().count() == 0

    # WHEN adding a new case to the database
    adapter._add_case(case_obj)

    # THEN it should be populated with the new case
    result = adapter.cases()
    assert result.count() == 1
    for case in result:
        assert case['owner'] == case_obj['owner']

    logger.info("All cases checked")

def test_add_existing_case(adapter,case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    adapter._add_case(case_obj)
    # WHEN adding a existing case to the database
    with pytest.raises(IntegrityError):
        # THEN it should raise integrity error
        adapter._add_case(case_obj)

def test_get_case(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    logger.info("Testing to get case")

    # WHEN retreiving an existing case from the database
    result = adapter.case(case_id=case_obj['_id'])
    # THEN we should get the correct case
    assert result['owner'] == case_obj['owner']

def test_get_cases(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving an existing case from the database
    result = adapter.cases()
    # THEN we should get the correct case
    assert result.count() == 1

def test_search_active_case(real_adapter, case_obj, institute_obj, user_obj):
    adapter = real_adapter

    # GIVEN a real database with no cases
    assert real_adapter.cases().count() == 0

    # Insert a case
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # WHEN flagging the case as active
    adapter.update_status(institute_obj, case_obj, user_obj, 'active', 'blank')

    # WHEN querying for active cases,
    name_query='status:active'
    # THEN a case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 1

    # BUT WHEN querying for inactive cases
    name_query='status:inactive'
    # THEN no case should be returned.
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 0

def test_get_research_case(real_adapter, case_obj, institute_obj):
    adapter = real_adapter

    # GIVEN a real database with no cases
    assert real_adapter.cases().count() == 0

    # WHEN flagging case_obj as research
    case_obj['is_research'] = True

    # AND WHEN inserting such case
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # THEN searching for reasearch cases should return one case
    research_cases = list(adapter.cases(owner=case_obj['owner'], is_research=True))
    assert len(research_cases) == 1

def test_get_cases_no_synopsis(real_adapter, case_obj, institute_obj, user_obj):

    adapter = real_adapter
    # GIVEN a real database with no cases
    assert real_adapter.cases().count() == 0

    # Insert a case
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # WHEN providing an empty value for synopsis:
    assert case_obj['synopsis'] == ''
    name_query='synopsis:'
    # Then case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 1

    # After adding synopsis to case
    link = 'synopsislink'
    synopsis = "Recurrent seizures"
    updated_case = adapter.update_synopsis(
        institute=institute_obj,
        case=case_obj,
        user=user_obj,
        link=link,
        content=synopsis
    )

    # WHEN providing an empty value for synopsis:
    assert case_obj['synopsis'] == ''
    name_query='synopsis:'
    # Then case should NOT be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 0

    # but if a term contained in case synopsis is provided in name query:
    name_query='synopsis:seizures'

    # Then updated case should be returned
    cases = list(adapter.cases(collaborator=updated_case['owner'], name_query=name_query))
    assert len(cases) == 1

def test_get_cases_no_HPO(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)

    # WHEN providing an empty value for term HP:
    name_query='HP:'
    # Then case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == [case_obj]

    # WHEN providing an empty value for phenotype group:
    name_query='PG:'
    # Then case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == [case_obj]

    # Add phenotype group and HPO term to case object:
    adapter.case_collection.find_one_and_update({
        '_id' : case_obj['_id']
        },
        {
            '$set' : {
                'phenotype_groups': [{'phenotype_id' : 'test_pg'}],
                'phenotype_terms' : [{'phenotype_id' : 'test_hp'}],
            }
        }
    )
    # WHEN providing an empty value for term HP:
    name_query='HP:'
    # Then case should NOT be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == []

    # WHEN providing an empty value for phenotype group:
    name_query='PG:'
    # Then case should NOT be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == []

def test_get_cases_no_assignees(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving an existing case from the database
    result = adapter.cases(name_query='john')
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_cases_display_name(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)

    other_case = case_obj
    other_case['_id'] = 'other_case'
    other_case['display_name'] = 'other_case'
    adapter.case_collection.insert_one(other_case)

    # WHEN retreiving cases by partial display name
    result = adapter.cases(name_query='643')
    # THEN we should get the correct case
    assert result.count() == 1

def test_get_cases_existing_individual(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving cases by partial individual name
    result = adapter.cases(name_query='NA1288')
    # THEN we should get the correct case
    assert result.count() == 1

def test_get_cases_assignees(real_adapter, case_obj, user_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    adapter.user_collection.insert_one(user_obj)

    user_obj = adapter.user_collection.find_one()
    case_obj['assignees'] = [user_obj['email']]
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving cases by partial individual name
    result = adapter.cases(name_query='john')
    # THEN we should get the correct case
    assert result.count() == 1

def test_get_cases_non_existing_assignee(real_adapter, case_obj, user_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    adapter.user_collection.insert_one(user_obj)

    user_obj = adapter.user_collection.find_one()
    case_obj['assignees'] = [user_obj['email']]
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving cases by partial individual name
    result = adapter.cases(name_query='damien')
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_cases_causatives(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    # Add a causative
    case_obj['causatives'] = ['a variant']
    # Insert the case
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving cases that have causatives
    result = adapter.cases(has_causatives=True)
    # THEN we should find one case
    assert result.count() == 1

def test_get_cases_causatives_no_causatives(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    # Insert a case without causatives
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving all cases that have causatives
    result = adapter.cases(has_causatives=True)
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_cases_empty_causatives(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    # Add a empty list as causatives
    case_obj['causatives'] = []
    # Insert the case
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving all cases that have causatives
    result = adapter.cases(has_causatives=True)
    # THEN we should not find any cases
    assert result.count() == 0

def test_get_cases_non_existing_individual(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving cases by partial display name
    result = adapter.cases(name_query='hello')
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_non_existing_case(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter._add_case(case_obj)
    logger.info("Testing to get case")

    # WHEN retreiving an non existing case from the database
    result = adapter.case(case_id='hello')
    # THEN we should get None back
    assert result is None

def test_delete_case(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    logger.info("Testing to delete case")

    # WHEN deleting a case from the database
    result = adapter.delete_case(case_id=case_obj['_id'])
    # THEN there should be no cases left in the database
    assert adapter.cases().count() == 0

def test_update_case_collaborators(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    assert len(adapter.case(case_obj['_id'])['collaborators']) == 1
    logger.info("Testing to update case")

    coll_1 = case_obj['collaborators'][0]
    coll_2 = 'test'
    coll_3 = 'test2'
    case_obj['collaborators'].append(coll_2)
    case_obj['collaborators'].append(coll_3)

    # WHEN updating a case with new collaborators
    res = adapter.update_case(case_obj)

    # THEN assert collaborator has been added
    assert len(res['collaborators']) == 3
    # THEN assert all collaborators where added
    assert set(res['collaborators']) == set([coll_1, coll_2, coll_3])

def test_update_case_individuals(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    logger.info("Testing to update case")

    res = adapter.case(case_obj['_id'])
    assert len(res['individuals']) == 3

    new_individuals = [{
        'individual_id': 'test',
        'display_name': 'test_name',
    }]
    case_obj['individuals'] = new_individuals
    # WHEN updating a case with new individuals
    res = adapter.update_case(case_obj)
    # THEN assert that 'individuals' has changed

    assert len(res['individuals']) == 1

def test_update_case_rerun_status(adapter, case_obj):
    case_obj['rerun_requested'] = True
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    logger.info("Testing to update case")

    res = adapter.case(case_obj['_id'])
    assert res['rerun_requested'] is True

    # WHEN updating a case
    res = adapter.update_case(case_obj)

    # THEN assert that 'rerun_requested' is set to False
    assert res['rerun_requested'] is False

def test_get_similar_cases(hpo_database, test_hpo_terms, case_obj):
    adapter = hpo_database

    # load test HPO files to database
    assert adapter.hpo_terms().count()

    # update test case using test HPO terms
    case_obj['phenotype_terms'] = test_hpo_terms
    adapter.case_collection.find_one_and_update(
        { '_id' : case_obj['_id'] },
        { '$set' : {'phenotype_terms' : test_hpo_terms }},
        return_document=pymongo.ReturnDocument.AFTER)
    # insert case into database
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # Add another case with slightly different phenotype
    case_2 = copy.copy(case_obj)
    case_2['_id']='case_2'
    case_2['phenotype_terms'] = test_hpo_terms[:-1] # exclude last term

    # insert this case in database:
    adapter.case_collection.insert_one(case_2)
    assert adapter.case_collection.find().count() == 2

    # Add another case with phenotype very different from case_obj
    case_3 = copy.copy(case_obj)
    case_3['_id']='case_3'
    case_3['phenotype_terms'] = [{'phenotype_id':'HP:0000533',
        'feature':'Recurrent skin infections'}]

    # insert this case in database:
    adapter.case_collection.insert_one(case_3)
    assert adapter.case_collection.find().count() == 3

    similar_cases = adapter.get_similar_cases(case_obj)
    # make sure that the function returns a list of tuples
    assert isinstance(similar_cases, list)
    assert isinstance(similar_cases[0], tuple)

    # and the first element of the list has a score higher or equal than the second
    assert similar_cases[0][1] >= similar_cases[1][1]
<commit_msg>most similar patient should actually have a higher score<commit_after># -*- coding: utf-8 -*-
import pytest
import copy
import pymongo
import logging
import datetime
from pprint import pprint as pp

from scout.constants import INDEXES
from scout.exceptions import (IntegrityError)

logger = logging.getLogger(__name__)

def test_add_cases(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.case_collection.find().count() == 0

    # WHEN adding a new case to the database
    adapter._add_case(case_obj)

    # THEN it should be populated with the new case
    result = adapter.cases()
    assert result.count() == 1
    for case in result:
        assert case['owner'] == case_obj['owner']

    logger.info("All cases checked")

def test_add_existing_case(adapter,case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    adapter._add_case(case_obj)
    # WHEN adding a existing case to the database
    with pytest.raises(IntegrityError):
        # THEN it should raise integrity error
        adapter._add_case(case_obj)

def test_get_case(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    logger.info("Testing to get case")

    # WHEN retreiving an existing case from the database
    result = adapter.case(case_id=case_obj['_id'])
    # THEN we should get the correct case
    assert result['owner'] == case_obj['owner']

def test_get_cases(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving an existing case from the database
    result = adapter.cases()
    # THEN we should get the correct case
    assert result.count() == 1

def test_search_active_case(real_adapter, case_obj, institute_obj, user_obj):
    adapter = real_adapter

    # GIVEN a real database with no cases
    assert real_adapter.cases().count() == 0

    # Insert a case
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # WHEN flagging the case as active
    adapter.update_status(institute_obj, case_obj, user_obj, 'active', 'blank')

    # WHEN querying for active cases,
    name_query='status:active'
    # THEN a case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 1

    # BUT WHEN querying for inactive cases
    name_query='status:inactive'
    # THEN no case should be returned.
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 0

def test_get_research_case(real_adapter, case_obj, institute_obj):
    adapter = real_adapter

    # GIVEN a real database with no cases
    assert real_adapter.cases().count() == 0

    # WHEN flagging case_obj as research
    case_obj['is_research'] = True

    # AND WHEN inserting such case
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # THEN searching for reasearch cases should return one case
    research_cases = list(adapter.cases(owner=case_obj['owner'], is_research=True))
    assert len(research_cases) == 1

def test_get_cases_no_synopsis(real_adapter, case_obj, institute_obj, user_obj):

    adapter = real_adapter
    # GIVEN a real database with no cases
    assert real_adapter.cases().count() == 0

    # Insert a case
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # WHEN providing an empty value for synopsis:
    assert case_obj['synopsis'] == ''
    name_query='synopsis:'
    # Then case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 1

    # After adding synopsis to case
    link = 'synopsislink'
    synopsis = "Recurrent seizures"
    updated_case = adapter.update_synopsis(
        institute=institute_obj,
        case=case_obj,
        user=user_obj,
        link=link,
        content=synopsis
    )

    # WHEN providing an empty value for synopsis:
    assert case_obj['synopsis'] == ''
    name_query='synopsis:'
    # Then case should NOT be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert len(cases) == 0

    # but if a term contained in case synopsis is provided in name query:
    name_query='synopsis:seizures'

    # Then updated case should be returned
    cases = list(adapter.cases(collaborator=updated_case['owner'], name_query=name_query))
    assert len(cases) == 1

def test_get_cases_no_HPO(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)

    # WHEN providing an empty value for term HP:
    name_query='HP:'
    # Then case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == [case_obj]

    # WHEN providing an empty value for phenotype group:
    name_query='PG:'
    # Then case should be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == [case_obj]

    # Add phenotype group and HPO term to case object:
    adapter.case_collection.find_one_and_update({
        '_id' : case_obj['_id']
        },
        {
            '$set' : {
                'phenotype_groups': [{'phenotype_id' : 'test_pg'}],
                'phenotype_terms' : [{'phenotype_id' : 'test_hp'}],
            }
        }
    )
    # WHEN providing an empty value for term HP:
    name_query='HP:'
    # Then case should NOT be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == []

    # WHEN providing an empty value for phenotype group:
    name_query='PG:'
    # Then case should NOT be returned
    cases = list(adapter.cases(collaborator=case_obj['owner'], name_query=name_query))
    assert cases == []

def test_get_cases_no_assignees(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving an existing case from the database
    result = adapter.cases(name_query='john')
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_cases_display_name(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)

    other_case = case_obj
    other_case['_id'] = 'other_case'
    other_case['display_name'] = 'other_case'
    adapter.case_collection.insert_one(other_case)

    # WHEN retreiving cases by partial display name
    result = adapter.cases(name_query='643')
    # THEN we should get the correct case
    assert result.count() == 1

def test_get_cases_existing_individual(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving cases by partial individual name
    result = adapter.cases(name_query='NA1288')
    # THEN we should get the correct case
    assert result.count() == 1

def test_get_cases_assignees(real_adapter, case_obj, user_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    adapter.user_collection.insert_one(user_obj)

    user_obj = adapter.user_collection.find_one()
    case_obj['assignees'] = [user_obj['email']]
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving cases by partial individual name
    result = adapter.cases(name_query='john')
    # THEN we should get the correct case
    assert result.count() == 1

def test_get_cases_non_existing_assignee(real_adapter, case_obj, user_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    adapter.user_collection.insert_one(user_obj)

    user_obj = adapter.user_collection.find_one()
    case_obj['assignees'] = [user_obj['email']]
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving cases by partial individual name
    result = adapter.cases(name_query='damien')
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_cases_causatives(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    # Add a causative
    case_obj['causatives'] = ['a variant']
    # Insert the case
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving cases that have causatives
    result = adapter.cases(has_causatives=True)
    # THEN we should find one case
    assert result.count() == 1

def test_get_cases_causatives_no_causatives(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    # Insert a case without causatives
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving all cases that have causatives
    result = adapter.cases(has_causatives=True)
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_cases_empty_causatives(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0

    # Add a empty list as causatives
    case_obj['causatives'] = []
    # Insert the case
    adapter.case_collection.insert_one(case_obj)

    # WHEN retreiving all cases that have causatives
    result = adapter.cases(has_causatives=True)
    # THEN we should not find any cases
    assert result.count() == 0

def test_get_cases_non_existing_individual(real_adapter, case_obj):
    adapter = real_adapter
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    # WHEN retreiving cases by partial display name
    result = adapter.cases(name_query='hello')
    # THEN we should get the correct case
    assert result.count() == 0

def test_get_non_existing_case(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter._add_case(case_obj)
    logger.info("Testing to get case")

    # WHEN retreiving an non existing case from the database
    result = adapter.case(case_id='hello')
    # THEN we should get None back
    assert result is None

def test_delete_case(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    logger.info("Testing to delete case")

    # WHEN deleting a case from the database
    result = adapter.delete_case(case_id=case_obj['_id'])
    # THEN there should be no cases left in the database
    assert adapter.cases().count() == 0

def test_update_case_collaborators(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    assert len(adapter.case(case_obj['_id'])['collaborators']) == 1
    logger.info("Testing to update case")

    coll_1 = case_obj['collaborators'][0]
    coll_2 = 'test'
    coll_3 = 'test2'
    case_obj['collaborators'].append(coll_2)
    case_obj['collaborators'].append(coll_3)

    # WHEN updating a case with new collaborators
    res = adapter.update_case(case_obj)

    # THEN assert collaborator has been added
    assert len(res['collaborators']) == 3
    # THEN assert all collaborators where added
    assert set(res['collaborators']) == set([coll_1, coll_2, coll_3])

def test_update_case_individuals(adapter, case_obj):
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    logger.info("Testing to update case")

    res = adapter.case(case_obj['_id'])
    assert len(res['individuals']) == 3

    new_individuals = [{
        'individual_id': 'test',
        'display_name': 'test_name',
    }]
    case_obj['individuals'] = new_individuals
    # WHEN updating a case with new individuals
    res = adapter.update_case(case_obj)
    # THEN assert that 'individuals' has changed

    assert len(res['individuals']) == 1

def test_update_case_rerun_status(adapter, case_obj):
    case_obj['rerun_requested'] = True
    # GIVEN an empty database (no cases)
    assert adapter.cases().count() == 0
    adapter.case_collection.insert_one(case_obj)
    assert adapter.cases().count() == 1
    logger.info("Testing to update case")

    res = adapter.case(case_obj['_id'])
    assert res['rerun_requested'] is True

    # WHEN updating a case
    res = adapter.update_case(case_obj)

    # THEN assert that 'rerun_requested' is set to False
    assert res['rerun_requested'] is False

def test_get_similar_cases(hpo_database, test_hpo_terms, case_obj):
    adapter = hpo_database

    # load test HPO files to database
    assert adapter.hpo_terms().count()

    # update test case using test HPO terms
    case_obj['phenotype_terms'] = test_hpo_terms
    adapter.case_collection.find_one_and_update(
        { '_id' : case_obj['_id'] },
        { '$set' : {'phenotype_terms' : test_hpo_terms }},
        return_document=pymongo.ReturnDocument.AFTER)
    # insert case into database
    adapter.case_collection.insert_one(case_obj)
    assert adapter.case_collection.find().count() == 1

    # Add another case with slightly different phenotype
    case_2 = copy.copy(case_obj)
    case_2['_id']='case_2'
    case_2['phenotype_terms'] = test_hpo_terms[:-1] # exclude last term

    # insert this case in database:
    adapter.case_collection.insert_one(case_2)
    assert adapter.case_collection.find().count() == 2

    # Add another case with phenotype very different from case_obj
    case_3 = copy.copy(case_obj)
    case_3['_id']='case_3'
    case_3['phenotype_terms'] = [{'phenotype_id':'HP:0000533',
        'feature':'Recurrent skin infections'}]

    # insert this case in database:
    adapter.case_collection.insert_one(case_3)
    assert adapter.case_collection.find().count() == 3

    similar_cases = adapter.get_similar_cases(case_obj)
    # make sure that the function returns a list of tuples
    assert isinstance(similar_cases, list)
    assert isinstance(similar_cases[0], tuple)

    # and the first element of the list has a score higher or equal than the second
    assert similar_cases[0][1] > similar_cases[1][1]
<|endoftext|>


<commit_before>import itertools
import logging

class Encoder:

    CHECK_SAT = "(check-sat)\n"
    GET_MODEL = "(get-model)\n"
    EXIT_CALL = "(exit)\n"

    def __init__ (self, uct, inputs, outputs):
        self.uct=uct
        self.inputs=inputs
        self.outputs=outputs
        self.upsilon = Upsilon(self.inputs)
        self._logger = logging.getLogger(__name__)

    def _func(self, function, args):

        smt_str = '(' + function + ' '
        for arg in args:
            smt_str += arg+' '
        if len(args):
            smt_str = smt_str[:-1]
        smt_str += ')'
        return smt_str

    def _assert(self, formula):
        smt_str = '(assert ' + formula + ')\n'
        return smt_str

    def _comment(self, comment):
        smt_str = '; ' + comment + '\n'
        return smt_str

    def _declare_fun(self, name, vars, type):
        smt_str = '(declare-fun '
        smt_str += name + ' ('

        for var in vars:
            smt_str += var + ' '
        if len(vars):
            smt_str = smt_str[:-1]

        smt_str += ') ' + type + ')\n'
        return smt_str

    def _declare_sort(self, name, num_param):
        smt_str = '(declare-sort '
        smt_str += name + ' ' + str(num_param) + ')\n'
        return smt_str

    def _implies(self, arg1, arg2):
        smt_str = '(=> ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _eq(self, arg1, arg2): #is equal to
        smt_str = '(= ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _gt(self, arg1, arg2): #is greater than
        smt_str = '(> ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _ge(self, arg1, arg2): #is greater than or equal to
        smt_str = '(>= ' + arg1 + ' '+ arg2 + ')'
        return smt_str

    def _lt(self, arg1, arg2): #is less than
        smt_str = '(< ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _le(self, arg1, arg2):#is less than or equal to
        smt_str = '(<= ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _not(self, argument):
        smt_str = '(not ' + argument + ')'
        return smt_str

    def _and(self, arguments):
        smt_str = '(and '
        smt_str += self.make_and_or_xor_body(arguments) + ')'
        return smt_str

    def _or(self, arguments):
        smt_str = '(or '
        smt_str += self.make_and_or_xor_body(arguments) + ')'
        return smt_str

    def _xor(self, arguments):
        smt_str = '(xor '
        smt_str += self.make_and_or_xor_body(arguments) + ')'
        return smt_str

    def make_and_or_xor_body(self, arguments):
        smt_str=''
        for arg in arguments:
            smt_str += arg + ' '
        if len(arguments):
            smt_str = smt_str[:-1]
        return smt_str

    def _forall(self, param_list, expression):
        smt_str = '(forall ('
        for pairs in param_list:             #name-sort pair
            smt_str += '('+ pairs[0] + ' ' + pairs[1] + ')'
        smt_str += ')' + expression + ')'
        return smt_str

    def _make_state_declarations(self, state_names, sort_name):
        lowername = sort_name.lower()

        smt_str = '(declare-datatypes () (({0} {1})))\n'.format(sort_name, ' '.join([lowername + '_' + x for x in state_names]))
        return smt_str

#        smt_str = self._comment("define a new type " + sort_name + ":")
#        smt_str += self._declare_sort(sort_name,0)
#        smt_str += self._comment("constants of type " + sort_name + ":")
#
#        for name in state_names:
#            smt_str += self._declare_fun(lowername + "_" + name, [], sort_name)
#
#        smt_str += self._comment("cardinality constraints:")
#
#        expList = []
#        for name in state_names:
#            expList.append(self._eq(lowername,lowername + "_" + name))
#        expression =  " " + self._or(expList)
#
#        smt_str += self._assert(self._forall([[lowername,sort_name]], expression))
#        smt_str += '\n'
#
#        return smt_str

    def _make_input_declarations(self):

        smt_str = self._comment("define a new type Upsilon for the input letters:")
        smt_str += self._declare_sort("Upsilon", 0)
        smt_str += self._comment("constants of type Upsilon:")

        for input_value in range (0, self.upsilon.get_num_element()):
            smt_str += self._declare_fun(self.upsilon.get_element_str(input_value),[],"Upsilon")

        smt_str +='\n'
        return smt_str

    def _make_other_declarations(self):

        smt_str = self._comment("Declarations for transition relation, output function and annotation")
        smt_str += self._declare_fun("tau", ["T","Upsilon"], "T")

        #declare output functions
        for input in self.inputs:
            smt_str += self._declare_fun("fo_"+input,["T"],"Bool")
        for output in self.outputs:
            smt_str += self._declare_fun("fo_"+output,["T"],"Bool")

        #declare annotations
        smt_str += self._declare_fun("lambda_B", ["Q","T"], "Bool")
        smt_str += self._declare_fun("lambda_sharp", ["Q","T"], "Int")
        smt_str += '\n'

        return smt_str

    def _make_root_condition(self):

        smt_str = self._comment("the root node of the run graph is labelled by a natural number:")

        elements = []
        for state in self.uct.initial_nodes:
            elements.append(self._func("lambda_B", ["q_"+state.name,"t_0"]))

        if len(elements)>1:
            smt_str += self._assert(self._and(elements))
        else:
            smt_str += self._assert(elements[0])

        return smt_str

    def _make_input_preservation(self, num_impl_states):
        smt_str = self._comment("input preserving:")
        elements = []
        for input in self.inputs:
            for input_value in range(0, self.upsilon.get_num_element()):
                for impl_state in range(0, num_impl_states):
                    element = self._func("fo_" + input, [self._func("tau", ["t_" + str(impl_state), self.upsilon.get_element_str(input_value)])])

                    if self.upsilon.is_input_set(input_value, input) is False:
                        element = self._not(element)
                    elements.append(element)

        smt_str += self._assert(self._and(elements))

        return smt_str

    def _make_trans_condition(self, labels, impl_state):
        smt_str=''
        input_output_list = self.inputs + self.outputs
        or_args = []
        and_args = []
        for var in input_output_list:  #for all inputs and outputs
            if (var in labels) and (labels[var] is True):
                and_args.append(self._func("fo_" + var, ["t_" + str(impl_state)]))
            elif (var in labels) and (labels[var] is False):
                and_args.append(self._not(self._func("fo_" + var, ["t_" + str(impl_state)])))
            elif var not in labels:
                pass
            else:
                assert False, "Error: Variable in label has wrong value: " + labels[var]
        if len(and_args)>1:
            or_args.append(self._and(and_args))
        elif len(and_args)==1:
            or_args.append(and_args[0])

        if len(or_args)>1:
            smt_str = self._or(or_args)
        elif len(or_args)==1:
            smt_str = or_args[0]

        return smt_str

    def _make_main_assertions(self, num_impl_states):
        smt_str=self._comment("main assertions")

        for uct_state in self.uct.nodes:
            for label, dst_set_list in uct_state.transitions.items():
                assert len(dst_set_list) == 1, 'unsupported: ' + str(dst_set_list)

                dst_set = dst_set_list[0]
                for uct_state_next in dst_set:
                    for input_value in range (0, self.upsilon.get_num_element()):
                        for impl_state in range (0, num_impl_states):
                            smt_str+=self._comment('q=q_' + uct_state.name + " (q',v)=(q_" + uct_state_next.name + "," +
                                                   self.upsilon.get_element_str(input_value) + "), t=t_" + str(impl_state))

                            implication_left_1 = self._func("lambda_B", ["q_" + uct_state.name, "t_" + str(impl_state)])
                            implication_left_2 = self._make_trans_condition(label, impl_state)

                            if len(implication_left_2)>0:
                                implication_left = self._and([implication_left_1, implication_left_2])
                            else:
                                implication_left = implication_left_1

                            arg = self._func("tau", ["t_" + str(impl_state), self.upsilon.get_element_str(input_value)])
                            implication_right_1 = self._func("lambda_B", ["q_" + uct_state_next.name, arg])

                            gt_arg_1 = self._func("lambda_sharp", ["q_" + uct_state_next.name, arg])
                            gt_arg_2 = self._func("lambda_sharp", ["q_" + uct_state.name, "t_" + str(impl_state)])

                            if uct_state_next not in self.uct.rejecting_nodes:
                                implication_right_2 = self._ge(gt_arg_1, gt_arg_2)
                            else:
                                implication_right_2 = self._gt(gt_arg_1, gt_arg_2)

                            implication_right = self._and([implication_right_1, implication_right_2])

                            smt_str += self._assert(self._implies(implication_left, implication_right))

        return smt_str

    def _make_set_logic(self, logic):
        return '(set-logic {0})\n'.format(logic)

    def _make_headers(self):
        return '(set-option :ematching false)\n(set-option :produce-models true)\n'

    def encode_uct(self, num_impl_states):
        """ Return list of strings representing smt file. """

        smt_str = self._make_headers()

        smt_str += self._make_set_logic('UFLIA')
        smt_str += self._make_state_declarations([uct_state.name for uct_state in self.uct.nodes], "Q")
        smt_str += self._make_state_declarations([str(impl_state) for impl_state in range(0, num_impl_states)], "T")
        smt_str += self._make_input_declarations()
        smt_str += self._make_other_declarations()

        smt_str += self._make_main_assertions(num_impl_states)

        smt_str += self._make_root_condition()
        smt_str += self._make_input_preservation(num_impl_states)
        smt_str += "\n"
        smt_str += self.CHECK_SAT
        for x in range (0, num_impl_states):
            for y in range (0, self.upsilon.get_num_element()):
                smt_str += "(get-value ((tau t_" + str(x) + " " + str(self.upsilon.get_element_str(y)) +")))\n"
        for x in  range (0, num_impl_states):
            smt_str += "(get-value (t_" + str(x) + "))\n"

        for input in self.inputs:
            for x in  range (0, num_impl_states):
                smt_str += "(get-value ((fo_"+input +" t_" + str(x) +")))\n"
        for output in self.outputs:
            for x in  range (0, num_impl_states):
                smt_str += "(get-value ((fo_"+output+" t_"+ str(x) +")))\n"
        smt_str += self.EXIT_CALL

        self._logger.debug(smt_str)

        return smt_str

class Upsilon:
    m = []
    inputs = None

    def __init__(self, inputs):
        self.inputs = inputs
        self.m = list(itertools.product([False, True], repeat=len(inputs)))

    def get_element_str (self, num):

        line = 'i'

        lineLen = len(self.inputs)

        for i in range (0,lineLen):
            value = self.m[num][i]
            name = self.inputs[i]
            if value is True:
                line+= "_" + name
            else:
                line+= "_not_" + name

        return line

    def get_num_element(self):
        return len(self.m)

    def is_input_set(self, num, input):
        pos = self.inputs.index(input)
        return self.m[num][pos]<commit_msg>minor<commit_after>import itertools
import logging

class Encoder:

    CHECK_SAT = "(check-sat)\n"
    GET_MODEL = "(get-model)\n"
    EXIT_CALL = "(exit)\n"

    def __init__ (self, uct, inputs, outputs):
        self.uct=uct
        self.inputs=inputs
        self.outputs=outputs
        self.upsilon = Upsilon(self.inputs)
        self._logger = logging.getLogger(__name__)

    def _func(self, function, args):

        smt_str = '(' + function + ' '
        for arg in args:
            smt_str += arg+' '
        if len(args):
            smt_str = smt_str[:-1]
        smt_str += ')'
        return smt_str

    def _assert(self, formula):
        smt_str = '(assert ' + formula + ')\n'
        return smt_str

    def _comment(self, comment):
        smt_str = '; ' + comment + '\n'
        return smt_str

    def _declare_fun(self, name, vars, type):
        smt_str = '(declare-fun '
        smt_str += name + ' ('

        for var in vars:
            smt_str += var + ' '
        if len(vars):
            smt_str = smt_str[:-1]

        smt_str += ') ' + type + ')\n'
        return smt_str

    def _declare_sort(self, name, num_param):
        smt_str = '(declare-sort '
        smt_str += name + ' ' + str(num_param) + ')\n'
        return smt_str

    def _implies(self, arg1, arg2):
        smt_str = '(=> ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _eq(self, arg1, arg2): #is equal to
        smt_str = '(= ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _gt(self, arg1, arg2): #is greater than
        smt_str = '(> ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _ge(self, arg1, arg2): #is greater than or equal to
        smt_str = '(>= ' + arg1 + ' '+ arg2 + ')'
        return smt_str

    def _lt(self, arg1, arg2): #is less than
        smt_str = '(< ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _le(self, arg1, arg2):#is less than or equal to
        smt_str = '(<= ' + arg1 + ' ' + arg2 + ')'
        return smt_str

    def _not(self, argument):
        smt_str = '(not ' + argument + ')'
        return smt_str

    def _and(self, arguments):
        smt_str = '(and '
        smt_str += self.make_and_or_xor_body(arguments) + ')'
        return smt_str

    def _or(self, arguments):
        smt_str = '(or '
        smt_str += self.make_and_or_xor_body(arguments) + ')'
        return smt_str

    def _xor(self, arguments):
        smt_str = '(xor '
        smt_str += self.make_and_or_xor_body(arguments) + ')'
        return smt_str

    def make_and_or_xor_body(self, arguments):
        smt_str=''
        for arg in arguments:
            smt_str += arg + ' '
        if len(arguments):
            smt_str = smt_str[:-1]
        return smt_str

    def _forall(self, param_list, expression):
        smt_str = '(forall ('
        for pairs in param_list:             #name-sort pair
            smt_str += '('+ pairs[0] + ' ' + pairs[1] + ')'
        smt_str += ')' + expression + ')'
        return smt_str

    def _make_state_declarations(self, state_names, sort_name):
        smt_str = '(declare-datatypes () (({0} {1})))\n'.format(sort_name, ' '.join([sort_name.lower() + '_' + x for x in state_names]))
        return smt_str

    def _make_input_declarations(self):

        smt_str = self._comment("define a new type Upsilon for the input letters:")
        smt_str += self._declare_sort("Upsilon", 0)
        smt_str += self._comment("constants of type Upsilon:")

        for input_value in range (0, self.upsilon.get_num_element()):
            smt_str += self._declare_fun(self.upsilon.get_element_str(input_value),[],"Upsilon")

        smt_str +='\n'
        return smt_str

    def _make_other_declarations(self):

        smt_str = self._comment("Declarations for transition relation, output function and annotation")
        smt_str += self._declare_fun("tau", ["T","Upsilon"], "T")

        #declare output functions
        for input in self.inputs:
            smt_str += self._declare_fun("fo_"+input,["T"],"Bool")
        for output in self.outputs:
            smt_str += self._declare_fun("fo_"+output,["T"],"Bool")

        #declare annotations
        smt_str += self._declare_fun("lambda_B", ["Q","T"], "Bool")
        smt_str += self._declare_fun("lambda_sharp", ["Q","T"], "Int")
        smt_str += '\n'

        return smt_str

    def _make_root_condition(self):

        smt_str = self._comment("the root node of the run graph is labelled by a natural number:")

        elements = []
        for state in self.uct.initial_nodes:
            elements.append(self._func("lambda_B", ["q_"+state.name,"t_0"]))

        if len(elements)>1:
            smt_str += self._assert(self._and(elements))
        else:
            smt_str += self._assert(elements[0])

        return smt_str

    def _make_input_preservation(self, num_impl_states):
        smt_str = self._comment("input preserving:")
        elements = []
        for input in self.inputs:
            for input_value in range(0, self.upsilon.get_num_element()):
                for impl_state in range(0, num_impl_states):
                    element = self._func("fo_" + input, [self._func("tau", ["t_" + str(impl_state), self.upsilon.get_element_str(input_value)])])

                    if self.upsilon.is_input_set(input_value, input) is False:
                        element = self._not(element)
                    elements.append(element)

        smt_str += self._assert(self._and(elements))

        return smt_str

    def _make_trans_condition(self, labels, impl_state):
        smt_str=''
        input_output_list = self.inputs + self.outputs
        or_args = []
        and_args = []
        for var in input_output_list:  #for all inputs and outputs
            if (var in labels) and (labels[var] is True):
                and_args.append(self._func("fo_" + var, ["t_" + str(impl_state)]))
            elif (var in labels) and (labels[var] is False):
                and_args.append(self._not(self._func("fo_" + var, ["t_" + str(impl_state)])))
            elif var not in labels:
                pass
            else:
                assert False, "Error: Variable in label has wrong value: " + labels[var]
        if len(and_args)>1:
            or_args.append(self._and(and_args))
        elif len(and_args)==1:
            or_args.append(and_args[0])

        if len(or_args)>1:
            smt_str = self._or(or_args)
        elif len(or_args)==1:
            smt_str = or_args[0]

        return smt_str

    def _make_main_assertions(self, num_impl_states):
        smt_str=self._comment("main assertions")

        for uct_state in self.uct.nodes:
            for label, dst_set_list in uct_state.transitions.items():
                assert len(dst_set_list) == 1, 'unsupported: ' + str(dst_set_list)

                dst_set = dst_set_list[0]
                for uct_state_next in dst_set:
                    for input_value in range (0, self.upsilon.get_num_element()):
                        for impl_state in range (0, num_impl_states):
                            smt_str+=self._comment('q=q_' + uct_state.name + " (q',v)=(q_" + uct_state_next.name + "," +
                                                   self.upsilon.get_element_str(input_value) + "), t=t_" + str(impl_state))

                            implication_left_1 = self._func("lambda_B", ["q_" + uct_state.name, "t_" + str(impl_state)])
                            implication_left_2 = self._make_trans_condition(label, impl_state)

                            if len(implication_left_2)>0:
                                implication_left = self._and([implication_left_1, implication_left_2])
                            else:
                                implication_left = implication_left_1

                            arg = self._func("tau", ["t_" + str(impl_state), self.upsilon.get_element_str(input_value)])
                            implication_right_1 = self._func("lambda_B", ["q_" + uct_state_next.name, arg])

                            gt_arg_1 = self._func("lambda_sharp", ["q_" + uct_state_next.name, arg])
                            gt_arg_2 = self._func("lambda_sharp", ["q_" + uct_state.name, "t_" + str(impl_state)])

                            if uct_state_next not in self.uct.rejecting_nodes:
                                implication_right_2 = self._ge(gt_arg_1, gt_arg_2)
                            else:
                                implication_right_2 = self._gt(gt_arg_1, gt_arg_2)

                            implication_right = self._and([implication_right_1, implication_right_2])

                            smt_str += self._assert(self._implies(implication_left, implication_right))

        return smt_str

    def _make_set_logic(self, logic):
        return '(set-logic {0})\n'.format(logic)

    def _make_headers(self):
        return '(set-option :ematching false)\n(set-option :produce-models true)\n'

    def encode_uct(self, num_impl_states):
        """ Return list of strings representing smt file. """

        smt_str = self._make_headers()

        smt_str += self._make_set_logic('UFLIA')
        smt_str += self._make_state_declarations([uct_state.name for uct_state in self.uct.nodes], "Q")
        smt_str += self._make_state_declarations([str(impl_state) for impl_state in range(0, num_impl_states)], "T")
        smt_str += self._make_input_declarations()
        smt_str += self._make_other_declarations()

        smt_str += self._make_main_assertions(num_impl_states)

        smt_str += self._make_root_condition()
        smt_str += self._make_input_preservation(num_impl_states)
        smt_str += "\n"
        smt_str += self.CHECK_SAT
        for x in range (0, num_impl_states):
            for y in range (0, self.upsilon.get_num_element()):
                smt_str += "(get-value ((tau t_" + str(x) + " " + str(self.upsilon.get_element_str(y)) +")))\n"
        for x in  range (0, num_impl_states):
            smt_str += "(get-value (t_" + str(x) + "))\n"

        for input in self.inputs:
            for x in  range (0, num_impl_states):
                smt_str += "(get-value ((fo_"+input +" t_" + str(x) +")))\n"
        for output in self.outputs:
            for x in  range (0, num_impl_states):
                smt_str += "(get-value ((fo_"+output+" t_"+ str(x) +")))\n"
        smt_str += self.EXIT_CALL

        self._logger.debug(smt_str)

        return smt_str

class Upsilon:
    m = []
    inputs = None

    def __init__(self, inputs):
        self.inputs = inputs
        self.m = list(itertools.product([False, True], repeat=len(inputs)))

    def get_element_str (self, num):

        line = 'i'

        lineLen = len(self.inputs)

        for i in range (0,lineLen):
            value = self.m[num][i]
            name = self.inputs[i]
            if value is True:
                line+= "_" + name
            else:
                line+= "_not_" + name

        return line

    def get_num_element(self):
        return len(self.m)

    def is_input_set(self, num, input):
        pos = self.inputs.index(input)
        return self.m[num][pos]<|endoftext|>