<commit_before>"""
Created on 13 Aug 2016

@author: Bruno Beloff (bruno.beloff@southcoastscience.com)

Note that, for the ISO 8601 constructors, milliseconds are optional.

http://www.saltycrane.com/blog/2009/05/converting-time-zones-datetime-objects-python/
https://stackoverflow.com/questions/6410971/python-datetime-object-show-wrong-timezone-offset
"""

import pytz
import re
import tzlocal

from datetime import datetime, timedelta, timezone

from scs_core.data.timedelta import Timedelta

# TODO: object should carry a flag indicating presence of millis - for "preserve millis" mode?

# --------------------------------------------------------------------------------------------------------------------

class LocalizedDatetime(object):
    """
    classdocs
    """

    @classmethod
    def now(cls):
        zone = tzlocal.get_localzone()
        localized = datetime.now(zone)

        return LocalizedDatetime(localized)

    @classmethod
    def construct_from_date(cls, date):
        zone = tzlocal.get_localzone()
        localized = zone.localize(datetime(date.year, date.month, date.day))

        return LocalizedDatetime(localized)

    @classmethod
    def construct_from_timestamp(cls, t, tz=None):
        zone = tzlocal.get_localzone() if tz is None else tz
        localized = datetime.fromtimestamp(t, zone)

        return LocalizedDatetime(localized)

    @classmethod
    def construct_from_iso8601(cls, datetime_str):
        if datetime_str is None:
            return None

        # Z timezone offset...
        localized = cls.__construct_from_iso8601_z(datetime_str)

        if localized:
            return localized

        # numeric timezone offset...
        return cls.__construct_from_iso8601_numeric(datetime_str)

    @classmethod
    def construct_from_date_time(cls, date_str, time_str, tz=None):
        # date...
        match = re.match('(\d{4})-(\d{2})-(\d{2})', date_str)       # e.g. 2019-01-14

        if match is None:
            return None

        fields = match.groups()

        year = int(fields[0])
        month = int(fields[1])
        day = int(fields[2])

        # time...
        match = re.match('(\d{2}):(\d{2})(:(\d{2}))?', time_str)       # e.g. 24:00:00

        if match is None:
            return None

        fields = match.groups()

        hours_delta = int(fields[0])
        minutes_delta = int(fields[1])
        seconds_delta = 0 if fields[3] is None else int(fields[3])

        # zone...
        zone = pytz.timezone('Etc/UTC') if tz is None else tz

        # construct...
        localized = zone.localize(datetime(year, month, day, 0, 0, 0, 0))

        start = LocalizedDatetime(localized)
        corrected = start.timedelta(seconds=seconds_delta, minutes=minutes_delta, hours=hours_delta)

        return corrected

    @classmethod
    def construct_from_jdict(cls, jdict):           # TODO: deprecated
        return cls.construct_from_iso8601(jdict)

    # ----------------------------------------------------------------------------------------------------------------

    @classmethod
    def __construct_from_iso8601_z(cls, datetime_str):
        # match...
        match = re.match('(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d{3}))?Z', datetime_str)

        if match is None:
            return None

        fields = match.groups()

        # fields...
        year = int(fields[0])
        month = int(fields[1])
        day = int(fields[2])

        hour = int(fields[3])
        minute = int(fields[4])
        second = int(fields[5])
        micros = int(fields[6]) * 1000 if fields[6] else 0

        # construct...
        zone_offset = timedelta(hours=0, minutes=0)
        zone = timezone(zone_offset)

        localized = datetime(year, month, day, hour, minute, second, micros, tzinfo=zone)

        return LocalizedDatetime(localized)

    @classmethod
    def __construct_from_iso8601_numeric(cls, datetime_str):
        # match...
        match = re.match('(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d{3}))?([ +\-]?)(\d{2}):(\d{2})',
                         datetime_str)

        if match is None:
            return None

        fields = match.groups()

        # fields...
        year = int(fields[0])
        month = int(fields[1])
        day = int(fields[2])

        hour = int(fields[3])
        minute = int(fields[4])
        second = int(fields[5])
        micros = int(fields[6]) * 1000 if fields[6] else 0

        zone_sign = -1 if fields[7] == '-' else 1
        zone_hours = int(fields[8])
        zone_mins = int(fields[9])

        # construct...
        zone_offset = zone_sign * timedelta(hours=zone_hours, minutes=zone_mins)
        zone = timezone(zone_offset)

        localized = datetime(year, month, day, hour, minute, second, micros, tzinfo=zone)

        return LocalizedDatetime(localized)

    # ----------------------------------------------------------------------------------------------------------------

    def __init__(self, localized):
        """
        Constructor
        """
        self.__datetime = localized                     # datetime

    def __lt__(self, other):
        return self.datetime < other.datetime

    def __eq__(self, other):
        return self.datetime == other.datetime

    def __sub__(self, other):
        if type(other) == LocalizedDatetime:
            operand = other.datetime

        elif type(other) == Timedelta:
            operand = other.delta

        else:
            operand = other

        return self.datetime - operand

    # ----------------------------------------------------------------------------------------------------------------

    def utc(self):
        localized = self.datetime.astimezone(pytz.timezone('Etc/UTC'))

        return LocalizedDatetime(localized)

    def localize(self, zone):                           # zone may be datetime.timezone or pytz.timezone
        localized = self.datetime.astimezone(zone)

        return LocalizedDatetime(localized)

    def timedelta(self, days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0):
        td = timedelta(days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds,
                       minutes=minutes, hours=hours, weeks=weeks)

        return LocalizedDatetime(self.__datetime + td)

    # ----------------------------------------------------------------------------------------------------------------

    def as_iso8601(self, include_millis=False):
        """
        example: 2016-08-13T00:38:05.210+01:00
        """
        date = self.__datetime.strftime("%Y-%m-%d")
        time = self.__datetime.strftime("%H:%M:%S")

        # millis...
        if include_millis:
            micros = float(self.__datetime.strftime("%f"))
            millis = ".%03d" % (micros // 1000)

        else:
            millis = ""

        # time zone...
        zone = self.__datetime.strftime("%z")

        # Z format...
        if float(zone[1:]) == 0.0:
            return "%sT%s%sZ" % (date, time, millis)

        # numeric format...
        zone_hours = zone[:3]
        zone_mins = zone[3:]

        return "%sT%s%s%s:%s" % (date, time, millis, zone_hours, zone_mins)

    def as_time(self):
        time = self.__datetime.strftime("%H:%M:%S")

        micros = float(self.__datetime.strftime("%f"))
        millis = "%03d" % (micros // 1000)

        return "%s.%s" % (time, millis)

    def timestamp(self):
        return self.__datetime.timestamp()

    # ----------------------------------------------------------------------------------------------------------------

    @property
    def datetime(self):
        return self.__datetime

    @property
    def utc_datetime(self):
        utc_localised = self.utc()

        return utc_localised.datetime

    @property
    def tzinfo(self):
        return self.__datetime.tzinfo

    # ----------------------------------------------------------------------------------------------------------------

    def __str__(self, *args, **kwargs):
        return "LocalizedDatetime:{datetime:%s}" % self.datetime
<commit_msg>Fixed a bug in sample_aggregate.py<commit_after>"""
Created on 13 Aug 2016

@author: Bruno Beloff (bruno.beloff@southcoastscience.com)

Note that, for the ISO 8601 constructors, milliseconds are optional.

http://www.saltycrane.com/blog/2009/05/converting-time-zones-datetime-objects-python/
https://stackoverflow.com/questions/6410971/python-datetime-object-show-wrong-timezone-offset
"""

import pytz
import re
import tzlocal

from datetime import datetime, timedelta, timezone

from scs_core.data.timedelta import Timedelta

# TODO: object should carry a flag indicating presence of millis - for "preserve millis" mode?

# --------------------------------------------------------------------------------------------------------------------

class LocalizedDatetime(object):
    """
    classdocs
    """

    @classmethod
    def now(cls):
        zone = tzlocal.get_localzone()
        localized = datetime.now(zone)

        return LocalizedDatetime(localized)

    @classmethod
    def construct_from_date(cls, date):
        zone = tzlocal.get_localzone()
        localized = zone.localize(datetime(date.year, date.month, date.day))

        return LocalizedDatetime(localized)

    @classmethod
    def construct_from_timestamp(cls, t, tz=None):
        zone = tzlocal.get_localzone() if tz is None else tz
        localized = datetime.fromtimestamp(t, zone)

        return LocalizedDatetime(localized)

    @classmethod
    def construct_from_iso8601(cls, datetime_str):
        if datetime_str is None:
            return None

        # Z timezone offset...
        localized = cls.__construct_from_iso8601_z(datetime_str)

        if localized:
            return localized

        # numeric timezone offset...
        return cls.__construct_from_iso8601_numeric(datetime_str)

    @classmethod
    def construct_from_date_time(cls, date_str, time_str, tz=None):
        # date...
        match = re.match('(\d{4})-(\d{2})-(\d{2})', date_str)       # e.g. 2019-01-14

        if match is None:
            return None

        fields = match.groups()

        year = int(fields[0])
        month = int(fields[1])
        day = int(fields[2])

        # time...
        match = re.match('(\d{2}):(\d{2})(:(\d{2}))?', time_str)       # e.g. 24:00:00

        if match is None:
            return None

        fields = match.groups()

        hours_delta = int(fields[0])
        minutes_delta = int(fields[1])
        seconds_delta = 0 if fields[3] is None else int(fields[3])

        # zone...
        zone = pytz.timezone('Etc/UTC') if tz is None else tz

        # construct...
        localized = zone.localize(datetime(year, month, day, 0, 0, 0, 0))

        start = LocalizedDatetime(localized)
        corrected = start.timedelta(seconds=seconds_delta, minutes=minutes_delta, hours=hours_delta)

        return corrected

    @classmethod
    def construct_from_jdict(cls, jdict):           # TODO: deprecated
        return cls.construct_from_iso8601(jdict)

    # ----------------------------------------------------------------------------------------------------------------

    @classmethod
    def __construct_from_iso8601_z(cls, datetime_str):
        # match...
        match = re.match('(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d{3}))?Z', datetime_str)

        if match is None:
            return None

        fields = match.groups()

        # fields...
        year = int(fields[0])
        month = int(fields[1])
        day = int(fields[2])

        hour = int(fields[3])
        minute = int(fields[4])
        second = int(fields[5])
        micros = int(fields[6]) * 1000 if fields[6] else 0

        # construct...
        zone_offset = timedelta(hours=0, minutes=0)
        zone = timezone(zone_offset)

        localized = datetime(year, month, day, hour, minute, second, micros, tzinfo=zone)

        return LocalizedDatetime(localized)

    @classmethod
    def __construct_from_iso8601_numeric(cls, datetime_str):
        # match...
        match = re.match('(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d{3}))?([ +\-]?)(\d{2}):(\d{2})',
                         datetime_str)

        if match is None:
            return None

        fields = match.groups()

        # fields...
        year = int(fields[0])
        month = int(fields[1])
        day = int(fields[2])

        hour = int(fields[3])
        minute = int(fields[4])
        second = int(fields[5])
        micros = int(fields[6]) * 1000 if fields[6] else 0

        zone_sign = -1 if fields[7] == '-' else 1
        zone_hours = int(fields[8])
        zone_mins = int(fields[9])

        # construct...
        zone_offset = zone_sign * timedelta(hours=zone_hours, minutes=zone_mins)
        zone = timezone(zone_offset)

        localized = datetime(year, month, day, hour, minute, second, micros, tzinfo=zone)

        return LocalizedDatetime(localized)

    # ----------------------------------------------------------------------------------------------------------------

    def __init__(self, localized):
        """
        Constructor
        """
        self.__datetime = localized                     # datetime

    def __eq__(self, other):
        return self.datetime == other.datetime

    def __ge__(self, other):
        return self.datetime >= other.datetime

    def __gt__(self, other):
        return self.datetime > other.datetime

    def __le__(self, other):
        return self.datetime <= other.datetime

    def __lt__(self, other):
        return self.datetime < other.datetime

    def __sub__(self, other):
        if type(other) == LocalizedDatetime:
            operand = other.datetime

        elif type(other) == Timedelta:
            operand = other.delta

        else:
            operand = other

        return self.datetime - operand

    # ----------------------------------------------------------------------------------------------------------------

    def utc(self):
        localized = self.datetime.astimezone(pytz.timezone('Etc/UTC'))

        return LocalizedDatetime(localized)

    def localize(self, zone):                           # zone may be datetime.timezone or pytz.timezone
        localized = self.datetime.astimezone(zone)

        return LocalizedDatetime(localized)

    def timedelta(self, days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0):
        td = timedelta(days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds,
                       minutes=minutes, hours=hours, weeks=weeks)

        return LocalizedDatetime(self.__datetime + td)

    # ----------------------------------------------------------------------------------------------------------------

    def as_iso8601(self, include_millis=False):
        """
        example: 2016-08-13T00:38:05.210+01:00
        """
        date = self.__datetime.strftime("%Y-%m-%d")
        time = self.__datetime.strftime("%H:%M:%S")

        # millis...
        if include_millis:
            micros = float(self.__datetime.strftime("%f"))
            millis = ".%03d" % (micros // 1000)

        else:
            millis = ""

        # time zone...
        zone = self.__datetime.strftime("%z")

        # Z format...
        if float(zone[1:]) == 0.0:
            return "%sT%s%sZ" % (date, time, millis)

        # numeric format...
        zone_hours = zone[:3]
        zone_mins = zone[3:]

        return "%sT%s%s%s:%s" % (date, time, millis, zone_hours, zone_mins)

    def as_time(self):
        time = self.__datetime.strftime("%H:%M:%S")

        micros = float(self.__datetime.strftime("%f"))
        millis = "%03d" % (micros // 1000)

        return "%s.%s" % (time, millis)

    def timestamp(self):
        return self.__datetime.timestamp()

    # ----------------------------------------------------------------------------------------------------------------

    @property
    def datetime(self):
        return self.__datetime

    @property
    def utc_datetime(self):
        utc_localised = self.utc()

        return utc_localised.datetime

    @property
    def tzinfo(self):
        return self.__datetime.tzinfo

    # ----------------------------------------------------------------------------------------------------------------

    def __str__(self, *args, **kwargs):
        return "LocalizedDatetime:{datetime:%s}" % self.datetime
<|endoftext|>


<commit_before>"""Display a rotomap."""

import cv2
import numpy

import mel.lib.common
import mel.lib.image
import mel.lib.math
import mel.lib.ui
import mel.rotomap.moles
import mel.rotomap.tricolour

def load_image(path):
    image = cv2.imread(path)

    return image

def draw_mole(image, x, y, colours):

    radius = 16
    for index in range(3):
        cv2.circle(image, (x, y), radius, colours[index], -1)
        radius -= 4

def draw_non_canonical_mole(image, x, y, colours):
    radius = 16
    thickness = 4
    for index in range(3):
        top_left = (x - radius, y - radius)
        bottom_right = (x + radius, y + radius)
        cv2.rectangle(image, top_left, bottom_right, colours[index], thickness)
        radius -= thickness

def draw_crosshair(image, x, y):
    inner_radius = 16
    outer_radius = 24
    white = (255, 255, 255)
    black = (0, 0, 0)

    directions = [(1, 0), (0, -1), (-1, 0), (0, 1)]  # Right, down, left, up

    size_color_list = [(3, white), (2, black)]

    for size, color in size_color_list:
        for d in directions:
            cv2.line(
                image,
                (x + (inner_radius * d[0]), y + (inner_radius * d[1])),
                (x + (outer_radius * d[0]), y + (outer_radius * d[1])),
                color,
                size)

class Display:

    def __init__(self, width, height, uuid_to_tricolour=None):
        self._name = str(id(self))

        if width is None or height is None:
            full_width_height = mel.lib.ui.guess_fullscreen_width_height()
            if width is None:
                width = full_width_height[0]
            if height is None:
                height = full_width_height[1]

        self._width = width
        self._height = height

        cv2.namedWindow(self._name)
        cv2.namedWindow(self._name, cv2.WINDOW_NORMAL)
        cv2.resizeWindow(self._name, self._width, self._height)

        self._uuid_to_tricolour = uuid_to_tricolour
        if self._uuid_to_tricolour is None:
            self._uuid_to_tricolour = (
                mel.rotomap.tricolour.uuid_to_tricolour_first_digits)

        self._transform = None

        self._zoom_x = None
        self._zoom_y = None
        self._is_zoomed = False

        self._is_showing_markers = True
        self._is_faded_markers = True
        self._highlight_uuid = None

    def toggle_markers(self):
        self._is_showing_markers = not self._is_showing_markers

    def set_highlight_uuid(self, highlight_uuid):
        self._highlight_uuid = highlight_uuid

    def toggle_faded_markers(self):
        self._is_faded_markers = not self._is_faded_markers

    def show_current(self, image, mole_list):

        if not self._is_zoomed:
            self._transform = FittedImageTransform(
                image, numpy.array((self._width, self._height)))
        else:
            self._transform = ZoomedImageTransform(
                image,
                numpy.array((self._zoom_x, self._zoom_y)),
                numpy.array((self._width, self._height)))

        image = self._transform.render()

        highlight_mole = None
        if self._highlight_uuid is not None:
            for m in mole_list:
                if m['uuid'] == self._highlight_uuid:
                    highlight_mole = m
                    break

        if self._is_showing_markers:
            image = self._overlay_mole_markers(
                image, mole_list, highlight_mole)

        cv2.imshow(self._name, image)

    def show_fitted(self, image, mole_list):
        self.set_fitted()
        self.show_current(image, mole_list)

    def show_zoomed(self, image, mole_list, x, y):
        self.set_zoomed(x, y)
        self.show_current(image, mole_list)

    def set_fitted(self):
        self._is_zoomed = False

    def set_zoomed(self, x, y):
        self._zoom_x = x
        self._zoom_y = y
        self._is_zoomed = True

    def _overlay_mole_markers(self, image, mole_list, highlight_mole):
        marker_image = image
        if self._is_faded_markers:
            marker_image = image.copy()
        for mole in mole_list:
            x, y = self._transform.imagexy_to_transformedxy(
                mole['x'], mole['y'])
            if mole is highlight_mole:
                draw_crosshair(marker_image, x, y)
            colours = self._uuid_to_tricolour(mole['uuid'])
            if mole['is_uuid_canonical']:
                draw_mole(marker_image, x, y, colours)
            else:
                draw_non_canonical_mole(marker_image, x, y, colours)
        if self._is_faded_markers:
            image = cv2.addWeighted(image, 0.75, marker_image, 0.25, 0.0)

        return image

    def set_mouse_callback(self, callback):
        cv2.setMouseCallback(self._name, callback)

    def clear_mouse_callback(self):
        cv2.setMouseCallback(
            self._name,
            mel.lib.common.make_null_mouse_callback())

    def windowxy_to_imagexy(self, window_x, window_y):
        return self._transform.transformedxy_to_imagexy(window_x, window_y)

    def set_title(self, title):
        cv2.setWindowTitle(self._name, title)

class ZoomedImageTransform():

    def __init__(self, image, pos, rect):
        self._pos = pos
        self._rect = rect
        self._offset = mel.lib.image.calc_centering_offset(pos, rect)

        self._image = image

    def render(self):

        return mel.lib.image.centered_at(
            self._image,
            self._pos,
            self._rect)

    def imagexy_to_transformedxy(self, x, y):
        return numpy.array((x, y)) + self._offset

    def transformedxy_to_imagexy(self, x, y):
        return numpy.array((x, y)) - self._offset

class FittedImageTransform():

    def __init__(self, image, fit_rect):
        self._fit_rect = fit_rect
        image_rect = mel.lib.image.get_image_rect(image)

        letterbox = mel.lib.image.calc_letterbox(
            image_rect[0],
            image_rect[1],
            self._fit_rect[0],
            self._fit_rect[1])

        self._offset = numpy.array(letterbox[:2])
        self._scale = image.shape[1] / letterbox[2]

        self._image = image

    def render(self):

        return mel.lib.image.letterbox(
            self._image, self._fit_rect[0], self._fit_rect[1])

    def imagexy_to_transformedxy(self, x, y):
        return (numpy.array((x, y)) / self._scale + self._offset).astype(int)

    def transformedxy_to_imagexy(self, x, y):
        return ((numpy.array((x, y)) - self._offset) * self._scale).astype(int)

class Editor:

    def __init__(self, path_list_list, width, height):
        self._uuid_to_tricolour = mel.rotomap.tricolour.UuidTriColourPicker()
        self.display = Display(width, height, self._uuid_to_tricolour)
        self.moledata_list = [MoleData(x) for x in path_list_list]

        self.moledata_index = 0
        self.moledata = self.moledata_list[self.moledata_index]
        self._follow = None
        self.show_current()

    def set_moles(self, moles):
        self.moledata.moles = moles
        self.show_current()

    def follow(self, uuid_to_follow):
        self._follow = uuid_to_follow
        self.display.set_highlight_uuid(self._follow)

        follow_mole = None
        for m in self.moledata.moles:
            if m['uuid'] == self._follow:
                follow_mole = m
                break

        if follow_mole is not None:
            image = self.moledata.get_image()
            self.display.show_zoomed(
                image, self.moledata.moles, follow_mole['x'], follow_mole['y'])
        else:
            self.show_fitted()

    def toggle_markers(self):
        self.display.toggle_markers()
        self.show_current()

    def toggle_faded_markers(self):
        self.display.toggle_faded_markers()
        self.show_current()

    def show_current(self):
        image = self.moledata.get_image()
        self.display.show_current(image, self.moledata.moles)
        self.display.set_title(self.moledata.current_image_path())

    def show_fitted(self):
        image = self.moledata.get_image()
        self.display.show_fitted(image, self.moledata.moles)

    def show_zoomed(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        image = self.moledata.get_image()
        self.display.show_zoomed(image, self.moledata.moles, image_x, image_y)

    def show_zoomed_display(self, image_x, image_y):
        image = self.moledata.get_image()
        self.display.show_zoomed(image, self.moledata.moles, image_x, image_y)

    def show_prev_map(self):
        self.moledata_index -= 1
        self.moledata_index %= len(self.moledata_list)
        self.moledata = self.moledata_list[self.moledata_index]
        self.show_current()

    def show_next_map(self):
        self.moledata_index += 1
        self.moledata_index %= len(self.moledata_list)
        self.moledata = self.moledata_list[self.moledata_index]
        self.show_current()

    def show_prev(self):
        self.moledata.decrement()
        self.show_current()

    def show_next(self):
        self.moledata.increment()
        self.show_current()

    def add_mole(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.add_mole(self.moledata.moles, image_x, image_y)
        self.moledata.save_moles()
        self.show_current()

    def add_mole_display(self, image_x, image_y, mole_uuid=None):
        mel.rotomap.moles.add_mole(
            self.moledata.moles, image_x, image_y, mole_uuid)
        self.moledata.save_moles()
        self.show_current()

    def set_mole_uuid(self, mouse_x, mouse_y, mole_uuid):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.set_nearest_mole_uuid(
            self.moledata.moles, image_x, image_y, mole_uuid)
        self.moledata.save_moles()
        self.show_current()

    def get_mole_uuid(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        return mel.rotomap.moles.get_nearest_mole_uuid(
            self.moledata.moles, image_x, image_y)

    def move_nearest_mole(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.move_nearest_mole(
            self.moledata.moles, image_x, image_y)
        self.moledata.save_moles()
        self.show_current()

    def remove_mole(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.remove_nearest_mole(
            self.moledata.moles, image_x, image_y)
        self.moledata.save_moles()
        self.show_current()

    def crud_mole(self, mole_uuid, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)

        i = mel.rotomap.moles.uuid_mole_index(self.moledata.moles, mole_uuid)
        if i is not None:
            self.moledata.moles[i]['x'] = image_x
            self.moledata.moles[i]['y'] = image_y
        else:
            mel.rotomap.moles.add_mole(
                self.moledata.moles, image_x, image_y, mole_uuid)

        self.moledata.save_moles()
        self.show_current()

class MoleData:

    def __init__(self, path_list):
        self.moles = []
        self._path_list = path_list
        self._list_index = 0
        self._num_images = len(self._path_list)
        self._cached_image = None
        self._cached_image_index = None
        self._load()

    def get_image(self):
        return self._load()

    def _load(self):

        if self._cached_image_index == self._list_index:
            return self._cached_image

        image_path = self._path_list[self._list_index]
        image = load_image(image_path)

        self.moles = mel.rotomap.moles.load_image_moles(image_path)

        self._cached_image_index = self._list_index
        self._cached_image = image

        return image

    def decrement(self):
        new_index = self._list_index + self._num_images - 1
        self._list_index = new_index % self._num_images

    def increment(self):
        self._list_index = (self._list_index + 1) % self._num_images

    def save_moles(self):
        image_path = self._path_list[self._list_index]
        mel.rotomap.moles.save_image_moles(self.moles, image_path)

    def current_image_path(self):
        return self._path_list[self._list_index]
<commit_msg>Refactor mel.rotomap.display, vectorise self._rect<commit_after>"""Display a rotomap."""

import cv2
import numpy

import mel.lib.common
import mel.lib.image
import mel.lib.math
import mel.lib.ui
import mel.rotomap.moles
import mel.rotomap.tricolour

def load_image(path):
    image = cv2.imread(path)

    return image

def draw_mole(image, x, y, colours):

    radius = 16
    for index in range(3):
        cv2.circle(image, (x, y), radius, colours[index], -1)
        radius -= 4

def draw_non_canonical_mole(image, x, y, colours):
    radius = 16
    thickness = 4
    for index in range(3):
        top_left = (x - radius, y - radius)
        bottom_right = (x + radius, y + radius)
        cv2.rectangle(image, top_left, bottom_right, colours[index], thickness)
        radius -= thickness

def draw_crosshair(image, x, y):
    inner_radius = 16
    outer_radius = 24
    white = (255, 255, 255)
    black = (0, 0, 0)

    directions = [(1, 0), (0, -1), (-1, 0), (0, 1)]  # Right, down, left, up

    size_color_list = [(3, white), (2, black)]

    for size, color in size_color_list:
        for d in directions:
            cv2.line(
                image,
                (x + (inner_radius * d[0]), y + (inner_radius * d[1])),
                (x + (outer_radius * d[0]), y + (outer_radius * d[1])),
                color,
                size)

class Display:

    def __init__(self, width, height, uuid_to_tricolour=None):
        self._name = str(id(self))

        if width is None or height is None:
            full_width_height = mel.lib.ui.guess_fullscreen_width_height()
            if width is None:
                width = full_width_height[0]
            if height is None:
                height = full_width_height[1]

        self._rect = numpy.array((width, height))

        cv2.namedWindow(self._name)
        cv2.namedWindow(self._name, cv2.WINDOW_NORMAL)
        cv2.resizeWindow(self._name, *self._rect)

        self._uuid_to_tricolour = uuid_to_tricolour
        if self._uuid_to_tricolour is None:
            self._uuid_to_tricolour = (
                mel.rotomap.tricolour.uuid_to_tricolour_first_digits)

        self._transform = None

        self._zoom_x = None
        self._zoom_y = None
        self._is_zoomed = False

        self._is_showing_markers = True
        self._is_faded_markers = True
        self._highlight_uuid = None

    def toggle_markers(self):
        self._is_showing_markers = not self._is_showing_markers

    def set_highlight_uuid(self, highlight_uuid):
        self._highlight_uuid = highlight_uuid

    def toggle_faded_markers(self):
        self._is_faded_markers = not self._is_faded_markers

    def show_current(self, image, mole_list):

        if not self._is_zoomed:
            self._transform = FittedImageTransform(
                image, self._rect)
        else:
            self._transform = ZoomedImageTransform(
                image,
                numpy.array((self._zoom_x, self._zoom_y)),
                self._rect)

        image = self._transform.render()

        highlight_mole = None
        if self._highlight_uuid is not None:
            for m in mole_list:
                if m['uuid'] == self._highlight_uuid:
                    highlight_mole = m
                    break

        if self._is_showing_markers:
            image = self._overlay_mole_markers(
                image, mole_list, highlight_mole)

        cv2.imshow(self._name, image)

    def show_fitted(self, image, mole_list):
        self.set_fitted()
        self.show_current(image, mole_list)

    def show_zoomed(self, image, mole_list, x, y):
        self.set_zoomed(x, y)
        self.show_current(image, mole_list)

    def set_fitted(self):
        self._is_zoomed = False

    def set_zoomed(self, x, y):
        self._zoom_x = x
        self._zoom_y = y
        self._is_zoomed = True

    def _overlay_mole_markers(self, image, mole_list, highlight_mole):
        marker_image = image
        if self._is_faded_markers:
            marker_image = image.copy()
        for mole in mole_list:
            x, y = self._transform.imagexy_to_transformedxy(
                mole['x'], mole['y'])
            if mole is highlight_mole:
                draw_crosshair(marker_image, x, y)
            colours = self._uuid_to_tricolour(mole['uuid'])
            if mole['is_uuid_canonical']:
                draw_mole(marker_image, x, y, colours)
            else:
                draw_non_canonical_mole(marker_image, x, y, colours)
        if self._is_faded_markers:
            image = cv2.addWeighted(image, 0.75, marker_image, 0.25, 0.0)

        return image

    def set_mouse_callback(self, callback):
        cv2.setMouseCallback(self._name, callback)

    def clear_mouse_callback(self):
        cv2.setMouseCallback(
            self._name,
            mel.lib.common.make_null_mouse_callback())

    def windowxy_to_imagexy(self, window_x, window_y):
        return self._transform.transformedxy_to_imagexy(window_x, window_y)

    def set_title(self, title):
        cv2.setWindowTitle(self._name, title)

class ZoomedImageTransform():

    def __init__(self, image, pos, rect):
        self._pos = pos
        self._rect = rect
        self._offset = mel.lib.image.calc_centering_offset(pos, rect)

        self._image = image

    def render(self):

        return mel.lib.image.centered_at(
            self._image,
            self._pos,
            self._rect)

    def imagexy_to_transformedxy(self, x, y):
        return numpy.array((x, y)) + self._offset

    def transformedxy_to_imagexy(self, x, y):
        return numpy.array((x, y)) - self._offset

class FittedImageTransform():

    def __init__(self, image, fit_rect):
        self._fit_rect = fit_rect
        image_rect = mel.lib.image.get_image_rect(image)

        letterbox = mel.lib.image.calc_letterbox(
            image_rect[0],
            image_rect[1],
            self._fit_rect[0],
            self._fit_rect[1])

        self._offset = numpy.array(letterbox[:2])
        self._scale = image.shape[1] / letterbox[2]

        self._image = image

    def render(self):

        return mel.lib.image.letterbox(
            self._image, self._fit_rect[0], self._fit_rect[1])

    def imagexy_to_transformedxy(self, x, y):
        return (numpy.array((x, y)) / self._scale + self._offset).astype(int)

    def transformedxy_to_imagexy(self, x, y):
        return ((numpy.array((x, y)) - self._offset) * self._scale).astype(int)

class Editor:

    def __init__(self, path_list_list, width, height):
        self._uuid_to_tricolour = mel.rotomap.tricolour.UuidTriColourPicker()
        self.display = Display(width, height, self._uuid_to_tricolour)
        self.moledata_list = [MoleData(x) for x in path_list_list]

        self.moledata_index = 0
        self.moledata = self.moledata_list[self.moledata_index]
        self._follow = None
        self.show_current()

    def set_moles(self, moles):
        self.moledata.moles = moles
        self.show_current()

    def follow(self, uuid_to_follow):
        self._follow = uuid_to_follow
        self.display.set_highlight_uuid(self._follow)

        follow_mole = None
        for m in self.moledata.moles:
            if m['uuid'] == self._follow:
                follow_mole = m
                break

        if follow_mole is not None:
            image = self.moledata.get_image()
            self.display.show_zoomed(
                image, self.moledata.moles, follow_mole['x'], follow_mole['y'])
        else:
            self.show_fitted()

    def toggle_markers(self):
        self.display.toggle_markers()
        self.show_current()

    def toggle_faded_markers(self):
        self.display.toggle_faded_markers()
        self.show_current()

    def show_current(self):
        image = self.moledata.get_image()
        self.display.show_current(image, self.moledata.moles)
        self.display.set_title(self.moledata.current_image_path())

    def show_fitted(self):
        image = self.moledata.get_image()
        self.display.show_fitted(image, self.moledata.moles)

    def show_zoomed(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        image = self.moledata.get_image()
        self.display.show_zoomed(image, self.moledata.moles, image_x, image_y)

    def show_zoomed_display(self, image_x, image_y):
        image = self.moledata.get_image()
        self.display.show_zoomed(image, self.moledata.moles, image_x, image_y)

    def show_prev_map(self):
        self.moledata_index -= 1
        self.moledata_index %= len(self.moledata_list)
        self.moledata = self.moledata_list[self.moledata_index]
        self.show_current()

    def show_next_map(self):
        self.moledata_index += 1
        self.moledata_index %= len(self.moledata_list)
        self.moledata = self.moledata_list[self.moledata_index]
        self.show_current()

    def show_prev(self):
        self.moledata.decrement()
        self.show_current()

    def show_next(self):
        self.moledata.increment()
        self.show_current()

    def add_mole(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.add_mole(self.moledata.moles, image_x, image_y)
        self.moledata.save_moles()
        self.show_current()

    def add_mole_display(self, image_x, image_y, mole_uuid=None):
        mel.rotomap.moles.add_mole(
            self.moledata.moles, image_x, image_y, mole_uuid)
        self.moledata.save_moles()
        self.show_current()

    def set_mole_uuid(self, mouse_x, mouse_y, mole_uuid):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.set_nearest_mole_uuid(
            self.moledata.moles, image_x, image_y, mole_uuid)
        self.moledata.save_moles()
        self.show_current()

    def get_mole_uuid(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        return mel.rotomap.moles.get_nearest_mole_uuid(
            self.moledata.moles, image_x, image_y)

    def move_nearest_mole(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.move_nearest_mole(
            self.moledata.moles, image_x, image_y)
        self.moledata.save_moles()
        self.show_current()

    def remove_mole(self, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)
        mel.rotomap.moles.remove_nearest_mole(
            self.moledata.moles, image_x, image_y)
        self.moledata.save_moles()
        self.show_current()

    def crud_mole(self, mole_uuid, mouse_x, mouse_y):
        image_x, image_y = self.display.windowxy_to_imagexy(mouse_x, mouse_y)

        i = mel.rotomap.moles.uuid_mole_index(self.moledata.moles, mole_uuid)
        if i is not None:
            self.moledata.moles[i]['x'] = image_x
            self.moledata.moles[i]['y'] = image_y
        else:
            mel.rotomap.moles.add_mole(
                self.moledata.moles, image_x, image_y, mole_uuid)

        self.moledata.save_moles()
        self.show_current()

class MoleData:

    def __init__(self, path_list):
        self.moles = []
        self._path_list = path_list
        self._list_index = 0
        self._num_images = len(self._path_list)
        self._cached_image = None
        self._cached_image_index = None
        self._load()

    def get_image(self):
        return self._load()

    def _load(self):

        if self._cached_image_index == self._list_index:
            return self._cached_image

        image_path = self._path_list[self._list_index]
        image = load_image(image_path)

        self.moles = mel.rotomap.moles.load_image_moles(image_path)

        self._cached_image_index = self._list_index
        self._cached_image = image

        return image

    def decrement(self):
        new_index = self._list_index + self._num_images - 1
        self._list_index = new_index % self._num_images

    def increment(self):
        self._list_index = (self._list_index + 1) % self._num_images

    def save_moles(self):
        image_path = self._path_list[self._list_index]
        mel.rotomap.moles.save_image_moles(self.moles, image_path)

    def current_image_path(self):
        return self._path_list[self._list_index]
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# This file is part of the pyMor project (http://www.pymor.org).
# Copyright Holders: Felix Albrecht, Rene Milk, Stephan Rave
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

from __future__ import absolute_import, division, print_function

import numpy as np

from pymor.algorithms.timestepping import TimeStepperInterface
from pymor import defaults
from pymor.core import abstractmethod
from pymor.core.cache import Cachable, cached, DEFAULT_DISK_CONFIG, NO_CACHE_CONFIG
from pymor.discretizations.interfaces import DiscretizationInterface
from pymor.la import induced_norm, VectorArrayInterface
from pymor.tools import selfless_arguments, FrozenDict
from pymor.operators import OperatorInterface, ConstantOperator
from pymor.parameters import Parametric
from pymor.tools import selfless_arguments

class DiscretizationBase(DiscretizationInterface):

    def __init__(self, operators, products=None, estimator=None, visualizer=None, caching='disk', name=None):
        if caching == 'disk':
            Cachable.__init__(self, config=DEFAULT_DISK_CONFIG)
        elif caching == 'none' or not caching:
            Cachable.__init__(self, config=NO_CACHE_CONFIG)
        else:
            raise NotImplementedError
        Parametric.__init__(self)
        self.operators = FrozenDict(operators)
        self.linear = all(op is None or isinstance(op, ConstantOperator) or op.linear for op in operators.itervalues())
        self.products = products
        self.estimator = estimator
        self.visualizer = visualizer
        self.caching = caching
        self.name = name

        if products:
            for k, v in products.iteritems():
                setattr(self, '{}_product'.format(k), v)
                setattr(self, '{}_norm'.format(k), induced_norm(v))
        if estimator is not None:
            self.estimate = self.__estimate
        if visualizer is not None:
            self.visualize = self.__visualize

    @abstractmethod
    def _solve(self, mu=None):
        '''Perform the actual solving.'''
        pass

    @cached
    def solve(self, mu=None):
        '''Solve for a parameter `mu`.

        The result is cached by default.
        '''
        return self._solve(mu)

    def __visualize(self, U):
        self.visualizer.visualize(U, self)

    def __estimate(self, U, mu=None):
        return self.estimator.estimate(U, mu=mu, discretization=self)

class StationaryDiscretization(DiscretizationBase):
    '''Generic class for discretizations of stationary linear problems.

    This class describes discrete problems given by the equation ::

        L_h(μ) ⋅ u_h(μ) = f_h(μ)

    which is to be solved for u_h.

    Parameters
    ----------
    operator
        The operator L_h given as a `LinearOperator`.
    rhs
        The functional f_h given as a `LinearOperator` with `dim_range == 1`.
    visualizer
        A function visualize(U) which visualizes the solution vectors. Can be None,
        in which case no visualization is availabe.
    name
        Name of the discretization.

    Attributes
    ----------
    operator
        The operator L_h. A synonym for operators['operator'].
    operators
        Dictionary of all operators contained in this discretization. The idea is
        that this attribute will be common to all discretizations such that it can
        be used for introspection. Compare the implementation of `reduce_generic_rb`.
        For this class, operators has the keys 'operator' and 'rhs'.
    rhs
        The functional f_h. A synonym for operators['rhs'].
    '''

    def __init__(self, operator, rhs, products=None, parameter_space=None, estimator=None, visualizer=None,
                 caching='disk', name=None):
        assert isinstance(operator, OperatorInterface) and operator.linear
        assert isinstance(rhs, OperatorInterface) and rhs.linear
        assert operator.dim_source == operator.dim_range == rhs.dim_source
        assert rhs.dim_range == 1

        operators = {'operator': operator, 'rhs': rhs}
        super(StationaryDiscretization, self).__init__(operators=operators, products=products,
                                                       estimator=estimator, visualizer=visualizer,
                                                       caching=caching, name=name)
        self.operator = operator
        self.rhs = rhs
        self.operators = operators
        self.solution_dim = operator.dim_range
        self.build_parameter_type(inherits=(operator, rhs))
        self.parameter_space = parameter_space
        self.lock()

    with_arguments = set(selfless_arguments(__init__)).union(['operators'])

    def with_(self, **kwargs):
        assert set(kwargs.keys()) <= self.with_arguments
        assert 'operators' not in kwargs or 'rhs' not in kwargs and 'operator' not in kwargs
        assert 'operators' not in kwargs or set(kwargs['operators'].keys()) <= set(('operator', 'rhs'))

        if 'operators' in kwargs:
            kwargs.update(kwargs.pop('operators'))

        return self._with_via_init(kwargs)

    def _solve(self, mu=None):
        mu = self.parse_parameter(mu)

        # explicitly checking if logging is disabled saves the expensive str(mu) call
        if not self.logging_disabled:
            if self.linear:
                pt = 'sparsity unknown' if getattr(self.operator, 'sparse', None) is None \
                    else ('sparse' if self.operator.sparse else 'dense')
            else:
                pt = 'nonlinear'
            self.logger.info('Solving {} ({}) for {} ...'.format(self.name, pt, mu))

        return self.operator.apply_inverse(self.rhs.as_vector(mu), mu=mu)

class InstationaryDiscretization(DiscretizationBase):

    def __init__(self, T, initial_data, operator, rhs=None, mass=None, time_stepper=None, products=None,
                 parameter_space=None, estimator=None, visualizer=None, caching='disk', name=None):
        assert isinstance(initial_data, (VectorArrayInterface, OperatorInterface))
        assert not isinstance(initial_data, OperatorInterface) or initial_data.dim_source == 0
        assert isinstance(operator, OperatorInterface)
        assert rhs is None or isinstance(rhs, OperatorInterface) and rhs.linear
        assert mass is None or isinstance(mass, OperatorInterface) and mass.linear
        if isinstance(initial_data, VectorArrayInterface):
            initial_data = ConstantOperator(initial_data, name='initial_data')
        assert isinstance(time_stepper, TimeStepperInterface)
        assert operator.dim_source == operator.dim_range == initial_data.dim_range
        assert rhs is None or rhs.dim_source == operator.dim_source and rhs.dim_range == 1
        assert mass is None or mass.dim_source == mass.dim_range == operator.dim_source

        operators = {'initial_data': initial_data, 'operator': operator, 'rhs': rhs, 'mass': mass}
        super(InstationaryDiscretization, self).__init__(operators=operators, products=products,
                                                         estimator=estimator, visualizer=visualizer,
                                                         caching=caching, name=name)
        self.T = T
        self.initial_data = initial_data
        self.operator = operator
        self.rhs = rhs
        self.mass = mass
        self.time_stepper = time_stepper
        self.solution_dim = operator.dim_range
        self.build_parameter_type(inherits=(initial_data, operator, rhs, mass), provides={'_t': 0})
        self.parameter_space = parameter_space

        if hasattr(time_stepper, 'nt'):
            self.with_arguments.add('time_stepper_nt')
        self.lock()

    with_arguments = set(selfless_arguments(__init__)).union(['operators'])

    def with_(self, **kwargs):
        assert set(kwargs.keys()) <= self.with_arguments
        assert 'operators' not in kwargs or not ('rhs' in kwargs or 'operator' in kwargs or 'initial_data' in kwargs or
                                                 'mass' in kwargs)
        assert 'operators' not in kwargs or kwargs['operators'].viewkeys() <= set(('operator', 'rhs', 'initial_data',
                                                                                'mass'))
        assert 'time_stepper_nt' not in kwargs or 'time_stepper' not in kwargs

        if 'time_stepper_nt' in kwargs:
            kwargs['time_stepper'] = self.time_stepper.with_(nt=kwargs.pop('time_stepper_nt'))

        if 'operators' in kwargs:
            kwargs.update(kwargs.pop('operators'))

        return self._with_via_init(kwargs)

    def _solve(self, mu=None):
        mu = self.parse_parameter(mu).copy()

        # explicitly checking if logging is disabled saves the expensive str(mu) call
        if not self.logging_disabled:
            if self.linear:
                pt = 'sparsity unknown' if getattr(self.operator, 'sparse', None) is None \
                    else ('sparse' if self.operator.sparse else 'dense')
            else:
                pt = 'nonlinear'
            self.logger.info('Solving {} ({}) for {} ...'.format(self.name, pt, mu))

        mu['_t'] = 0
        U0 = self.initial_data.apply(self.initial_data.type_source.zeros(0), mu=mu)
        return self.time_stepper.solve(operator=self.operator, rhs=self.rhs, initial_data=U0, mass=self.mass,
                                       initial_time=0, end_time=self.T, mu=mu)
<commit_msg>[discretizations.basic] truely disable caching if asked to<commit_after># -*- coding: utf-8 -*-
# This file is part of the pyMor project (http://www.pymor.org).
# Copyright Holders: Felix Albrecht, Rene Milk, Stephan Rave
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

from __future__ import absolute_import, division, print_function

import numpy as np

from pymor.algorithms.timestepping import TimeStepperInterface
from pymor import defaults
from pymor.core import abstractmethod
from pymor.core.cache import Cachable, cached, DEFAULT_DISK_CONFIG, NO_CACHE_CONFIG
from pymor.discretizations.interfaces import DiscretizationInterface
from pymor.la import induced_norm, VectorArrayInterface
from pymor.tools import selfless_arguments, FrozenDict
from pymor.operators import OperatorInterface, ConstantOperator
from pymor.parameters import Parametric
from pymor.tools import selfless_arguments

class DiscretizationBase(DiscretizationInterface):

    def __init__(self, operators, products=None, estimator=None, visualizer=None, caching='disk', name=None):
        if caching == 'disk':
            Cachable.__init__(self, config=DEFAULT_DISK_CONFIG)
        elif caching == 'none' or not caching:
            Cachable.__init__(self, disable=True)
        else:
            raise NotImplementedError
        Parametric.__init__(self)
        self.operators = FrozenDict(operators)
        self.linear = all(op is None or isinstance(op, ConstantOperator) or op.linear for op in operators.itervalues())
        self.products = products
        self.estimator = estimator
        self.visualizer = visualizer
        self.caching = caching
        self.name = name

        if products:
            for k, v in products.iteritems():
                setattr(self, '{}_product'.format(k), v)
                setattr(self, '{}_norm'.format(k), induced_norm(v))
        if estimator is not None:
            self.estimate = self.__estimate
        if visualizer is not None:
            self.visualize = self.__visualize

    @abstractmethod
    def _solve(self, mu=None):
        '''Perform the actual solving.'''
        pass

    @cached
    def solve(self, mu=None):
        '''Solve for a parameter `mu`.

        The result is cached by default.
        '''
        return self._solve(mu)

    def __visualize(self, U):
        self.visualizer.visualize(U, self)

    def __estimate(self, U, mu=None):
        return self.estimator.estimate(U, mu=mu, discretization=self)

class StationaryDiscretization(DiscretizationBase):
    '''Generic class for discretizations of stationary linear problems.

    This class describes discrete problems given by the equation ::

        L_h(μ) ⋅ u_h(μ) = f_h(μ)

    which is to be solved for u_h.

    Parameters
    ----------
    operator
        The operator L_h given as a `LinearOperator`.
    rhs
        The functional f_h given as a `LinearOperator` with `dim_range == 1`.
    visualizer
        A function visualize(U) which visualizes the solution vectors. Can be None,
        in which case no visualization is availabe.
    name
        Name of the discretization.

    Attributes
    ----------
    operator
        The operator L_h. A synonym for operators['operator'].
    operators
        Dictionary of all operators contained in this discretization. The idea is
        that this attribute will be common to all discretizations such that it can
        be used for introspection. Compare the implementation of `reduce_generic_rb`.
        For this class, operators has the keys 'operator' and 'rhs'.
    rhs
        The functional f_h. A synonym for operators['rhs'].
    '''

    def __init__(self, operator, rhs, products=None, parameter_space=None, estimator=None, visualizer=None,
                 caching='disk', name=None):
        assert isinstance(operator, OperatorInterface) and operator.linear
        assert isinstance(rhs, OperatorInterface) and rhs.linear
        assert operator.dim_source == operator.dim_range == rhs.dim_source
        assert rhs.dim_range == 1

        operators = {'operator': operator, 'rhs': rhs}
        super(StationaryDiscretization, self).__init__(operators=operators, products=products,
                                                       estimator=estimator, visualizer=visualizer,
                                                       caching=caching, name=name)
        self.operator = operator
        self.rhs = rhs
        self.operators = operators
        self.solution_dim = operator.dim_range
        self.build_parameter_type(inherits=(operator, rhs))
        self.parameter_space = parameter_space
        self.lock()

    with_arguments = set(selfless_arguments(__init__)).union(['operators'])

    def with_(self, **kwargs):
        assert set(kwargs.keys()) <= self.with_arguments
        assert 'operators' not in kwargs or 'rhs' not in kwargs and 'operator' not in kwargs
        assert 'operators' not in kwargs or set(kwargs['operators'].keys()) <= set(('operator', 'rhs'))

        if 'operators' in kwargs:
            kwargs.update(kwargs.pop('operators'))

        return self._with_via_init(kwargs)

    def _solve(self, mu=None):
        mu = self.parse_parameter(mu)

        # explicitly checking if logging is disabled saves the expensive str(mu) call
        if not self.logging_disabled:
            if self.linear:
                pt = 'sparsity unknown' if getattr(self.operator, 'sparse', None) is None \
                    else ('sparse' if self.operator.sparse else 'dense')
            else:
                pt = 'nonlinear'
            self.logger.info('Solving {} ({}) for {} ...'.format(self.name, pt, mu))

        return self.operator.apply_inverse(self.rhs.as_vector(mu), mu=mu)

class InstationaryDiscretization(DiscretizationBase):

    def __init__(self, T, initial_data, operator, rhs=None, mass=None, time_stepper=None, products=None,
                 parameter_space=None, estimator=None, visualizer=None, caching='disk', name=None):
        assert isinstance(initial_data, (VectorArrayInterface, OperatorInterface))
        assert not isinstance(initial_data, OperatorInterface) or initial_data.dim_source == 0
        assert isinstance(operator, OperatorInterface)
        assert rhs is None or isinstance(rhs, OperatorInterface) and rhs.linear
        assert mass is None or isinstance(mass, OperatorInterface) and mass.linear
        if isinstance(initial_data, VectorArrayInterface):
            initial_data = ConstantOperator(initial_data, name='initial_data')
        assert isinstance(time_stepper, TimeStepperInterface)
        assert operator.dim_source == operator.dim_range == initial_data.dim_range
        assert rhs is None or rhs.dim_source == operator.dim_source and rhs.dim_range == 1
        assert mass is None or mass.dim_source == mass.dim_range == operator.dim_source

        operators = {'initial_data': initial_data, 'operator': operator, 'rhs': rhs, 'mass': mass}
        super(InstationaryDiscretization, self).__init__(operators=operators, products=products,
                                                         estimator=estimator, visualizer=visualizer,
                                                         caching=caching, name=name)
        self.T = T
        self.initial_data = initial_data
        self.operator = operator
        self.rhs = rhs
        self.mass = mass
        self.time_stepper = time_stepper
        self.solution_dim = operator.dim_range
        self.build_parameter_type(inherits=(initial_data, operator, rhs, mass), provides={'_t': 0})
        self.parameter_space = parameter_space

        if hasattr(time_stepper, 'nt'):
            self.with_arguments.add('time_stepper_nt')
        self.lock()

    with_arguments = set(selfless_arguments(__init__)).union(['operators'])

    def with_(self, **kwargs):
        assert set(kwargs.keys()) <= self.with_arguments
        assert 'operators' not in kwargs or not ('rhs' in kwargs or 'operator' in kwargs or 'initial_data' in kwargs or
                                                 'mass' in kwargs)
        assert 'operators' not in kwargs or kwargs['operators'].viewkeys() <= set(('operator', 'rhs', 'initial_data',
                                                                                'mass'))
        assert 'time_stepper_nt' not in kwargs or 'time_stepper' not in kwargs

        if 'time_stepper_nt' in kwargs:
            kwargs['time_stepper'] = self.time_stepper.with_(nt=kwargs.pop('time_stepper_nt'))

        if 'operators' in kwargs:
            kwargs.update(kwargs.pop('operators'))

        return self._with_via_init(kwargs)

    def _solve(self, mu=None):
        mu = self.parse_parameter(mu).copy()

        # explicitly checking if logging is disabled saves the expensive str(mu) call
        if not self.logging_disabled:
            if self.linear:
                pt = 'sparsity unknown' if getattr(self.operator, 'sparse', None) is None \
                    else ('sparse' if self.operator.sparse else 'dense')
            else:
                pt = 'nonlinear'
            self.logger.info('Solving {} ({}) for {} ...'.format(self.name, pt, mu))

        mu['_t'] = 0
        U0 = self.initial_data.apply(self.initial_data.type_source.zeros(0), mu=mu)
        return self.time_stepper.solve(operator=self.operator, rhs=self.rhs, initial_data=U0, mass=self.mass,
                                       initial_time=0, end_time=self.T, mu=mu)
<|endoftext|>


<commit_before>#############################################################################
##
## Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
## Contact: http://www.qt-project.org/legal
##
## This file is part of Qt Creator.
##
## Commercial License Usage
## Licensees holding valid commercial Qt licenses may use this file in
## accordance with the commercial license agreement provided with the
## Software or, alternatively, in accordance with the terms contained in
## a written agreement between you and Digia.  For licensing terms and
## conditions see http://qt.digia.com/licensing.  For further information
## use the contact form at http://qt.digia.com/contact-us.
##
## GNU Lesser General Public License Usage
## Alternatively, this file may be used under the terms of the GNU Lesser
## General Public License version 2.1 as published by the Free Software
## Foundation and appearing in the file LICENSE.LGPL included in the
## packaging of this file.  Please review the following information to
## ensure the GNU Lesser General Public License version 2.1 requirements
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
##
## In addition, as a special exception, Digia gives you certain additional
## rights.  These rights are described in the Digia Qt LGPL Exception
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
##
#############################################################################

source("../../shared/qtcreator.py")

def addFileToProject(projectPath, category, fileTemplate, fileName):
    __createProjectOrFileSelectType__(category, fileTemplate, isProject=False)
    nameLineEdit = waitForObject("{name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' "
                                 "visible='1'}")
    replaceEditorContent(nameLineEdit, fileName)
    test.compare(waitForObject("{type='Utils::BaseValidatingLineEdit' unnamed='1' visible='1'}").text,
                 projectPath, "Verifying whether path is correct.")
    clickButton(waitForObject(":Next_QPushButton"))
    projCombo = waitForObject("{buddy={name='projectLabel' text='Add to project:' type='QLabel' "
                              "visible='1'} name='projectComboBox' type='QComboBox' visible='1'}")
    proFileName = os.path.basename(projectPath) + ".pro"
    test.verify(not selectFromCombo(projCombo, proFileName), "Verifying project is selected.")
    __createProjectHandleLastPage__()

def main():
    startApplication("qtcreator" + SettingsPath)
    if not startedWithoutPluginError():
        return
    targets = Targets.desktopTargetClasses()
    if not checkDebuggingLibrary(Targets.intToArray(targets)):
        test.fatal("Error while checking debugging libraries - leaving this test.")
        invokeMenuItem("File", "Exit")
        return

    # empty Qt
    workingDir = tempDir()
    projectName, checkedTargets = createEmptyQtProject(workingDir, "EmptyQtProj", targets)
    addFileToProject(os.path.join(workingDir, projectName), "  C++", "C++ Source File", "main.cpp")
    editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
    typeLines(editor, ["int main() {"])
    invokeMenuItem("File", "Save All")
    performDebugging(workingDir, projectName, checkedTargets)
    invokeMenuItem("File", "Close All Projects and Editors")
    # C/C++
    targets = Targets.intToArray(Targets.desktopTargetClasses())
    for name,isC in {"C":True, "CPP":False}.items():
        for singleTarget in targets:
            workingDir = tempDir()
            qtVersion = re.search("\d{3}", Targets.getStringForTarget(singleTarget)).group()
            projectName = createNewNonQtProject(workingDir, "Sample%s%s" % (name, qtVersion),
                                                singleTarget, isC)
            if projectName == None:
                test.fail("Failed to create Sample%s%s" % (name, qtVersion),
                          "Target: %s, plainC: %s" % (Targets.getStringForTargt(singleTarget), isC))
                continue
            editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
            replaceEditorContent(editor, "")
            typeLines(editor, ["int main() {"])
            invokeMenuItem("File", "Save All")
            progressBarWait(15000)
            setRunInTerminal(1, 0, False)
            performDebugging(workingDir, projectName, [singleTarget])
            invokeMenuItem("File", "Close All Projects and Editors")
    invokeMenuItem("File", "Exit")

def __handleAppOutputWaitForDebuggerFinish__():
    ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton")
    appOutput = waitForObject("{type='Core::OutputWindow' visible='1' "
                              "windowTitle='Application Output Window'}")
    test.verify(waitFor("str(appOutput.plainText).endswith('Debugging has finished')", 20000),
                "Verifying whether debugging has finished.")

def performDebugging(workingDir, projectName, checkedTargets):
    # for checking if it's a plain C application (as project names are set to match project type)
    sampleC = re.compile("SampleC\d{3}")
    for kit, config in iterateBuildConfigs(len(checkedTargets), "Debug"):
        test.log("Selecting '%s' as build config" % config)
        selectBuildConfig(len(checkedTargets), kit, config)
        verifyBuildConfig(len(checkedTargets), kit, True)
        progressBarWait(10000)
        invokeMenuItem("Build", "Rebuild All")
        waitForSignal("{type='ProjectExplorer::BuildManager' unnamed='1'}", "buildQueueFinished(bool)")
        isMsvc = isMsvcConfig(len(checkedTargets), kit)
        allowAppThroughWinFW(workingDir, projectName, False)
        clickButton(waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton"))
        handleDebuggerWarnings(config, isMsvc)
        waitForObject(":Qt Creator.DebugModeWidget_QSplitter")
        __handleAppOutputWaitForDebuggerFinish__()
        clickButton(":*Qt Creator.Clear_QToolButton")
        editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
        placeCursorToLine(editor, "int main.*", True)
        type(editor, "<Down>")
        invokeMenuItem("Debug", "Toggle Breakpoint")
        clickButton(waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton"))
        handleDebuggerWarnings(config, isMsvc)
        # on Mac the breakpoint won't get hit if it's a C++ based application and the breakpoint is
        # set to an empty code line inside an empty main
        if platform.system() != "Darwin" or sampleC.match(projectName):
            clickButton(waitForObject(":*Qt Creator.Continue_Core::Internal::FancyToolButton"))
        __handleAppOutputWaitForDebuggerFinish__()
        removeOldBreakpoints()
        deleteAppFromWinFW(workingDir, projectName, False)
<commit_msg>Squish: Let tst_debug_empty_main continue on error<commit_after>#############################################################################
##
## Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
## Contact: http://www.qt-project.org/legal
##
## This file is part of Qt Creator.
##
## Commercial License Usage
## Licensees holding valid commercial Qt licenses may use this file in
## accordance with the commercial license agreement provided with the
## Software or, alternatively, in accordance with the terms contained in
## a written agreement between you and Digia.  For licensing terms and
## conditions see http://qt.digia.com/licensing.  For further information
## use the contact form at http://qt.digia.com/contact-us.
##
## GNU Lesser General Public License Usage
## Alternatively, this file may be used under the terms of the GNU Lesser
## General Public License version 2.1 as published by the Free Software
## Foundation and appearing in the file LICENSE.LGPL included in the
## packaging of this file.  Please review the following information to
## ensure the GNU Lesser General Public License version 2.1 requirements
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
##
## In addition, as a special exception, Digia gives you certain additional
## rights.  These rights are described in the Digia Qt LGPL Exception
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
##
#############################################################################

source("../../shared/qtcreator.py")

def addFileToProject(projectPath, category, fileTemplate, fileName):
    __createProjectOrFileSelectType__(category, fileTemplate, isProject=False)
    nameLineEdit = waitForObject("{name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' "
                                 "visible='1'}")
    replaceEditorContent(nameLineEdit, fileName)
    test.compare(waitForObject("{type='Utils::BaseValidatingLineEdit' unnamed='1' visible='1'}").text,
                 projectPath, "Verifying whether path is correct.")
    clickButton(waitForObject(":Next_QPushButton"))
    projCombo = waitForObject("{buddy={name='projectLabel' text='Add to project:' type='QLabel' "
                              "visible='1'} name='projectComboBox' type='QComboBox' visible='1'}")
    proFileName = os.path.basename(projectPath) + ".pro"
    test.verify(not selectFromCombo(projCombo, proFileName), "Verifying project is selected.")
    __createProjectHandleLastPage__()

def main():
    startApplication("qtcreator" + SettingsPath)
    if not startedWithoutPluginError():
        return
    targets = Targets.desktopTargetClasses()
    if not checkDebuggingLibrary(Targets.intToArray(targets)):
        test.fatal("Error while checking debugging libraries - leaving this test.")
        invokeMenuItem("File", "Exit")
        return

    # empty Qt
    workingDir = tempDir()
    projectName, checkedTargets = createEmptyQtProject(workingDir, "EmptyQtProj", targets)
    addFileToProject(os.path.join(workingDir, projectName), "  C++", "C++ Source File", "main.cpp")
    editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
    typeLines(editor, ["int main() {"])
    invokeMenuItem("File", "Save All")
    performDebugging(workingDir, projectName, checkedTargets)
    invokeMenuItem("File", "Close All Projects and Editors")
    # C/C++
    targets = Targets.intToArray(Targets.desktopTargetClasses())
    for name,isC in {"C":True, "CPP":False}.items():
        for singleTarget in targets:
            workingDir = tempDir()
            qtVersion = re.search("\d{3}", Targets.getStringForTarget(singleTarget)).group()
            projectName = createNewNonQtProject(workingDir, "Sample%s%s" % (name, qtVersion),
                                                singleTarget, isC)
            if projectName == None:
                test.fail("Failed to create Sample%s%s" % (name, qtVersion),
                          "Target: %s, plainC: %s" % (Targets.getStringForTargt(singleTarget), isC))
                continue
            editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
            replaceEditorContent(editor, "")
            typeLines(editor, ["int main() {"])
            invokeMenuItem("File", "Save All")
            progressBarWait(15000)
            setRunInTerminal(1, 0, False)
            performDebugging(workingDir, projectName, [singleTarget])
            invokeMenuItem("File", "Close All Projects and Editors")
    invokeMenuItem("File", "Exit")

def __handleAppOutputWaitForDebuggerFinish__():
    ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton")
    appOutput = waitForObject("{type='Core::OutputWindow' visible='1' "
                              "windowTitle='Application Output Window'}")
    if not test.verify(waitFor("str(appOutput.plainText).endswith('Debugging has finished')", 20000),
                       "Verifying whether debugging has finished."):
        test.log("Aborting debugging to let test continue.")
        invokeMenuItem("Debug", "Abort Debugging")
        waitFor("str(appOutput.plainText).endswith('Debugging has finished')", 5000)

def performDebugging(workingDir, projectName, checkedTargets):
    # for checking if it's a plain C application (as project names are set to match project type)
    sampleC = re.compile("SampleC\d{3}")
    for kit, config in iterateBuildConfigs(len(checkedTargets), "Debug"):
        test.log("Selecting '%s' as build config" % config)
        selectBuildConfig(len(checkedTargets), kit, config)
        verifyBuildConfig(len(checkedTargets), kit, True)
        progressBarWait(10000)
        invokeMenuItem("Build", "Rebuild All")
        waitForSignal("{type='ProjectExplorer::BuildManager' unnamed='1'}", "buildQueueFinished(bool)")
        isMsvc = isMsvcConfig(len(checkedTargets), kit)
        allowAppThroughWinFW(workingDir, projectName, False)
        clickButton(waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton"))
        handleDebuggerWarnings(config, isMsvc)
        waitForObject(":Qt Creator.DebugModeWidget_QSplitter")
        __handleAppOutputWaitForDebuggerFinish__()
        clickButton(":*Qt Creator.Clear_QToolButton")
        editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
        placeCursorToLine(editor, "int main.*", True)
        type(editor, "<Down>")
        invokeMenuItem("Debug", "Toggle Breakpoint")
        clickButton(waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton"))
        handleDebuggerWarnings(config, isMsvc)
        # on Mac the breakpoint won't get hit if it's a C++ based application and the breakpoint is
        # set to an empty code line inside an empty main
        if platform.system() != "Darwin" or sampleC.match(projectName):
            clickButton(waitForObject(":*Qt Creator.Continue_Core::Internal::FancyToolButton"))
        __handleAppOutputWaitForDebuggerFinish__()
        removeOldBreakpoints()
        deleteAppFromWinFW(workingDir, projectName, False)
<|endoftext|>


<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    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 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import wizard
import pooler
import netsvc

form = '''<?xml version="1.0"?>
<form string="Open Invoice">
    <label string="Are you sure you want to open this invoice ?"/>
    <newline/>
    <label string="(Invoice should be unreconciled if you want to open it)"/>
</form>'''

fields = {
}

def _change_inv_state(self, cr, uid, data, context):
    pool_obj = pooler.get_pool(cr.dbname)
    data_inv = pool_obj.get('account.invoice').browse(cr, uid, data['ids'][0])
    if data_inv.reconciled:
        raise wizard.except_wizard(_('Warning'), _('Invoice is already reconciled'))
    wf_service = netsvc.LocalService("workflow")
    res = wf_service.trg_validate(uid, 'account.invoice', data['ids'][0], 'open_test', cr)
    return {}

class wiz_state_open(wizard.interface):
    states = {
        'init': {
            'actions': [],
            'result': {'type':'form', 'arch':form, 'fields':fields, 'state':[('end','No','gtk-no'),('yes','Yes','gtk-yes')]}
        },
        'yes': {
            'actions': [_change_inv_state],
            'result': {'type':'state', 'state':'end'}
        }
    }
wiz_state_open('account.wizard_paid_open')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[FIX] Account : Warning message was missing _ import<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    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 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import wizard
import pooler
import netsvc
from tools.translate import _

form = '''<?xml version="1.0"?>
<form string="Open Invoice">
    <label string="Are you sure you want to open this invoice ?"/>
    <newline/>
    <label string="(Invoice should be unreconciled if you want to open it)"/>
</form>'''

fields = {
}

def _change_inv_state(self, cr, uid, data, context):
    pool_obj = pooler.get_pool(cr.dbname)
    data_inv = pool_obj.get('account.invoice').browse(cr, uid, data['ids'][0])
    if data_inv.reconciled:
        raise wizard.except_wizard(_('Warning'), _('Invoice is already reconciled'))
    wf_service = netsvc.LocalService("workflow")
    res = wf_service.trg_validate(uid, 'account.invoice', data['ids'][0], 'open_test', cr)
    return {}

class wiz_state_open(wizard.interface):
    states = {
        'init': {
            'actions': [],
            'result': {'type':'form', 'arch':form, 'fields':fields, 'state':[('end','No','gtk-no'),('yes','Yes','gtk-yes')]}
        },
        'yes': {
            'actions': [_change_inv_state],
            'result': {'type':'state', 'state':'end'}
        }
    }
wiz_state_open('account.wizard_paid_open')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before>"""
	Sync's doctype and docfields from txt files to database
	perms will get synced only if none exist
"""
import webnotes

def sync_all(force=0):
	modules = []
	modules += sync_core_doctypes(force)
	modules += sync_modules(force)
	try:
		webnotes.conn.begin()
		webnotes.conn.sql("DELETE FROM __CacheItem")
		webnotes.conn.commit()
	except Exception, e:
		if e[0]!=1146: raise e
	return modules

def sync_core_doctypes(force=0):
	import os
	import core
	# doctypes
	return walk_and_sync(os.path.abspath(os.path.dirname(core.__file__)), force)

def sync_modules(force=0):
	import conf
	return walk_and_sync(conf.modules_path, force)

def walk_and_sync(start_path, force=0):
	"""walk and sync all doctypes and pages"""
	import os
	from webnotes.modules import reload_doc

	modules = []

	for path, folders, files in os.walk(start_path):
		for f in files:
			if f.endswith(".txt"):
				# great grand-parent folder is module_name
				module_name = path.split(os.sep)[-3]
				if not module_name in modules:
					modules.append(module_name)

				# grand parent folder is doctype
				doctype = path.split(os.sep)[-2]

				# parent folder is the name
				name = path.split(os.sep)[-1]

				if doctype == 'doctype':
					sync(module_name, name, force)
				elif doctype == 'page':
					reload_doc(module_name, 'page', name)
					print module_name + ' | ' + doctype + ' | ' + name

	return modules

# docname in small letters with underscores
def sync(module_name, docname, force=0):
	with open(get_file_path(module_name, docname), 'r') as f:
		from webnotes.model.utils import peval_doclist
		doclist = peval_doclist(f.read())
		modified = doclist[0]['modified']
		if not doclist:
			raise Exception('DocList could not be evaluated')
		if modified == str(webnotes.conn.get_value(doclist[0].get('doctype'),
			doclist[0].get('name'), 'modified')) and not force:
			return
		webnotes.conn.begin()

		delete_doctype_docfields(doclist)
		save_doctype_docfields(doclist)
		save_perms_if_none_exist(doclist)
		webnotes.conn.sql("""UPDATE `tab{doctype}`
			SET modified=%s WHERE name=%s""".format(doctype=doclist[0]['doctype']),
				(modified, doclist[0]['name']))

		webnotes.conn.commit()
		print module_name, '|', docname

		#raise Exception
		return doclist[0].get('name')

def get_file_path(module_name, docname):
	if not (module_name and docname):
		raise Exception('No Module Name or DocName specified')
	import os
	module = __import__(module_name)
	module_init_path = os.path.abspath(module.__file__)
	module_path = os.sep.join(module_init_path.split(os.sep)[:-1])
	return os.sep.join([module_path, 'doctype', docname, docname + '.txt'])

def delete_doctype_docfields(doclist):
	parent = doclist[0].get('name')
	if not parent: raise Exception('Could not determine parent')
	webnotes.conn.sql("DELETE FROM `tabDocType` WHERE name=%s", parent)
	webnotes.conn.sql("DELETE FROM `tabDocField` WHERE parent=%s", parent)

def save_doctype_docfields(doclist):
	from webnotes.model.doc import Document
	from webnotes.model.code import get_obj
	parent_doc = Document(fielddata=doclist[0])
	parent_doc.save(1, check_links=0,
			ignore_fields=1)
	idx = 1
	for d in doclist:
		if d.get('doctype') != 'DocField': continue
		d['idx'] = idx
		Document(fielddata=d).save(1, check_links=0, ignore_fields=1)
		idx += 1

	update_schema(parent_doc.name)

def update_schema(docname):
	from webnotes.model.db_schema import updatedb
	updatedb(docname)

	from webnotes.utils.cache import CacheItem
	CacheItem(docname).clear()

def save_perms_if_none_exist(doclist):
	res = webnotes.conn.sql("""SELECT name FROM `tabDocPerm`
			WHERE parent=%s""", doclist[0].get('name'))
	if res and res[0][0]: return

	from webnotes.model.doc import Document
	for d in doclist:
		if d.get('doctype') != 'DocPerm': continue
		Document(fielddata=d).save(1, check_links=0, ignore_fields=1)

def sync_install(force=1):
	# sync all doctypes
	modules = sync_all(force)

	# load install docs
	load_install_docs(modules)

def load_install_docs(modules):
	import os
	if isinstance(modules, basestring): modules = [modules]

	for module_name in modules:
		module = __import__(module_name)
		if hasattr(module, 'install_docs'):
			webnotes.conn.begin()

			for data in module.install_docs:
				if data.get('name'):
					if not webnotes.conn.exists(data['doctype'], data.get('name')):
						create_doc(data)
				elif not webnotes.conn.exists(data):
					create_doc(data)

			webnotes.conn.commit()

		if hasattr(module, 'module_init'):
			module.module_init()

def create_doc(data):
	from webnotes.model.doc import Document
	d = Document(data['doctype'])
	d.fields.update(data)
	d.save()
	print 'Created %(doctype)s %(name)s' % d.fields

import unittest
class TestSync(unittest.TestCase):
	def setUp(self):
		self.test_case = {
			'module_name': 'selling',
			'docname': 'sales_order'
		}
		webnotes.conn.begin()

	def tearDown(self):
		pass
		#from webnotes.utils import cstr
		#webnotes.conn.rollback()

	def test_sync(self):
		#old_doctype, old_docfields = self.query('Profile')
		#self.parent = sync(self.test_case.get('module_name'), self.test_case.get('docname'))
		#new_doctype, new_docfields = self.query(self.parent)
		#self.assertNotEqual(old_doctype, new_doctype)
		#self.assertNotEqual(old_docfields, new_docfields)

		#from webnotes.utils import cstr
		#print new_doctype[0]
		#print
		#print "\n".join((cstr(d) for d in new_docfields))
		#print "\n\n"
		pass

	def test_sync_all(self):
		sync_all()

	def query(self, parent):
		doctype = webnotes.conn.sql("SELECT name FROM `tabDocType` \
			WHERE name=%s", parent)
		docfields = webnotes.conn.sql("SELECT idx, fieldname, label, fieldtype FROM `tabDocField` \
			WHERE parent=%s order by idx", parent)
		#doctype = webnotes.conn.sql("SELECT * FROM `tabDocType` \
		#	WHERE name=%s", parent)
		#docfields = webnotes.conn.sql("SELECT * FROM `tabDocField` \
		#	WHERE parent=%s order by idx", parent)
		return doctype, docfields

<commit_msg>reload search criteria, print format and doctype mapper through sync_all<commit_after>"""
	Sync's doctype and docfields from txt files to database
	perms will get synced only if none exist
"""
import webnotes

def sync_all(force=0):
	modules = []
	modules += sync_core_doctypes(force)
	modules += sync_modules(force)
	try:
		webnotes.conn.begin()
		webnotes.conn.sql("DELETE FROM __CacheItem")
		webnotes.conn.commit()
	except Exception, e:
		if e[0]!=1146: raise e
	return modules

def sync_core_doctypes(force=0):
	import os
	import core
	# doctypes
	return walk_and_sync(os.path.abspath(os.path.dirname(core.__file__)), force)

def sync_modules(force=0):
	import conf
	return walk_and_sync(conf.modules_path, force)

def walk_and_sync(start_path, force=0):
	"""walk and sync all doctypes and pages"""
	import os
	from webnotes.modules import reload_doc

	modules = []

	for path, folders, files in os.walk(start_path):
		for f in files:
			if f.endswith(".txt"):
				# great grand-parent folder is module_name
				module_name = path.split(os.sep)[-3]
				if not module_name in modules:
					modules.append(module_name)

				# grand parent folder is doctype
				doctype = path.split(os.sep)[-2]

				# parent folder is the name
				name = path.split(os.sep)[-1]

				if doctype == 'doctype':
					sync(module_name, name, force)
				elif doctype in ['page', 'search_criteria', 'Print Format', 'DocType Mapper']:
					reload_doc(module_name, doctype, name)
					print module_name + ' | ' + doctype + ' | ' + name

	return modules

# docname in small letters with underscores
def sync(module_name, docname, force=0):
	with open(get_file_path(module_name, docname), 'r') as f:
		from webnotes.model.utils import peval_doclist
		doclist = peval_doclist(f.read())
		modified = doclist[0]['modified']
		if not doclist:
			raise Exception('DocList could not be evaluated')
		if modified == str(webnotes.conn.get_value(doclist[0].get('doctype'),
			doclist[0].get('name'), 'modified')) and not force:
			return
		webnotes.conn.begin()

		delete_doctype_docfields(doclist)
		save_doctype_docfields(doclist)
		save_perms_if_none_exist(doclist)
		webnotes.conn.sql("""UPDATE `tab{doctype}`
			SET modified=%s WHERE name=%s""".format(doctype=doclist[0]['doctype']),
				(modified, doclist[0]['name']))

		webnotes.conn.commit()
		print module_name, '|', docname

		#raise Exception
		return doclist[0].get('name')

def get_file_path(module_name, docname):
	if not (module_name and docname):
		raise Exception('No Module Name or DocName specified')
	import os
	module = __import__(module_name)
	module_init_path = os.path.abspath(module.__file__)
	module_path = os.sep.join(module_init_path.split(os.sep)[:-1])
	return os.sep.join([module_path, 'doctype', docname, docname + '.txt'])

def delete_doctype_docfields(doclist):
	parent = doclist[0].get('name')
	if not parent: raise Exception('Could not determine parent')
	webnotes.conn.sql("DELETE FROM `tabDocType` WHERE name=%s", parent)
	webnotes.conn.sql("DELETE FROM `tabDocField` WHERE parent=%s", parent)

def save_doctype_docfields(doclist):
	from webnotes.model.doc import Document
	from webnotes.model.code import get_obj
	parent_doc = Document(fielddata=doclist[0])
	parent_doc.save(1, check_links=0,
			ignore_fields=1)
	idx = 1
	for d in doclist:
		if d.get('doctype') != 'DocField': continue
		d['idx'] = idx
		Document(fielddata=d).save(1, check_links=0, ignore_fields=1)
		idx += 1

	update_schema(parent_doc.name)

def update_schema(docname):
	from webnotes.model.db_schema import updatedb
	updatedb(docname)

	from webnotes.utils.cache import CacheItem
	CacheItem(docname).clear()

def save_perms_if_none_exist(doclist):
	res = webnotes.conn.sql("""SELECT name FROM `tabDocPerm`
			WHERE parent=%s""", doclist[0].get('name'))
	if res and res[0][0]: return

	from webnotes.model.doc import Document
	for d in doclist:
		if d.get('doctype') != 'DocPerm': continue
		Document(fielddata=d).save(1, check_links=0, ignore_fields=1)

def sync_install(force=1):
	# sync all doctypes
	modules = sync_all(force)

	# load install docs
	load_install_docs(modules)

def load_install_docs(modules):
	import os
	if isinstance(modules, basestring): modules = [modules]

	for module_name in modules:
		module = __import__(module_name)
		if hasattr(module, 'install_docs'):
			webnotes.conn.begin()

			for data in module.install_docs:
				if data.get('name'):
					if not webnotes.conn.exists(data['doctype'], data.get('name')):
						create_doc(data)
				elif not webnotes.conn.exists(data):
					create_doc(data)

			webnotes.conn.commit()

		if hasattr(module, 'module_init'):
			module.module_init()

def create_doc(data):
	from webnotes.model.doc import Document
	d = Document(data['doctype'])
	d.fields.update(data)
	d.save()
	print 'Created %(doctype)s %(name)s' % d.fields

import unittest
class TestSync(unittest.TestCase):
	def setUp(self):
		self.test_case = {
			'module_name': 'selling',
			'docname': 'sales_order'
		}
		webnotes.conn.begin()

	def tearDown(self):
		pass
		#from webnotes.utils import cstr
		#webnotes.conn.rollback()

	def test_sync(self):
		#old_doctype, old_docfields = self.query('Profile')
		#self.parent = sync(self.test_case.get('module_name'), self.test_case.get('docname'))
		#new_doctype, new_docfields = self.query(self.parent)
		#self.assertNotEqual(old_doctype, new_doctype)
		#self.assertNotEqual(old_docfields, new_docfields)

		#from webnotes.utils import cstr
		#print new_doctype[0]
		#print
		#print "\n".join((cstr(d) for d in new_docfields))
		#print "\n\n"
		pass

	def test_sync_all(self):
		sync_all()

	def query(self, parent):
		doctype = webnotes.conn.sql("SELECT name FROM `tabDocType` \
			WHERE name=%s", parent)
		docfields = webnotes.conn.sql("SELECT idx, fieldname, label, fieldtype FROM `tabDocField` \
			WHERE parent=%s order by idx", parent)
		#doctype = webnotes.conn.sql("SELECT * FROM `tabDocType` \
		#	WHERE name=%s", parent)
		#docfields = webnotes.conn.sql("SELECT * FROM `tabDocField` \
		#	WHERE parent=%s order by idx", parent)
		return doctype, docfields

<|endoftext|>