<commit_before>""" Module for interfacing with the PostGIS database

.. module:: dbio
   :synopsis: Definition of the DBIO module

.. moduleauthor:: Kostas Andreadis <kandread@jpl.nasa.gov>

"""

import numpy as np
import tempfile
from osgeo import gdal, osr
import subprocess
import random
import psycopg2 as pg
import string
import rpath
import sys

def connect(dbname):
    """Connect to database *dbname*."""
    try:
        db = pg.connect(database=dbname)
    except pg.OperationalError:
        db = None
        try:
            db = pg.connect(database=dbname, host="/tmp/")
        except:
            print("Cannot connect to database {0}. Please restart it by running \n {1}/pg_ctl -D {2}/postgres restart".format(
                dbname, rpath.bins, rpath.data))
            sys.exit()
    return db

def writeGeotif(lat, lon, res, data, filename=None):
    """Writes Geotif in temporary directory so it can be imported into the PostGIS database."""
    if isinstance(data, np.ma.masked_array):
        nodata = np.double(data.fill_value)
        data = data.data
    else:
        nodata = -9999.
    if len(data.shape) < 2:
        nrows = int((max(lat) - min(lat)) / res) + 1
        ncols = int((max(lon) - min(lon)) / res) + 1
        out = np.zeros((nrows, ncols)) + nodata
        for c in range(len(lat)):
            i = int((max(lat) - lat[c]) / res)
            j = int((lon[c] - min(lon)) / res)
            out[i, j] = data[c]
    else:
        nrows, ncols = data.shape
        out = data
    if filename is None:
        f = tempfile.NamedTemporaryFile(suffix=".tif", delete=False)
        filename = f.name
        f.close()
    driver = gdal.GetDriverByName("GTiff")
    ods = driver.Create(filename, ncols, nrows, 1, gdal.GDT_Float32)
    ods.SetGeoTransform([min(lon) - res / 2.0, res, 0,
                         max(lat) + res / 2.0, 0, -res])
    srs = osr.SpatialReference()
    srs.SetWellKnownGeogCS("WGS84")
    ods.SetProjection(srs.ExportToWkt())
    ods.GetRasterBand(1).WriteArray(out)
    ods.GetRasterBand(1).SetNoDataValue(nodata)
    ods = None
    return filename

def _deleteRasters(dbname, tablename, dt):
    """If date already exists delete associated rasters before ingesting."""
    db = connect(dbname)
    cur = db.cursor()
    sql = "select * from {0} where fdate='{1}'".format(tablename, dt.strftime("%Y-%m-%d"))
    cur.execute(sql)
    if bool(cur.rowcount):
        print("WARNING! Overwriting raster in {0} table for {1}".format(tablename, dt.strftime("%Y-%m-%d")))
        cur.execute("delete from {0} where fdate='{1}'".format(tablename, dt.strftime("%Y-%m-%d")))
        db.commit()
    cur.close()
    db.close()

def _getResamplingMethod(dbname, tablename, res):
    """Return a raster resampling method based on the resolution of the model and the requested datasets."""
    db = connect(dbname)
    cur = db.cursor()
    cur.execute(
        "select st_pixelheight(rast) from {0} limit 1".format(tablename))
    data_res = cur.fetchone()[0]
    if res == data_res:
        resample_method = "near"
    elif res < data_res:
        resample_method = "bilinear"
    else:
        resample_method = "average"
    cur.close()
    db.close()
    return resample_method

def _createRasterTable(dbname, stname):
    """Create table *stname* holding rasters in database *dbname*."""
    db = connect(dbname)
    cur = db.cursor()
    cur.execute(
        "create table {0} (rid serial primary key, rast raster, fdate date not null)".format(stname))
    db.commit()
    cur.close()
    db.close()

def _createResampledViews(dbname, sname, tname, temptable, dt, tilesize, overwrite):
    """Cache resampled tables by using materialized views."""
    db = connect(dbname)
    cur = db.cursor()
    # create catalog that holds information on resampled rasters
    sql = """create or replace function resampled(_s text, _t text, out result double precision) as
    $func$
    begin
    execute format('select st_scalex(rast) from %s.%s limit 1',quote_ident(_s),quote_ident(_t)) into result;
    end
    $func$ language plpgsql;"""
    cur.execute(sql)
    cur.execute("create or replace view raster_resampled as (select r_table_schema as sname,r_table_name as tname,resampled(r_table_schema,r_table_name) as resolution from raster_columns)")
    # create or update materialized view for each resolution available to VIC
    cur.execute("select distinct(resolution) from vic.soils")
    if bool(cur.rowcount):
        resolutions = [r[0] for r in cur.fetchall()]
        for res in resolutions:
            # check if view exists
            cur.execute(
                "select * from pg_catalog.pg_class c inner join pg_catalog.pg_namespace n on c.relnamespace=n.oid where n.nspname='{0}' and c.relname='{1}_{2}'".format(sname, tname, int(1.0 / res)))
            method = _getResamplingMethod(
                dbname, "{0}.{1}".format(sname, tname), res)
            # if it exists insert data, if not create it
            if bool(cur.rowcount):
                    # check if date already exists and delete it before ingesting
                if overwrite:
                    _deleteRasters(dbname, "{0}.{1}_{2}".format(sname, tname, int(1.0 / res)), dt)
                sql = "insert into {0}.{1}_{2} (with dt as (select max(fdate) as maxdate from {0}.{1}_{2}), f as (select fdate,st_tile(st_rescale(rast,{3},'{4}'),{5},{6}) as rast from {0}.{1},dt where fdate>maxdate) select fdate,rast,dense_rank() over (order by st_upperleftx(rast),st_upperlefty(rast)) as rid from f)".format(sname, tname, int(1.0 / res), res, method, tilesize[0], tilesize[1])
                cur.execute(sql)
                # cur.execute("refresh materialized view {0}.{1}_{2}".format(
                #     sname, tname, int(1.0 / res)))
            else:
                sql = "create table {0}.{1}_{2} as (with f as (select fdate,st_tile(st_rescale(rast,{3},'{4}'),{5},{6}) as rast from {0}.{1}) select fdate,rast,dense_rank() over (order by st_upperleftx(rast),st_upperlefty(rast)) as rid from f)".format(
                    sname, tname, int(1.0 / res), res, method, tilesize[0], tilesize[1])
                # sql = "create materialized view {0}.{1}_{2} as (with f as (select fdate,st_tile(st_rescale(rast,{3},'{4}'),{5},{6}) as rast from {0}.{1}) select fdate,rast,dense_rank() over (order by st_upperleftx(rast),st_upperlefty(rast)) as rid from f)".format(
                #     sname, tname, int(1.0 / res), res, method, tilesize[0], tilesize[1])
                cur.execute(sql)
                cur.execute("create index {1}_{2}_t on {0}.{1}_{2}(fdate)".format(
                    sname, tname, int(1.0 / res)))
                cur.execute("create index {1}_{2}_r on {0}.{1}_{2}(rid)".format(
                    sname, tname, int(1.0 / res)))
        db.commit()

def ingest(dbname, filename, dt, stname, resample=True, overwrite=True):
    """Imports Geotif *filename* into database *db*."""
    tilesize = (10, 10)
    db = connect(dbname)
    cur = db.cursor()
    # import temporary table
    temptable = ''.join(random.SystemRandom().choice(
        string.ascii_letters) for _ in range(8))
    subprocess.call("{3}/raster2pgsql -d -s 4326 {0} {2} | {3}/psql -d {1}".format(
        filename, dbname, temptable, rpath.bins), shell=True)
    cur.execute("alter table {0} add column fdate date".format(temptable))
    cur.execute(
        "update {3} set fdate = date '{0}-{1}-{2}'".format(dt.year, dt.month, dt.day, temptable))
    # check if table exists
    schemaname, tablename = stname.split(".")
    cur.execute(
        "select * from information_schema.schemata where schema_name='{0}'".format(schemaname))
    if not bool(cur.rowcount):
        cur.execute("create schema {0}".format(schemaname))
    cur.execute(
        "select * from information_schema.tables where table_schema='{0}' and table_name='{1}'".format(schemaname, tablename))
    if not bool(cur.rowcount):
        _createRasterTable(dbname, stname)
    # check if date already exists and delete it before ingesting
    if overwrite:
        _deleteRasters(dbname, "{0}.{1}".format(schemaname, tablename), dt)
    # create tiles from imported raster and insert into table
    cur.execute("insert into {0}.{1} (fdate,rast) select fdate,rast from {2}".format(
        schemaname, tablename, temptable))
    # create indexes for table
    cur.execute("drop index if exists {0}.{1}_t".format(schemaname, tablename))
    cur.execute("create index {1}_t on {0}.{1}(fdate)".format(
        schemaname, tablename))
    db.commit()
    # create materialized views for resampled rasters
    if resample:
        _createResampledViews(dbname, schemaname,
                              tablename, temptable, dt, tilesize, overwrite)
    # delete temporary table
    cur.execute("drop table {0}".format(temptable))
    db.commit()
    cur.close()
    db.close()
<commit_msg>Moved date index creation in separate function.<commit_after>""" Module for interfacing with the PostGIS database

.. module:: dbio
   :synopsis: Definition of the DBIO module

.. moduleauthor:: Kostas Andreadis <kandread@jpl.nasa.gov>

"""

import numpy as np
import tempfile
from osgeo import gdal, osr
import subprocess
import random
import psycopg2 as pg
import string
import rpath
import sys

def connect(dbname):
    """Connect to database *dbname*."""
    try:
        db = pg.connect(database=dbname)
    except pg.OperationalError:
        db = None
        try:
            db = pg.connect(database=dbname, host="/tmp/")
        except:
            print("Cannot connect to database {0}. Please restart it by running \n {1}/pg_ctl -D {2}/postgres restart".format(
                dbname, rpath.bins, rpath.data))
            sys.exit()
    return db

def writeGeotif(lat, lon, res, data, filename=None):
    """Writes Geotif in temporary directory so it can be imported into the PostGIS database."""
    if isinstance(data, np.ma.masked_array):
        nodata = np.double(data.fill_value)
        data = data.data
    else:
        nodata = -9999.
    if len(data.shape) < 2:
        nrows = int((max(lat) - min(lat)) / res) + 1
        ncols = int((max(lon) - min(lon)) / res) + 1
        out = np.zeros((nrows, ncols)) + nodata
        for c in range(len(lat)):
            i = int((max(lat) - lat[c]) / res)
            j = int((lon[c] - min(lon)) / res)
            out[i, j] = data[c]
    else:
        nrows, ncols = data.shape
        out = data
    if filename is None:
        f = tempfile.NamedTemporaryFile(suffix=".tif", delete=False)
        filename = f.name
        f.close()
    driver = gdal.GetDriverByName("GTiff")
    ods = driver.Create(filename, ncols, nrows, 1, gdal.GDT_Float32)
    ods.SetGeoTransform([min(lon) - res / 2.0, res, 0,
                         max(lat) + res / 2.0, 0, -res])
    srs = osr.SpatialReference()
    srs.SetWellKnownGeogCS("WGS84")
    ods.SetProjection(srs.ExportToWkt())
    ods.GetRasterBand(1).WriteArray(out)
    ods.GetRasterBand(1).SetNoDataValue(nodata)
    ods = None
    return filename

def _deleteRasters(dbname, tablename, dt):
    """If date already exists delete associated rasters before ingesting."""
    db = connect(dbname)
    cur = db.cursor()
    sql = "select * from {0} where fdate='{1}'".format(tablename, dt.strftime("%Y-%m-%d"))
    cur.execute(sql)
    if bool(cur.rowcount):
        print("WARNING! Overwriting raster in {0} table for {1}".format(tablename, dt.strftime("%Y-%m-%d")))
        cur.execute("delete from {0} where fdate='{1}'".format(tablename, dt.strftime("%Y-%m-%d")))
        db.commit()
    cur.close()
    db.close()

def _getResamplingMethod(dbname, tablename, res):
    """Return a raster resampling method based on the resolution of the model and the requested datasets."""
    db = connect(dbname)
    cur = db.cursor()
    cur.execute(
        "select st_pixelheight(rast) from {0} limit 1".format(tablename))
    data_res = cur.fetchone()[0]
    if res == data_res:
        resample_method = "near"
    elif res < data_res:
        resample_method = "bilinear"
    else:
        resample_method = "average"
    cur.close()
    db.close()
    return resample_method

def _createRasterTable(dbname, stname):
    """Create table *stname* holding rasters in database *dbname*."""
    db = connect(dbname)
    cur = db.cursor()
    cur.execute(
        "create table {0} (rid serial primary key, rast raster, fdate date not null)".format(stname))
    db.commit()
    cur.close()
    db.close()

def _createDateIndex(dbname, schemaname, tablename):
    """Create table index based on date column."""
    db = connect(dbname)
    cur = db.cursor()
    cur.execute("create index {1}_t on {0}.{1}(fdate)".format(schemaname, tablename))
    db.commit()
    cur.close()
    db.close()

def _createResampledViews(dbname, sname, tname, temptable, dt, tilesize, overwrite):
    """Cache resampled tables by using materialized views."""
    db = connect(dbname)
    cur = db.cursor()
    # create catalog that holds information on resampled rasters
    sql = """create or replace function resampled(_s text, _t text, out result double precision) as
    $func$
    begin
    execute format('select st_scalex(rast) from %s.%s limit 1',quote_ident(_s),quote_ident(_t)) into result;
    end
    $func$ language plpgsql;"""
    cur.execute(sql)
    cur.execute("create or replace view raster_resampled as (select r_table_schema as sname,r_table_name as tname,resampled(r_table_schema,r_table_name) as resolution from raster_columns)")
    # create or update materialized view for each resolution available to VIC
    cur.execute("select distinct(resolution) from vic.soils")
    if bool(cur.rowcount):
        resolutions = [r[0] for r in cur.fetchall()]
        for res in resolutions:
            # check if view exists
            cur.execute(
                "select * from pg_catalog.pg_class c inner join pg_catalog.pg_namespace n on c.relnamespace=n.oid where n.nspname='{0}' and c.relname='{1}_{2}'".format(sname, tname, int(1.0 / res)))
            method = _getResamplingMethod(
                dbname, "{0}.{1}".format(sname, tname), res)
            # if it exists insert data, if not create it
            if bool(cur.rowcount):
                    # check if date already exists and delete it before ingesting
                if overwrite:
                    _deleteRasters(dbname, "{0}.{1}_{2}".format(sname, tname, int(1.0 / res)), dt)
                sql = "insert into {0}.{1}_{2} (with dt as (select max(fdate) as maxdate from {0}.{1}_{2}), f as (select fdate,st_tile(st_rescale(rast,{3},'{4}'),{5},{6}) as rast from {0}.{1},dt where fdate>maxdate) select fdate,rast,dense_rank() over (order by st_upperleftx(rast),st_upperlefty(rast)) as rid from f)".format(sname, tname, int(1.0 / res), res, method, tilesize[0], tilesize[1])
                cur.execute(sql)
                # cur.execute("refresh materialized view {0}.{1}_{2}".format(
                #     sname, tname, int(1.0 / res)))
            else:
                sql = "create table {0}.{1}_{2} as (with f as (select fdate,st_tile(st_rescale(rast,{3},'{4}'),{5},{6}) as rast from {0}.{1}) select fdate,rast,dense_rank() over (order by st_upperleftx(rast),st_upperlefty(rast)) as rid from f)".format(
                    sname, tname, int(1.0 / res), res, method, tilesize[0], tilesize[1])
                # sql = "create materialized view {0}.{1}_{2} as (with f as (select fdate,st_tile(st_rescale(rast,{3},'{4}'),{5},{6}) as rast from {0}.{1}) select fdate,rast,dense_rank() over (order by st_upperleftx(rast),st_upperlefty(rast)) as rid from f)".format(
                #     sname, tname, int(1.0 / res), res, method, tilesize[0], tilesize[1])
                cur.execute(sql)
                cur.execute("create index {1}_{2}_t on {0}.{1}_{2}(fdate)".format(
                    sname, tname, int(1.0 / res)))
                cur.execute("create index {1}_{2}_r on {0}.{1}_{2}(rid)".format(
                    sname, tname, int(1.0 / res)))
        db.commit()

def ingest(dbname, filename, dt, stname, resample=True, overwrite=True):
    """Imports Geotif *filename* into database *db*."""
    tilesize = (10, 10)
    db = connect(dbname)
    cur = db.cursor()
    # import temporary table
    temptable = ''.join(random.SystemRandom().choice(
        string.ascii_letters) for _ in range(8))
    subprocess.call("{3}/raster2pgsql -d -s 4326 {0} {2} | {3}/psql -d {1}".format(
        filename, dbname, temptable, rpath.bins), shell=True)
    cur.execute("alter table {0} add column fdate date".format(temptable))
    cur.execute(
        "update {3} set fdate = date '{0}-{1}-{2}'".format(dt.year, dt.month, dt.day, temptable))
    # check if table exists
    schemaname, tablename = stname.split(".")
    cur.execute(
        "select * from information_schema.schemata where schema_name='{0}'".format(schemaname))
    if not bool(cur.rowcount):
        cur.execute("create schema {0}".format(schemaname))
    cur.execute(
        "select * from information_schema.tables where table_schema='{0}' and table_name='{1}'".format(schemaname, tablename))
    if not bool(cur.rowcount):
        _createRasterTable(dbname, stname)
        _createDateIndex(schemaname, tablename)
    # check if date already exists and delete it before ingesting
    if overwrite:
        _deleteRasters(dbname, "{0}.{1}".format(schemaname, tablename), dt)
    # create tiles from imported raster and insert into table
    cur.execute("insert into {0}.{1} (fdate,rast) select fdate,rast from {2}".format(
        schemaname, tablename, temptable))
    # create indexes for table
    # cur.execute("drop index if exists {0}.{1}_t".format(schemaname, tablename))
    # cur.execute("create index {1}_t on {0}.{1}(fdate)".format(
    #     schemaname, tablename))
    db.commit()
    # create materialized views for resampled rasters
    if resample:
        _createResampledViews(dbname, schemaname,
                              tablename, temptable, dt, tilesize, overwrite)
    # delete temporary table
    cur.execute("drop table {0}".format(temptable))
    db.commit()
    cur.close()
    db.close()
<|endoftext|>


<commit_before># Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.

from UM.Job import Job
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Application import Application
from UM.Mesh.MeshData import MeshData

from UM.Message import Message
from UM.i18n import i18nCatalog

from cura import LayerData
from cura import LayerDataDecorator

import numpy
import struct

catalog = i18nCatalog("cura")

class ProcessSlicedObjectListJob(Job):
    def __init__(self, message):
        super().__init__()
        self._message = message
        self._scene = Application.getInstance().getController().getScene()
        self._progress = None
        self._abortRequested = False

    ##  Aborts the processing of layers.
    #
    #   This abort is made on a best-effort basis, meaning that the actual
    #   job thread will check once in a while to see whether an abort is
    #   requested and then stop processing by itself. There is no guarantee
    #   that the abort will stop the job any time soon or even at all.
    def abort(self):
        self._abortRequested = True

    def run(self):
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()
            Job.yieldThread()
            if self._abortRequested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        object_id_map = {}
        new_node = SceneNode()
        ## Put all nodes in a dict identified by ID
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
                else:
                    object_id_map[id(node)] = node
            Job.yieldThread()
            if self._abortRequested:
                if self._progress:
                    self._progress.hide()
                return

        settings = Application.getInstance().getMachineManager().getWorkingProfile()

        center = None
        if not settings.getSettingValue("machine_center_is_zero"):
            center = numpy.array([settings.getSettingValue("machine_width") / 2, 0.0, -settings.getSettingValue("machine_depth") / 2])
        else:
            center = numpy.array([0.0, 0.0, 0.0])

        mesh = MeshData()
        layer_data = LayerData.LayerData()

        layer_count = 0
        for i in range(self._message.repeatedMessageCount("objects")):
            layer_count += self._message.getRepeatedMessage("objects", i).repeatedMessageCount("layers")

        current_layer = 0
        for i in range(self._message.repeatedMessageCount("objects")):
            object = self._message.getRepeatedMessage("objects", i)
            try:
                node = object_id_map[object.id]
            except KeyError:
                continue

            for l in range(object.repeatedMessageCount("layers")):
                layer = object.getRepeatedMessage("layers", l)

                layer_data.addLayer(layer.id)
                layer_data.setLayerHeight(layer.id, layer.height)
                layer_data.setLayerThickness(layer.id, layer.thickness)

                for p in range(layer.repeatedMessageCount("polygons")):
                    polygon = layer.getRepeatedMessage("polygons", p)

                    points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
                    points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                    points = numpy.asarray(points, dtype=numpy.float32)
                    points /= 1000
                    points = numpy.insert(points, 1, (layer.height / 1000), axis = 1)

                    points[:,2] *= -1

                    points -= center

                    layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width)

                current_layer += 1
                progress = (current_layer / layer_count) * 100
                # TODO: Rebuild the layer data mesh once the layer has been processed.
                # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

                if self._abortRequested:
                    if self._progress:
                        self._progress.hide()
                    return
                if self._progress:
                    self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._abortRequested:
            if self._progress:
                self._progress.hide()
            return

        #Add layerdata decorator to scene node to indicate that the node has layerdata
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot()) #Note: After this we can no longer abort!

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()

    def _onActiveViewChanged(self):
        if self.isRunning():
            if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
                if not self._progress:
                    self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, 0)
                    self._progress.show()
            else:
                if self._progress:
                    self._progress.hide()

<commit_msg>Added more yield thread to prevent GIL lockdown<commit_after># Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.

from UM.Job import Job
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Application import Application
from UM.Mesh.MeshData import MeshData

from UM.Message import Message
from UM.i18n import i18nCatalog

from cura import LayerData
from cura import LayerDataDecorator

import numpy
import struct

catalog = i18nCatalog("cura")

class ProcessSlicedObjectListJob(Job):
    def __init__(self, message):
        super().__init__()
        self._message = message
        self._scene = Application.getInstance().getController().getScene()
        self._progress = None
        self._abortRequested = False

    ##  Aborts the processing of layers.
    #
    #   This abort is made on a best-effort basis, meaning that the actual
    #   job thread will check once in a while to see whether an abort is
    #   requested and then stop processing by itself. There is no guarantee
    #   that the abort will stop the job any time soon or even at all.
    def abort(self):
        self._abortRequested = True

    def run(self):
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()
            Job.yieldThread()
            if self._abortRequested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        object_id_map = {}
        new_node = SceneNode()
        ## Put all nodes in a dict identified by ID
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
                else:
                    object_id_map[id(node)] = node
            Job.yieldThread()
            if self._abortRequested:
                if self._progress:
                    self._progress.hide()
                return

        settings = Application.getInstance().getMachineManager().getWorkingProfile()

        center = None
        if not settings.getSettingValue("machine_center_is_zero"):
            center = numpy.array([settings.getSettingValue("machine_width") / 2, 0.0, -settings.getSettingValue("machine_depth") / 2])
        else:
            center = numpy.array([0.0, 0.0, 0.0])

        mesh = MeshData()
        layer_data = LayerData.LayerData()

        layer_count = 0
        for i in range(self._message.repeatedMessageCount("objects")):
            layer_count += self._message.getRepeatedMessage("objects", i).repeatedMessageCount("layers")

        current_layer = 0
        for i in range(self._message.repeatedMessageCount("objects")):
            object = self._message.getRepeatedMessage("objects", i)
            try:
                node = object_id_map[object.id]
            except KeyError:
                continue

            for l in range(object.repeatedMessageCount("layers")):
                layer = object.getRepeatedMessage("layers", l)

                layer_data.addLayer(layer.id)
                layer_data.setLayerHeight(layer.id, layer.height)
                layer_data.setLayerThickness(layer.id, layer.thickness)

                for p in range(layer.repeatedMessageCount("polygons")):
                    polygon = layer.getRepeatedMessage("polygons", p)

                    points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
                    points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                    points = numpy.asarray(points, dtype=numpy.float32)
                    points /= 1000
                    points = numpy.insert(points, 1, (layer.height / 1000), axis = 1)

                    points[:,2] *= -1

                    points -= center

                    layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width)
                    Job.yieldThread()
                Job.yieldThread()
                current_layer += 1
                progress = (current_layer / layer_count) * 100
                # TODO: Rebuild the layer data mesh once the layer has been processed.
                # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

                if self._abortRequested:
                    if self._progress:
                        self._progress.hide()
                    return
                if self._progress:
                    self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._abortRequested:
            if self._progress:
                self._progress.hide()
            return

        #Add layerdata decorator to scene node to indicate that the node has layerdata
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot()) #Note: After this we can no longer abort!

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()

    def _onActiveViewChanged(self):
        if self.isRunning():
            if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
                if not self._progress:
                    self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, 0)
                    self._progress.show()
            else:
                if self._progress:
                    self._progress.hide()

<|endoftext|>


<commit_before>from util import read
from cfg import cfg

class Config(object):
	def __init__(self, cfg):
		self._cfg = {}
		for key, val in cfg.items():
			self.update(key, val)

	def __getattr__(self, key):
		return self._cfg[key]

	# dict compabitility
	def values(self):
		return self._cfg.values()

	def items(self):
		return self._cfg.items()

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

	def update(self, key, val):
		self._cfg[key] = isinstance(val, dict) and Config(val) or val

config = Config(cfg)
for key, val in [[term.strip() for term in line.split(" = ")] for line in read("ct.cfg", True)]:
	if key == "ENCODE":
		config.update("encode", val == "True")
	else:
		config.update(key.lower(), val)<commit_msg>config updates JS_PATH (config.js.path) from ct.cfg file<commit_after>from util import read
from cfg import cfg

class Config(object):
	def __init__(self, cfg):
		self._cfg = {}
		for key, val in cfg.items():
			self.update(key, val)

	def __getattr__(self, key):
		return self._cfg[key]

	# dict compabitility
	def values(self):
		return self._cfg.values()

	def items(self):
		return self._cfg.items()

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

	def update(self, key, val):
		self._cfg[key] = isinstance(val, dict) and Config(val) or val

config = Config(cfg)
for key, val in [[term.strip() for term in line.split(" = ")] for line in read("ct.cfg", True)]:
	if key == "ENCODE":
		config.update("encode", val == "True")
	elif key == "JS_PATH":
		config.js.update("path", val)
	else:
		config.update(key.lower(), val)<|endoftext|>


<commit_before>"""
### Usage: ctdeploy [-d|s|p] [-un] [--js_path=PATH]

### Options:

    -h, --help            show this help message and exit
    -d, --dynamic         switch to dynamic (development) mode
    -s, --static          switch to static (debug) mode
    -p, --production      switch to production (garbled) mode
    -u, --upload          uploads project in specified mode and then switches
                          back to dynamic (development) mode
    -n, --no_build        skip compilation step
    -j JS_PATH, --js_path=JS_PATH
                          set javascript path (default=js)

### Supports 3 modes:
    - dynamic (files live in html)
      - normal development files
        - dynamic imports throughout
      - original files are loaded ad-hoc
        - chrome debugger plays nice
      - no wire encryption
      - all imports lazy
    - static (files live in html-static)
      - compiler builds same html files
        - script imports in head
        - otherwise unmodified source files
      - original files are directly referenced
        - chrome debugger prefers
      - no wire encryption
      - all hard requirements loaded in head
        - lazy-designated imports still lazy
    - production (files live in html-production)
      - all code is compiled in head
        - html is compressed
        - javascript is minified and mangled
      - original code is unrecognizable
        - chrome debugger almost useless
      - wire encryption
      - designated lazy imports (indicated by second bool arg to CT.require)

Generates fresh 'static' and 'production' files (from 'development' source files in 'html' on every run, unless -n [or --no_build] flag is used). Mode is established in the app.yaml file, which routes requests to the appropriate directory, and the ct.cfg file, which determines backend behavior, especially regarding encryption.
"""

import subprocess, commands, os
from cantools import config
from cantools.util import log, error, read, write
from builder import build

def doyaml(mode):
    log("switching to %s mode"%(mode,))
    lines = read(config.yaml.path, lines=True)
    f = open(config.yaml.path, 'w')
    m = None
    for line in lines:
        if line.startswith(config.yaml.start):
            m = line[len(config.yaml.start):].strip()
        elif line.startswith(config.yaml.end):
            m = None
        elif m == mode:
            line = line.strip("#")
        elif m:
            line = "#%s"%(line.strip("#"),)
        f.write(line)
    f.close()

def setmode(mode):
    doyaml(mode) # support other backends beyond app engine
    ctpy = read(config.py.path)
    isprod = mode == "production"
    # set encode
    ctpy = ctpy.replace(config.py.enc%(str(not isprod),),
        config.py.enc%(str(isprod),))
    # set mode
    for m in ["dynamic", "static", "production"]:
        if m != mode:
            ctpy = ctpy.replace(config.py.mode%(m,), config.py.mode%(mode,))
    write(ctpy, config.py.path)

def run():
    from optparse import OptionParser
    parser = OptionParser("ctdeploy [-d|s|p] [-un] [--js_path=PATH]")
    parser.add_option("-d", "--dynamic", action="store_true", dest="dynamic",
        default=False, help="switch to dynamic (development) mode")
    parser.add_option("-s", "--static", action="store_true", dest="static",
        default=False, help="switch to static (debug) mode")
    parser.add_option("-p", "--production", action="store_true", dest="production",
        default=False, help="switch to production (garbled) mode")
    parser.add_option("-u", "--upload", action="store_true", dest="upload", default=False,
        help="uploads project in specified mode and then switches back to dynamic (development) mode")
    parser.add_option("-n", "--no_build", action="store_true",
        dest="no_build", default=False, help="skip compilation step")
    parser.add_option("-j", "--js_path", dest="js_path", default=config.js.path,
        help="set javascript path (default=%s)"%(config.js.path,))
    options, args = parser.parse_args()

    mode = options.dynamic and "dynamic" or options.static and "static" or options.production and "production"
    if not mode:
        error("no mode specified")

    # 0) set js path
    if options.js_path != config.js.path:
        log("setting js path to: %s"%(options.js_path,))
        config.js.update("path", options.js_path)

    # 1) build static/production files
    if not options.no_build:
        os.path.walk(config.build.web.dynamic, build, None)

    # 2) switch to specified mode
    setmode(mode)

    # 3) if -u, upload project and switch back to -d mode
    if options.upload:
        log("uploading files")
        subprocess.call('appcfg.py update . --no_precompilation --noauth_local_webserver', shell=True)
        setmode("dynamic")

    log("goodbye")

if __name__ == "__main__":
    run()<commit_msg>ctdeploy -v (--vpush): get new version; update version numbers (setup, __init__); ctinit -a; ctdoc -w; docs prod compilation; git push<commit_after>"""
### Usage: ctdeploy [-d|s|p] [-un] [--js_path=PATH]

### Options:

    -h, --help            show this help message and exit
    -d, --dynamic         switch to dynamic (development) mode
    -s, --static          switch to static (debug) mode
    -p, --production      switch to production (garbled) mode
    -u, --upload          uploads project in specified mode and then switches
                          back to dynamic (development) mode
    -n, --no_build        skip compilation step
    -j JS_PATH, --js_path=JS_PATH
                          set javascript path (default=js)

### Supports 3 modes:
    - dynamic (files live in html)
      - normal development files
        - dynamic imports throughout
      - original files are loaded ad-hoc
        - chrome debugger plays nice
      - no wire encryption
      - all imports lazy
    - static (files live in html-static)
      - compiler builds same html files
        - script imports in head
        - otherwise unmodified source files
      - original files are directly referenced
        - chrome debugger prefers
      - no wire encryption
      - all hard requirements loaded in head
        - lazy-designated imports still lazy
    - production (files live in html-production)
      - all code is compiled in head
        - html is compressed
        - javascript is minified and mangled
      - original code is unrecognizable
        - chrome debugger almost useless
      - wire encryption
      - designated lazy imports (indicated by second bool arg to CT.require)

Generates fresh 'static' and 'production' files (from 'development' source files in 'html' on every run, unless -n [or --no_build] flag is used). Mode is established in the app.yaml file, which routes requests to the appropriate directory, and the ct.cfg file, which determines backend behavior, especially regarding encryption.
"""

import subprocess, commands, os
from cantools import config, __version__
from cantools.util import log, error, read, write, cmd
from builder import build

def doyaml(mode):
    log("switching to %s mode"%(mode,))
    lines = read(config.yaml.path, lines=True)
    f = open(config.yaml.path, 'w')
    m = None
    for line in lines:
        if line.startswith(config.yaml.start):
            m = line[len(config.yaml.start):].strip()
        elif line.startswith(config.yaml.end):
            m = None
        elif m == mode:
            line = line.strip("#")
        elif m:
            line = "#%s"%(line.strip("#"),)
        f.write(line)
    f.close()

def setmode(mode):
    doyaml(mode) # support other backends beyond app engine
    ctpy = read(config.py.path)
    isprod = mode == "production"
    # set encode
    ctpy = ctpy.replace(config.py.enc%(str(not isprod),),
        config.py.enc%(str(isprod),))
    # set mode
    for m in ["dynamic", "static", "production"]:
        if m != mode:
            ctpy = ctpy.replace(config.py.mode%(m,), config.py.mode%(mode,))
    write(ctpy, config.py.path)

def vpush():
    log("vpushin!", important=True)
    log("current version: %s"%(__version__,))
    vnumz = [int(n) for n in __version__.split(".")]
    while len(vnumz) < 4:
        vnumz.append(0)
    vnumz[-1] += 1
    minor = ".".join([str(n) for n in vnumz])
    vnumz[-2] += 1
    major = ".".join([str(n) for n in vnumz[:-1]])
    log("How should we increment? You have three options:", important=True)
    log("Major ('M' - %s)"%(major,), 1)
    log("Minor ('m' - %s)"%(minor,), 1)
    log("Custom ('c')", 1)
    selection = raw_input("So? (M/m/c - default: m): ")
    if selection == "c":
        version = raw_input("ok, then. what's the new version #? ")
    elif selection == "M":
        version = major
    else: # default (minor)
        version = minor
    log("going with: %s"%(version,), important=True)
    for fname in ["setup.py", os.path.join("cantools", "__init__.py")]:
        log("updating: %s"%(fname,), 1)
        write(read(fname).replace(__version__, version), fname)
    cmd("ctinit -a")
    cmd("ctdoc -w")
    cmd("cd docs")
    cmd("ctdeploy -p")
    cmd("cd ..")
    cmd("git add -u")
    cmd('git commit -m "vpush %s"'%(version,))
    cmd("git push")
    log("we did it (%s -> %s)!"%(__version__, version))

def run():
    from optparse import OptionParser
    parser = OptionParser("ctdeploy [-d|s|p] [-un] [--js_path=PATH]")
    parser.add_option("-d", "--dynamic", action="store_true", dest="dynamic",
        default=False, help="switch to dynamic (development) mode")
    parser.add_option("-s", "--static", action="store_true", dest="static",
        default=False, help="switch to static (debug) mode")
    parser.add_option("-p", "--production", action="store_true", dest="production",
        default=False, help="switch to production (garbled) mode")
    parser.add_option("-u", "--upload", action="store_true", dest="upload", default=False,
        help="uploads project in specified mode and then switches back to dynamic (development) mode")
    parser.add_option("-n", "--no_build", action="store_true",
        dest="no_build", default=False, help="skip compilation step")
    parser.add_option("-j", "--js_path", dest="js_path", default=config.js.path,
        help="set javascript path (default=%s)"%(config.js.path,))
    parser.add_option("-v", "--vpush", action="store_true", dest="vpush",
        default=False, help="push a new version of cantools [ct dev only]")
    options, args = parser.parse_args()

    if options.vpush:
        vpush()
    else:
        mode = options.dynamic and "dynamic" or options.static and "static" or options.production and "production"
        if not mode:
            error("no mode specified")

        # 0) set js path
        if options.js_path != config.js.path:
            log("setting js path to: %s"%(options.js_path,))
            config.js.update("path", options.js_path)

        # 1) build static/production files
        if not options.no_build:
            os.path.walk(config.build.web.dynamic, build, None)

        # 2) switch to specified mode
        setmode(mode)

        # 3) if -u, upload project and switch back to -d mode
        if options.upload:
            log("uploading files")
            subprocess.call('appcfg.py update . --no_precompilation --noauth_local_webserver', shell=True)
            setmode("dynamic")

    log("goodbye")

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


<commit_before>from datetime import datetime
from django.conf import settings
from django.conf.urls import url
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
import requests

from . import BaseBackend
from ..contacts.models import Contact
from ..msgs.models import Message
from ..utils import uuid_to_int

from dash.orgs.models import Org
from dash.utils import is_dict_equal
from dash.utils.sync import BaseSyncer, sync_local_to_changes

from itertools import chain

class IdentityStore(object):
    '''Implements required methods for accessing the identity data in the
    identity store.'''
    def __init__(self, base_url, auth_token, address_type):
        '''
        base_url: the base URL where the identity store is located
        auth_token: the token that should be used for authorized access
        address_type: the address type of the addresses that we want
        '''
        self.base_url = base_url.rstrip('/')
        self.address_type = address_type
        self.session = requests.Session()
        self.session.headers.update({'Authorization': 'Token %s' % auth_token})
        self.session.headers.update({'Content-Type': 'application/json'})

    def get_paginated_response(self, url, params={}, **kwargs):
        '''Get the results of all pages of a response. Returns an iterator that
        returns each of the items.'''
        while url is not None:
            r = self.session.get(url, params=params, **kwargs)
            data = r.json()
            for result in data.get('results', []):
                yield result
            url = data.get('next', None)
            # params are included in the next url
            params = {}

    def get_identity(self, uuid):
        '''Returns the details of the identity.'''
        r = self.session.get(
            '%s/api/v1/identities/%s/' % (self.base_url, uuid))
        if r.status_code == 404:
            return None
        return r.json()

    def get_addresses(self, uuid):
        '''Get the list of addresses that a message to an identity specified
        by uuid should be sent to.'''
        identity = self.get_identity(uuid)
        if identity and identity.get('communicate_through') is not None:
            identity = self.get_identity(identity['communicate_through'])
        addresses = self.get_paginated_response(
            '%s/api/v1/identities/%s/addresses/%s' % (
                self.base_url, identity['id'], self.address_type),
            params={'default': True})
        return (
            a['address'] for a in addresses if a.get('address') is not None)

    def get_identities_for_address(self, address):
        return self.get_paginated_response(
            '%s/api/v1/identities/search/' % (self.base_url),
            params={'details__addresses__%s' % self.address_type: address})

    def create_identity(self, address):
        identity = self.session.post(
            '%s/api/v1/identities/' % (self.base_url,),
            json={
                'details': {
                    'addresses': {
                        self.address_type: {
                            address: {},
                        },
                    },
                    'default_addr_type': 'msisdn',
                },
            }
        )
        return identity.json()

    def get_identities(self, **params):
        '''Get the list of identities filtered by the given kwargs.'''
        url = '%s/api/v1/identities/?' % self.base_url

        identities = self.get_paginated_response(
            url, params=params)

        # Users who opt to be forgotten from the system have their details
        # stored as 'redacted'.
        return (
            IdentityStoreContact(i) for i in identities if
            i.get('details').get('name') != "redacted"
        )

class IdentityStoreContact(object):
    """
    Holds identity data for syncing
    """
    def __init__(self, json_data):
        for k, v in json_data.items():
            setattr(self, k, v)

        # Languages in the identity store have the country code at the end
        self.language = None
        remote_language = json_data.get('details').get('preferred_language')
        if remote_language is not None:
            self.language, _, _ = remote_language.partition('_')
        self.name = json_data.get('details').get('name', None)
        self.addresses = json_data.get('details').get('addresses')
        self.fields = {}
        self.groups = {}

class IdentityStoreContactSyncer(BaseSyncer):
    """
    Syncer for contacts from the Identity Store
    """
    model = Contact
    remote_id_attr = 'id'

    def local_kwargs(self, org, remote):
        return {
            'org': org,
            'uuid': remote.id,
            'name': remote.name,
            'language': remote.language,
            'is_blocked': False,  # TODO: Get 'is_blocked' from Opt-outs
            'is_stub': False,
            'fields': {},
            Contact.SAVE_GROUPS_ATTR: {},
        }

    def update_required(self, local, remote, remote_as_kwargs):
        if local.is_stub or local.name != remote.name or \
                local.language != remote.language:
            return True

        if {g.uuid for g in local.groups.all()} != \
                {g.uuid for g in remote.groups}:
            return True

        return not is_dict_equal(
            local.get_fields(), remote.fields, ignore_none_values=True)

    def delete_local(self, local):
        local.release()

class JunebugMessageSendingError(Exception):
    '''Exception that is raised when errors occur when trying to send messages
    through Junebug.'''

class JunebugMessageSender(object):
    def __init__(self, base_url, channel_id, from_address, identity_store):
        self.base_url = base_url
        self.channel_id = channel_id
        self.from_address = from_address
        self.identity_store = identity_store
        self.session = requests.Session()

    @property
    def url(self):
        return '%s/channels/%s/messages/' % (
            self.base_url.rstrip('/'), self.channel_id)

    def split_urn(self, urn):
        try:
            type_, address = urn.split(':', 1)
        except ValueError:
            raise JunebugMessageSendingError('Invalid URN: %s' % urn)
        return type_, address

    def send_message(self, message):
        if message.urn:
            _, to_addr = self.split_urn(message.urn)
            addresses = [to_addr]
        elif message.contact and message.contact.uuid:
            uuid = message.contact.uuid
            addresses = self.identity_store.get_addresses(uuid)
        else:
            # If we don't have an URN for a message, we cannot send it.
            raise JunebugMessageSendingError(
                'Cannot send message without URN: %r' % message)
        for to_addr in addresses:
            data = {
                'to': to_addr,
                'from': self.from_address,
                'content': message.text,
            }
            self.session.post(self.url, json=data)

class JunebugBackend(BaseBackend):
    '''
    Junebug instance as a backend.
    '''

    def __init__(self):
        self.identity_store = IdentityStore(
            settings.IDENTITY_API_ROOT, settings.IDENTITY_AUTH_TOKEN,
            settings.IDENTITY_ADDRESS_TYPE)
        self.message_sender = JunebugMessageSender(
            settings.JUNEBUG_API_ROOT, settings.JUNEBUG_CHANNEL_ID,
            settings.JUNEBUG_FROM_ADDRESS, self.identity_store)

    def pull_contacts(
            self, org, modified_after, modified_before,
            progress_callback=None):
        """
        Pulls contacts modified in the given time window

        :param org: the org
        :param datetime modified_after: pull contacts modified after this
        :param datetime modified_before: pull contacts modified before this
        :param progress_callback:
            callable that will be called from time to time with number of
            contacts pulled
        :return:
            tuple of the number of contacts created, updated, deleted and
            ignored
        """
        identity_store = self.identity_store

        # all identities created in the Identity Store in the time window
        new_identities = identity_store.get_identities(
            created_from=modified_after, created_to=modified_before)

        # all identities modified in the Identity Store in the time window
        modified_identities = identity_store.get_identities(
            updated_from=modified_after,
            updated_to=modified_before)

        identities_to_update = list(chain(modified_identities, new_identities))

        # sync_local_to_changes() expects iterables for the 3rd and 4th args
        # Deleted identities are updated via the Identity Store callback
        return sync_local_to_changes(
            org, IdentityStoreContactSyncer(), [identities_to_update], [],
            progress_callback)

    def pull_fields(self, org):
        """
        Pulls all contact fields

        :param org: the org
        :return:
            tuple of the number of fields created, updated, deleted and ignored
        """
        return (0, 0, 0, 0)

    def pull_groups(self, org):
        """
        Pulls all contact groups

        :param org: the org
        :return:
            tuple of the number of groups created, updated, deleted and ignored
        """
        return (0, 0, 0, 0)

    def pull_labels(self, org):
        """
        Pulls all message labels

        :param org: the org
        :return:
            tuple of the number of labels created, updated, deleted and ignored
        """
        return (0, 0, 0, 0)

    def pull_messages(
            self, org, modified_after, modified_before, as_handled=False,
            progress_callback=None):
        """
        Pulls messages modified in the given time window

        :param org: the org
        :param datetime modified_after: pull messages modified after this
        :param datetime modified_before: pull messages modified before this
        :param bool as_handled:
            whether messages should be saved as already handled
        :param progress_callback:
            callable that will be called from time to time with number of
            messages pulled
        :return:
            tuple of the number of messages created, updated, deleted and
            ignored
        """
        return (0, 0, 0, 0)

    def push_label(self, org, label):
        """
        Pushes a new or updated label

        :param org: the org
        :param label: the label
        """

    def push_outgoing(self, org, outgoing, as_broadcast=False):
        """
        Pushes (i.e. sends) outgoing messages

        :param org: the org
        :param outgoing: the outgoing messages
        :param as_broadcast:
            whether outgoing messages differ only by recipient and so can be
            sent as single broadcast
        """
        for message in outgoing:
            self.message_sender.send_message(message)

    def add_to_group(self, org, contact, group):
        """
        Adds the given contact to a group

        :param org: the org
        :param contact: the contact
        :param group: the group
        """

    def remove_from_group(self, org, contact, group):
        """
        Removes the given contact from a group

        :param org: the org
        :param contact: the contact
        :param group: the group
        """

    def stop_runs(self, org, contact):
        """
        Stops any ongoing flow runs for the given contact

        :param org: the org
        :param contact: the contact
        """

    def label_messages(self, org, messages, label):
        """
        Adds a label to the given messages

        :param org: the org
        :param messages: the messages
        :param label: the label
        """

    def unlabel_messages(self, org, messages, label):
        """
        Removes a label from the given messages

        :param org: the org
        :param messages: the messages
        :param label: the label
        """

    def archive_messages(self, org, messages):
        """
        Archives the given messages

        :param org: the org
        :param messages: the messages
        """

    def archive_contact_messages(self, org, contact):
        """
        Archives all messages for the given contact

        :param org: the org
        :param contact: the contact
        """

    def restore_messages(self, org, messages):
        """
        Restores (un-archives) the given messages

        :param org: the org
        :param messages: the messages
        """

    def flag_messages(self, org, messages):
        """
        Flags the given messages

        :param org: the org
        :param messages: the messages
        """

    def unflag_messages(self, org, messages):
        """
        Un-flags the given messages

        :param org: the org
        :param messages: the messages
        """

    def fetch_contact_messages(
            self, org, contact, created_after, created_before):
        """
        Fetches additional messages to display on a case timeline

        :param org: the org
        :param contact: the contact
        :param created_after: include messages created after this time
        :param created_before: include messages created before this time
        :return:
            the messages as JSON objects in reverse chronological order. JSON
            format should match that returned by Message.as_json() for incoming
            messages and Outgoing.as_json() for outgoing messages.
        """
        return []

    def get_url_patterns(self):
        """
        Returns the list of URL patterns that should be registered for this
        backend.

        :return: a list of URL patterns.
        """
        return [
            url(
                settings.JUNEBUG_INBOUND_URL, received_junebug_message,
                name='inbound_junebug_message'
            ),
            url(
                settings.IDENTITY_STORE_OPTOUT_URL,
                receive_identity_store_optout, name='identity_store_optout'
            ),
        ]

@csrf_exempt
def received_junebug_message(request):
    '''Handles MO messages from Junebug.'''
    if request.method != 'POST':
        return JsonResponse({'reason': 'Method not allowed.'}, status=405)

    try:
        data = json.loads(request.body)
    except ValueError as e:
        return JsonResponse(
            {'reason': 'JSON decode error', 'details': e.message}, status=400)

    identity_store = IdentityStore(
        settings.IDENTITY_API_ROOT, settings.IDENTITY_AUTH_TOKEN,
        settings.IDENTITY_ADDRESS_TYPE)
    identities = identity_store.get_identities_for_address(data.get('from'))
    try:
        identity = identities.next()
    except StopIteration:
        identity = identity_store.create_identity(data.get('from'))
    contact = Contact.get_or_create(request.org, identity.get('id'))

    message_id = uuid_to_int(data.get('message_id'))
    msg = Message.objects.create(
        org=request.org, backend_id=message_id, contact=contact,
        type=Message.TYPE_INBOX, text=(data.get('content') or ''),
        created_on=datetime.now(), has_labels=True)

    return JsonResponse(msg.as_json())

@csrf_exempt
def receive_identity_store_optout(request):
    '''Handles optout notifications from the Identity Store.'''
    if request.method != 'POST':
        return JsonResponse({'reason': 'Method not allowed.'}, status=405)

    try:
        data = json.loads(request.body)
    except ValueError as e:
        return JsonResponse(
            {'reason': 'JSON decode error', 'details': e.message}, status=400)

    identity_id = data.get('identity_id')
    optout_type = data.get('optout_type')

    syncer = IdentityStoreContactSyncer()

    # The identity store currently doesn't specify the response format or do
    # anything with the response.

    # TODO: Support existance and usage of multiple orgs
    org = Org.objects.first()
    local_contact = syncer.fetch_local(org, identity_id)
    if local_contact:
        local_contact.lock(org, identity_id)
        if optout_type == "forget":
            local_contact.release()
            return JsonResponse({"success": True}, status=200)

        elif optout_type == "stop" or optout_type == "stopall":
            local_contact.is_blocked = True
            return JsonResponse({"success": True}, status=200)
        elif optout_type == "unsubscribe":
            # This case is not relevant to Casepro
            return JsonResponse({"success": True}, status=200)

    return JsonResponse({
        'reason': "Unrecognised value for 'optout_type': " + optout_type},
        status=400
    )
<commit_msg>Use 'identity' as id key in optout view<commit_after>from datetime import datetime
from django.conf import settings
from django.conf.urls import url
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
import requests

from . import BaseBackend
from ..contacts.models import Contact
from ..msgs.models import Message
from ..utils import uuid_to_int

from dash.orgs.models import Org
from dash.utils import is_dict_equal
from dash.utils.sync import BaseSyncer, sync_local_to_changes

from itertools import chain

class IdentityStore(object):
    '''Implements required methods for accessing the identity data in the
    identity store.'''
    def __init__(self, base_url, auth_token, address_type):
        '''
        base_url: the base URL where the identity store is located
        auth_token: the token that should be used for authorized access
        address_type: the address type of the addresses that we want
        '''
        self.base_url = base_url.rstrip('/')
        self.address_type = address_type
        self.session = requests.Session()
        self.session.headers.update({'Authorization': 'Token %s' % auth_token})
        self.session.headers.update({'Content-Type': 'application/json'})

    def get_paginated_response(self, url, params={}, **kwargs):
        '''Get the results of all pages of a response. Returns an iterator that
        returns each of the items.'''
        while url is not None:
            r = self.session.get(url, params=params, **kwargs)
            data = r.json()
            for result in data.get('results', []):
                yield result
            url = data.get('next', None)
            # params are included in the next url
            params = {}

    def get_identity(self, uuid):
        '''Returns the details of the identity.'''
        r = self.session.get(
            '%s/api/v1/identities/%s/' % (self.base_url, uuid))
        if r.status_code == 404:
            return None
        return r.json()

    def get_addresses(self, uuid):
        '''Get the list of addresses that a message to an identity specified
        by uuid should be sent to.'''
        identity = self.get_identity(uuid)
        if identity and identity.get('communicate_through') is not None:
            identity = self.get_identity(identity['communicate_through'])
        addresses = self.get_paginated_response(
            '%s/api/v1/identities/%s/addresses/%s' % (
                self.base_url, identity['id'], self.address_type),
            params={'default': True})
        return (
            a['address'] for a in addresses if a.get('address') is not None)

    def get_identities_for_address(self, address):
        return self.get_paginated_response(
            '%s/api/v1/identities/search/' % (self.base_url),
            params={'details__addresses__%s' % self.address_type: address})

    def create_identity(self, address):
        identity = self.session.post(
            '%s/api/v1/identities/' % (self.base_url,),
            json={
                'details': {
                    'addresses': {
                        self.address_type: {
                            address: {},
                        },
                    },
                    'default_addr_type': 'msisdn',
                },
            }
        )
        return identity.json()

    def get_identities(self, **params):
        '''Get the list of identities filtered by the given kwargs.'''
        url = '%s/api/v1/identities/?' % self.base_url

        identities = self.get_paginated_response(
            url, params=params)

        # Users who opt to be forgotten from the system have their details
        # stored as 'redacted'.
        return (
            IdentityStoreContact(i) for i in identities if
            i.get('details').get('name') != "redacted"
        )

class IdentityStoreContact(object):
    """
    Holds identity data for syncing
    """
    def __init__(self, json_data):
        for k, v in json_data.items():
            setattr(self, k, v)

        # Languages in the identity store have the country code at the end
        self.language = None
        remote_language = json_data.get('details').get('preferred_language')
        if remote_language is not None:
            self.language, _, _ = remote_language.partition('_')
        self.name = json_data.get('details').get('name', None)
        self.addresses = json_data.get('details').get('addresses')
        self.fields = {}
        self.groups = {}

class IdentityStoreContactSyncer(BaseSyncer):
    """
    Syncer for contacts from the Identity Store
    """
    model = Contact
    remote_id_attr = 'id'

    def local_kwargs(self, org, remote):
        return {
            'org': org,
            'uuid': remote.id,
            'name': remote.name,
            'language': remote.language,
            'is_blocked': False,  # TODO: Get 'is_blocked' from Opt-outs
            'is_stub': False,
            'fields': {},
            Contact.SAVE_GROUPS_ATTR: {},
        }

    def update_required(self, local, remote, remote_as_kwargs):
        if local.is_stub or local.name != remote.name or \
                local.language != remote.language:
            return True

        if {g.uuid for g in local.groups.all()} != \
                {g.uuid for g in remote.groups}:
            return True

        return not is_dict_equal(
            local.get_fields(), remote.fields, ignore_none_values=True)

    def delete_local(self, local):
        local.release()

class JunebugMessageSendingError(Exception):
    '''Exception that is raised when errors occur when trying to send messages
    through Junebug.'''

class JunebugMessageSender(object):
    def __init__(self, base_url, channel_id, from_address, identity_store):
        self.base_url = base_url
        self.channel_id = channel_id
        self.from_address = from_address
        self.identity_store = identity_store
        self.session = requests.Session()

    @property
    def url(self):
        return '%s/channels/%s/messages/' % (
            self.base_url.rstrip('/'), self.channel_id)

    def split_urn(self, urn):
        try:
            type_, address = urn.split(':', 1)
        except ValueError:
            raise JunebugMessageSendingError('Invalid URN: %s' % urn)
        return type_, address

    def send_message(self, message):
        if message.urn:
            _, to_addr = self.split_urn(message.urn)
            addresses = [to_addr]
        elif message.contact and message.contact.uuid:
            uuid = message.contact.uuid
            addresses = self.identity_store.get_addresses(uuid)
        else:
            # If we don't have an URN for a message, we cannot send it.
            raise JunebugMessageSendingError(
                'Cannot send message without URN: %r' % message)
        for to_addr in addresses:
            data = {
                'to': to_addr,
                'from': self.from_address,
                'content': message.text,
            }
            self.session.post(self.url, json=data)

class JunebugBackend(BaseBackend):
    '''
    Junebug instance as a backend.
    '''

    def __init__(self):
        self.identity_store = IdentityStore(
            settings.IDENTITY_API_ROOT, settings.IDENTITY_AUTH_TOKEN,
            settings.IDENTITY_ADDRESS_TYPE)
        self.message_sender = JunebugMessageSender(
            settings.JUNEBUG_API_ROOT, settings.JUNEBUG_CHANNEL_ID,
            settings.JUNEBUG_FROM_ADDRESS, self.identity_store)

    def pull_contacts(
            self, org, modified_after, modified_before,
            progress_callback=None):
        """
        Pulls contacts modified in the given time window

        :param org: the org
        :param datetime modified_after: pull contacts modified after this
        :param datetime modified_before: pull contacts modified before this
        :param progress_callback:
            callable that will be called from time to time with number of
            contacts pulled
        :return:
            tuple of the number of contacts created, updated, deleted and
            ignored
        """
        identity_store = self.identity_store

        # all identities created in the Identity Store in the time window
        new_identities = identity_store.get_identities(
            created_from=modified_after, created_to=modified_before)

        # all identities modified in the Identity Store in the time window
        modified_identities = identity_store.get_identities(
            updated_from=modified_after,
            updated_to=modified_before)

        identities_to_update = list(chain(modified_identities, new_identities))

        # sync_local_to_changes() expects iterables for the 3rd and 4th args
        # Deleted identities are updated via the Identity Store callback
        return sync_local_to_changes(
            org, IdentityStoreContactSyncer(), [identities_to_update], [],
            progress_callback)

    def pull_fields(self, org):
        """
        Pulls all contact fields

        :param org: the org
        :return:
            tuple of the number of fields created, updated, deleted and ignored
        """
        return (0, 0, 0, 0)

    def pull_groups(self, org):
        """
        Pulls all contact groups

        :param org: the org
        :return:
            tuple of the number of groups created, updated, deleted and ignored
        """
        return (0, 0, 0, 0)

    def pull_labels(self, org):
        """
        Pulls all message labels

        :param org: the org
        :return:
            tuple of the number of labels created, updated, deleted and ignored
        """
        return (0, 0, 0, 0)

    def pull_messages(
            self, org, modified_after, modified_before, as_handled=False,
            progress_callback=None):
        """
        Pulls messages modified in the given time window

        :param org: the org
        :param datetime modified_after: pull messages modified after this
        :param datetime modified_before: pull messages modified before this
        :param bool as_handled:
            whether messages should be saved as already handled
        :param progress_callback:
            callable that will be called from time to time with number of
            messages pulled
        :return:
            tuple of the number of messages created, updated, deleted and
            ignored
        """
        return (0, 0, 0, 0)

    def push_label(self, org, label):
        """
        Pushes a new or updated label

        :param org: the org
        :param label: the label
        """

    def push_outgoing(self, org, outgoing, as_broadcast=False):
        """
        Pushes (i.e. sends) outgoing messages

        :param org: the org
        :param outgoing: the outgoing messages
        :param as_broadcast:
            whether outgoing messages differ only by recipient and so can be
            sent as single broadcast
        """
        for message in outgoing:
            self.message_sender.send_message(message)

    def add_to_group(self, org, contact, group):
        """
        Adds the given contact to a group

        :param org: the org
        :param contact: the contact
        :param group: the group
        """

    def remove_from_group(self, org, contact, group):
        """
        Removes the given contact from a group

        :param org: the org
        :param contact: the contact
        :param group: the group
        """

    def stop_runs(self, org, contact):
        """
        Stops any ongoing flow runs for the given contact

        :param org: the org
        :param contact: the contact
        """

    def label_messages(self, org, messages, label):
        """
        Adds a label to the given messages

        :param org: the org
        :param messages: the messages
        :param label: the label
        """

    def unlabel_messages(self, org, messages, label):
        """
        Removes a label from the given messages

        :param org: the org
        :param messages: the messages
        :param label: the label
        """

    def archive_messages(self, org, messages):
        """
        Archives the given messages

        :param org: the org
        :param messages: the messages
        """

    def archive_contact_messages(self, org, contact):
        """
        Archives all messages for the given contact

        :param org: the org
        :param contact: the contact
        """

    def restore_messages(self, org, messages):
        """
        Restores (un-archives) the given messages

        :param org: the org
        :param messages: the messages
        """

    def flag_messages(self, org, messages):
        """
        Flags the given messages

        :param org: the org
        :param messages: the messages
        """

    def unflag_messages(self, org, messages):
        """
        Un-flags the given messages

        :param org: the org
        :param messages: the messages
        """

    def fetch_contact_messages(
            self, org, contact, created_after, created_before):
        """
        Fetches additional messages to display on a case timeline

        :param org: the org
        :param contact: the contact
        :param created_after: include messages created after this time
        :param created_before: include messages created before this time
        :return:
            the messages as JSON objects in reverse chronological order. JSON
            format should match that returned by Message.as_json() for incoming
            messages and Outgoing.as_json() for outgoing messages.
        """
        return []

    def get_url_patterns(self):
        """
        Returns the list of URL patterns that should be registered for this
        backend.

        :return: a list of URL patterns.
        """
        return [
            url(
                settings.JUNEBUG_INBOUND_URL, received_junebug_message,
                name='inbound_junebug_message'
            ),
            url(
                settings.IDENTITY_STORE_OPTOUT_URL,
                receive_identity_store_optout, name='identity_store_optout'
            ),
        ]

@csrf_exempt
def received_junebug_message(request):
    '''Handles MO messages from Junebug.'''
    if request.method != 'POST':
        return JsonResponse({'reason': 'Method not allowed.'}, status=405)

    try:
        data = json.loads(request.body)
    except ValueError as e:
        return JsonResponse(
            {'reason': 'JSON decode error', 'details': e.message}, status=400)

    identity_store = IdentityStore(
        settings.IDENTITY_API_ROOT, settings.IDENTITY_AUTH_TOKEN,
        settings.IDENTITY_ADDRESS_TYPE)
    identities = identity_store.get_identities_for_address(data.get('from'))
    try:
        identity = identities.next()
    except StopIteration:
        identity = identity_store.create_identity(data.get('from'))
    contact = Contact.get_or_create(request.org, identity.get('id'))

    message_id = uuid_to_int(data.get('message_id'))
    msg = Message.objects.create(
        org=request.org, backend_id=message_id, contact=contact,
        type=Message.TYPE_INBOX, text=(data.get('content') or ''),
        created_on=datetime.now(), has_labels=True)

    return JsonResponse(msg.as_json())

@csrf_exempt
def receive_identity_store_optout(request):
    '''Handles optout notifications from the Identity Store.'''
    if request.method != 'POST':
        return JsonResponse({'reason': 'Method not allowed.'}, status=405)

    try:
        data = json.loads(request.body)
    except ValueError as e:
        return JsonResponse(
            {'reason': 'JSON decode error', 'details': e.message}, status=400)

    identity_id = data.get('identity')
    optout_type = data.get('optout_type')

    syncer = IdentityStoreContactSyncer()

    # The identity store currently doesn't specify the response format or do
    # anything with the response.

    # TODO: Support existance and usage of multiple orgs
    org = Org.objects.first()
    local_contact = syncer.fetch_local(org, identity_id)
    if local_contact:
        local_contact.lock(org, identity_id)
        if optout_type == "forget":
            local_contact.release()
            return JsonResponse({"success": True}, status=200)

        elif optout_type == "stop" or optout_type == "stopall":
            local_contact.is_blocked = True
            return JsonResponse({"success": True}, status=200)
        elif optout_type == "unsubscribe":
            # This case is not relevant to Casepro
            return JsonResponse({"success": True}, status=200)

    return JsonResponse({
        'reason': "Unrecognised value for 'optout_type': " + optout_type},
        status=400
    )
<|endoftext|>


<commit_before>#Задача 6. Вариант 26.
#Создайте игру, в которой компьютер загадывает название одного из восьми категорий, на которые разделяются дорожные знаки в соответствии с Венской конвенцией о дорожных знаках и сигналах, а игрок должен его угадать.

#Sosnovy M.S.
#16.03.2016

import random
print("компьютер загадывает название одного из восьми категорий, на которые разделяются дорожные знаки в соответствии с Венской конвенцией о дорожных знаках и сигналах. \nУгадайте ее.")
cont = ('Предупреждающие знаки' , 'Знаки преимущественного права проезда' , 'Запрещающие и ограничивающие знаки' , 'Предписывающие знаки' , 'Знаки особых предписаний')
con = random.randint(0,4)
x=0
ball=5

while(x != 5):
    print(cont[x])
    x += 1
answer = input("\nВведите название подгруппы: ")
while(answer != cont[con]):
    print("Неверно, попробуйте ещё раз.")
    answer = input("\nВведите название подгруппы: ")
    ball -=1
if (ball < 0):
    ball = 0
print("Верно, Вы победили!\n Вы набрали: " + str(ball) + " балл(ов)")
input("\nДля выхода нажмите Enter.")
<commit_msg>Update task_7_26.py<commit_after>#Задача 7. Вариант 26.
#Создайте игру, в которой компьютер загадывает название одного из восьми категорий, на которые разделяются дорожные знаки в соответствии с Венской конвенцией о дорожных знаках и сигналах, а игрок должен его угадать.

#Sosnovy M.S.
#16.03.2016

import random
print("компьютер загадывает название одного из восьми категорий, на которые разделяются дорожные знаки в соответствии с Венской конвенцией о дорожных знаках и сигналах. \nУгадайте ее.")
cont = ('Предупреждающие знаки' , 'Знаки преимущественного права проезда' , 'Запрещающие и ограничивающие знаки' , 'Предписывающие знаки' , 'Знаки особых предписаний')
con = random.randint(0,4)
x=0
ball=5

while(x != 5):
    print(cont[x])
    x += 1
answer = input("\nВведите название подгруппы: ")
while(answer != cont[con]):
    print("Неверно, попробуйте ещё раз.")
    answer = input("\nВведите название подгруппы: ")
    ball -=1
if (ball < 0):
    ball = 0
print("Верно, Вы победили!\n Вы набрали: " + str(ball) + " балл(ов)")
input("\nДля выхода нажмите Enter.")
<|endoftext|>


<commit_before>"""Configurable for configuring the IPython inline backend

This module does not import anything from matplotlib.
"""
#-----------------------------------------------------------------------------
#       Copyright (C) 2011 The IPython Development Team
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

from IPython.config.configurable import SingletonConfigurable
from IPython.utils.traitlets import (
    Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode
)
from IPython.utils.warn import warn

#-----------------------------------------------------------------------------
# Configurable for inline backend options
#-----------------------------------------------------------------------------

def pil_available():
    """Test if PIL/Pillow is available"""
    out = False
    try:
        from PIL import Image
        out = True
    except:
        pass
    return out

# inherit from InlineBackendConfig for deprecation purposes
class InlineBackendConfig(SingletonConfigurable):
    pass

class InlineBackend(InlineBackendConfig):
    """An object to store configuration of the inline backend."""

    def _config_changed(self, name, old, new):
        # warn on change of renamed config section
        if new.InlineBackendConfig != old.InlineBackendConfig:
            warn("InlineBackendConfig has been renamed to InlineBackend")
        super(InlineBackend, self)._config_changed(name, old, new)

    # The typical default figure size is too large for inline use,
    # so we shrink the figure size to 6x4, and tweak fonts to
    # make that fit.
    rc = Dict({'figure.figsize': (6.0,4.0),
        # play nicely with white background in the Qt and notebook frontend
        'figure.facecolor': (1,1,1,0),
        'figure.edgecolor': (1,1,1,0),
        # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
        'font.size': 10,
        # 72 dpi matches SVG/qtconsole
        # this only affects PNG export, as SVG has no dpi setting
        'savefig.dpi': 72,
        # 10pt still needs a little more room on the xlabel:
        'figure.subplot.bottom' : .125
        }, config=True,
        help="""Subset of matplotlib rcParams that should be different for the
        inline backend."""
    )

    figure_formats = Set({'png'}, config=True,
                          help="""A set of figure formats to enable: 'png',
                          'retina', 'jpeg', 'svg', 'pdf'.""")

    def _update_figure_formatters(self):
        if self.shell is not None:
            select_figure_formats(self.shell, self.figure_formats, **self.print_figure_kwargs)

    def _figure_formats_changed(self, name, old, new):
        from IPython.core.pylabtools import select_figure_formats
        if 'jpg' in new or 'jpeg' in new:
            if not pil_available():
                raise TraitError("Requires PIL/Pillow for JPG figures")
        if self.shell is None:
            return
        else:
            self._update_figure_formatters()

    figure_format = Unicode(config=True, help="""The figure format to enable (deprecated
                                         use `figure_formats` instead)""")

    def _figure_format_changed(self, name, old, new):
        if new:
            self.figure_formats = {new}

    print_figure_kwargs = Dict({'bbox_inches' : 'tight'}, config=True,
        help="""Extra kwargs to be passed to fig.canvas.print_figure.

        Logical examples include: bbox_inches, quality (for jpeg figures), etc.
        """
    )
    _print_figure_kwargs_changed = _update_figure_formatters

    close_figures = Bool(True, config=True,
        help="""Close all figures at the end of each cell.

        When True, ensures that each cell starts with no active figures, but it
        also means that one must keep track of references in order to edit or
        redraw figures in subsequent cells. This mode is ideal for the notebook,
        where residual plots from other cells might be surprising.

        When False, one must call figure() to create new figures. This means
        that gcf() and getfigs() can reference figures created in other cells,
        and the active figure can continue to be edited with pylab/pyplot
        methods that reference the current active figure. This mode facilitates
        iterative editing of figures, and behaves most consistently with
        other matplotlib backends, but figure barriers between cells must
        be explicit.
        """)

    shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')

<commit_msg>remove redundant check for self.shell is None<commit_after>"""Configurable for configuring the IPython inline backend

This module does not import anything from matplotlib.
"""
#-----------------------------------------------------------------------------
#       Copyright (C) 2011 The IPython Development Team
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

from IPython.config.configurable import SingletonConfigurable
from IPython.utils.traitlets import (
    Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode
)
from IPython.utils.warn import warn

#-----------------------------------------------------------------------------
# Configurable for inline backend options
#-----------------------------------------------------------------------------

def pil_available():
    """Test if PIL/Pillow is available"""
    out = False
    try:
        from PIL import Image
        out = True
    except:
        pass
    return out

# inherit from InlineBackendConfig for deprecation purposes
class InlineBackendConfig(SingletonConfigurable):
    pass

class InlineBackend(InlineBackendConfig):
    """An object to store configuration of the inline backend."""

    def _config_changed(self, name, old, new):
        # warn on change of renamed config section
        if new.InlineBackendConfig != old.InlineBackendConfig:
            warn("InlineBackendConfig has been renamed to InlineBackend")
        super(InlineBackend, self)._config_changed(name, old, new)

    # The typical default figure size is too large for inline use,
    # so we shrink the figure size to 6x4, and tweak fonts to
    # make that fit.
    rc = Dict({'figure.figsize': (6.0,4.0),
        # play nicely with white background in the Qt and notebook frontend
        'figure.facecolor': (1,1,1,0),
        'figure.edgecolor': (1,1,1,0),
        # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
        'font.size': 10,
        # 72 dpi matches SVG/qtconsole
        # this only affects PNG export, as SVG has no dpi setting
        'savefig.dpi': 72,
        # 10pt still needs a little more room on the xlabel:
        'figure.subplot.bottom' : .125
        }, config=True,
        help="""Subset of matplotlib rcParams that should be different for the
        inline backend."""
    )

    figure_formats = Set({'png'}, config=True,
                          help="""A set of figure formats to enable: 'png',
                          'retina', 'jpeg', 'svg', 'pdf'.""")

    def _update_figure_formatters(self):
        if self.shell is not None:
            select_figure_formats(self.shell, self.figure_formats, **self.print_figure_kwargs)

    def _figure_formats_changed(self, name, old, new):
        from IPython.core.pylabtools import select_figure_formats
        if 'jpg' in new or 'jpeg' in new:
            if not pil_available():
                raise TraitError("Requires PIL/Pillow for JPG figures")
        self._update_figure_formatters()

    figure_format = Unicode(config=True, help="""The figure format to enable (deprecated
                                         use `figure_formats` instead)""")

    def _figure_format_changed(self, name, old, new):
        if new:
            self.figure_formats = {new}

    print_figure_kwargs = Dict({'bbox_inches' : 'tight'}, config=True,
        help="""Extra kwargs to be passed to fig.canvas.print_figure.

        Logical examples include: bbox_inches, quality (for jpeg figures), etc.
        """
    )
    _print_figure_kwargs_changed = _update_figure_formatters

    close_figures = Bool(True, config=True,
        help="""Close all figures at the end of each cell.

        When True, ensures that each cell starts with no active figures, but it
        also means that one must keep track of references in order to edit or
        redraw figures in subsequent cells. This mode is ideal for the notebook,
        where residual plots from other cells might be surprising.

        When False, one must call figure() to create new figures. This means
        that gcf() and getfigs() can reference figures created in other cells,
        and the active figure can continue to be edited with pylab/pyplot
        methods that reference the current active figure. This mode facilitates
        iterative editing of figures, and behaves most consistently with
        other matplotlib backends, but figure barriers between cells must
        be explicit.
        """)

    shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')

<|endoftext|>


<commit_before>import generator
from parser   import Parser
from compiler import SyntaxCompiler

_parser   = None
_compiler = None

def get_parser():
    global _parser
    if not _parser:
        _parser = Parser()
    return _parser

def get_compiler():
    global _compiler
    if not _compiler:
        _compiler = SyntaxCompiler()
    return _compiler

def compile_string(syntax):
    return get_parser().parse_string(syntax, get_compiler())

def compile(syntax_file):
    return get_parser().parse(syntax_file, get_compiler())

def generate_to_file(converter, input_file, output_file, format = 'xml'):
    builder = generator.new(format)
    if builder is None:
        raise TypeError('invalid output format ' + repr(format))
    converter.parse(input_file, builder)
    builder.serialize_to_file(output_file)
<commit_msg>Improved behavior when used in a threaded environment.<commit_after>import generator
from parser   import Parser
from compiler import SyntaxCompiler

def compile_string(syntax):
    return Parser().parse_string(syntax, SyntaxCompiler())

def compile(syntax_file):
    return Parser().parse(syntax_file, SyntaxCompiler())

def generate_to_file(converter, input_file, output_file, format = 'xml'):
    builder = generator.new(format)
    if builder is None:
        raise TypeError('invalid output format ' + repr(format))
    converter.parse(input_file, builder)
    builder.serialize_to_file(output_file)
<|endoftext|>