<commit_before>import logging

import dbus
import telepathy

try:
	import conic as _conic
	conic = _conic
except (ImportError, OSError):
	conic = None

try:
	import osso as _osso
	osso = _osso
except (ImportError, OSError):
	osso = None

import constants
import util.coroutines as coroutines
import util.go_utils as gobject_utils
import util.tp_utils as telepathy_utils
import util.misc as misc_utils
import gvoice

_moduleLogger = logging.getLogger(__name__)

class NewGVConversations(object):

	def __init__(self, connRef):
		self._connRef = connRef
		self.__callback = None

	def start(self):
		self.__callback = coroutines.func_sink(
			coroutines.expand_positional(
				self._on_conversations_updated
			)
		)
		self._connRef().session.voicemails.updateSignalHandler.register_sink(
			self.__callback
		)
		self._connRef().session.texts.updateSignalHandler.register_sink(
			self.__callback
		)

	def stop(self):
		if self.__callback is None:
			_moduleLogger.info("New conversation monitor stopped without starting")
			return
		self._connRef().session.voicemails.updateSignalHandler.unregister_sink(
			self.__callback
		)
		self._connRef().session.texts.updateSignalHandler.unregister_sink(
			self.__callback
		)
		self.__callback = None

	@misc_utils.log_exception(_moduleLogger)
	def _on_conversations_updated(self, conv, conversationIds):
		_moduleLogger.debug("Incoming messages from: %r" % (conversationIds, ))
		for phoneNumber in conversationIds:
			h = self._connRef().get_handle_by_name(telepathy.HANDLE_TYPE_CONTACT, phoneNumber)
			# Just let the TextChannel decide whether it should be reported to the user or not
			props = self._connRef().generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
			if self._connRef()._channel_manager.channel_exists(props):
				continue

			# Maemo 4.1's RTComm opens a window for a chat regardless if a
			# message is received or not, so we need to do some filtering here
			mergedConv = conv.get_conversation(phoneNumber)
			newConversations = mergedConv.conversations
			newConversations = gvoice.conversations.filter_out_read(newConversations)
			newConversations = gvoice.conversations.filter_out_self(newConversations)
			newConversations = list(newConversations)
			if not newConversations:
				continue

			chan = self._connRef()._channel_manager.channel_for_props(props, signal=True)

class RefreshVoicemail(object):

	def __init__(self, connRef):
		self._connRef = connRef
		self._newChannelSignaller = telepathy_utils.NewChannelSignaller(self._on_new_channel)
		self._outstandingRequests = []
		self._isStarted = False

	def start(self):
		self._newChannelSignaller.start()
		self._isStarted = True

	def stop(self):
		if not self._isStarted:
			_moduleLogger.info("voicemail monitor stopped without starting")
			return
		_moduleLogger.info("Stopping voicemail refresh")
		self._newChannelSignaller.stop()

		# I don't want to trust whether the cancel happens within the current
		# callback or not which could be the deciding factor between invalid
		# iterators or infinite loops
		localRequests = [r for r in self._outstandingRequests]
		for request in localRequests:
			localRequests.cancel()

		self._isStarted = False

	@misc_utils.log_exception(_moduleLogger)
	def _on_new_channel(self, bus, serviceName, connObjectPath, channelObjectPath, channelType):
		if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
			return

		cmName = telepathy_utils.cm_from_path(connObjectPath)
		if cmName == constants._telepathy_implementation_name_:
			_moduleLogger.debug("Ignoring channels from self to prevent deadlock")
			return

		conn = telepathy.client.Connection(serviceName, connObjectPath)
		try:
			chan = telepathy.client.Channel(serviceName, channelObjectPath)
		except dbus.exceptions.UnknownMethodException:
			_moduleLogger.exception("Client might not have implemented a deprecated method")
			return
		missDetection = telepathy_utils.WasMissedCall(
			bus, conn, chan, self._on_missed_call, self._on_error_for_missed
		)
		self._outstandingRequests.append(missDetection)

	@misc_utils.log_exception(_moduleLogger)
	def _on_missed_call(self, missDetection):
		_moduleLogger.info("Missed a call")
		self._connRef().session.voicemailsStateMachine.reset_timers()
		self._outstandingRequests.remove(missDetection)

	@misc_utils.log_exception(_moduleLogger)
	def _on_error_for_missed(self, missDetection, reason):
		_moduleLogger.debug("Error: %r claims %r" % (missDetection, reason))
		self._outstandingRequests.remove(missDetection)

class TimedDisconnect(object):

	def __init__(self, connRef):
		self._connRef = connRef
		self.__delayedDisconnect = gobject_utils.Timeout(self._on_delayed_disconnect)

	def start(self):
		self.__delayedDisconnect.start(seconds=20)

	def stop(self):
		self.__delayedDisconnect.cancel()

	@misc_utils.log_exception(_moduleLogger)
	def _on_delayed_disconnect(self):
		_moduleLogger.info("Timed disconnect occurred")
		self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR)

class AutoDisconnect(object):

	def __init__(self, connRef):
		self._connRef = connRef
		if conic is not None:
			self.__connection = conic.Connection()
		else:
			self.__connection = None

		self.__connectionEventId = None
		self.__delayedDisconnect = gobject_utils.Timeout(self._on_delayed_disconnect)

	def start(self):
		if self.__connection is not None:
			self.__connectionEventId = self.__connection.connect("connection-event", self._on_connection_change)

	def stop(self):
		self._cancel_delayed_disconnect()

	@misc_utils.log_exception(_moduleLogger)
	def _on_connection_change(self, connection, event):
		"""
		@note Maemo specific
		"""
		status = event.get_status()
		error = event.get_error()
		iap_id = event.get_iap_id()
		bearer = event.get_bearer_type()

		if status == conic.STATUS_DISCONNECTED:
			_moduleLogger.info("Disconnected from network, starting countdown to logoff")
			self.__delayedDisconnect.start(seconds=5)
		elif status == conic.STATUS_CONNECTED:
			_moduleLogger.info("Connected to network")
			self._cancel_delayed_disconnect()
		else:
			_moduleLogger.info("Other status: %r" % (status, ))

	@misc_utils.log_exception(_moduleLogger)
	def _cancel_delayed_disconnect(self):
		_moduleLogger.info("Cancelling auto-log off")
		self.__delayedDisconnect.cancel()

	@misc_utils.log_exception(_moduleLogger)
	def _on_delayed_disconnect(self):
		if not self._connRef().session.is_logged_in():
			_moduleLogger.info("Received connection change event when not logged in")
			return
		try:
			self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR)
		except Exception:
			_moduleLogger.exception("Error durring disconnect")

class DisconnectOnShutdown(object):
	"""
	I'm unsure when I get notified of shutdown or if I have enough time to do
	anything about it, but thought this might help
	"""

	def __init__(self, connRef):
		self._connRef = connRef

		self._osso = None
		self._deviceState = None

	def start(self):
		if osso is not None:
			self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
			self._deviceState = osso.DeviceState(self._osso)
			self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
		else:
			_moduleLogger.warning("No device state support")

	def stop(self):
		try:
			self._deviceState.close()
		except AttributeError:
			pass # Either None or close was removed (in Fremantle)
		self._deviceState = None
		try:
			self._osso.close()
		except AttributeError:
			pass # Either None or close was removed (in Fremantle)
		self._osso = None

	@misc_utils.log_exception(_moduleLogger)
	def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
		"""
		@note Hildon specific
		"""
		try:
			self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_REQUESTED)
		except Exception:
			_moduleLogger.exception("Error durring disconnect")

class DelayEnableContactIntegration(object):

	def __init__(self, protocolName):
		self.__enableSystemContactSupport = telepathy_utils.EnableSystemContactIntegration(
			protocolName
		)
		self.__delayedEnable = gobject_utils.Async(self._on_delayed_enable)

	def start(self):
		self.__delayedEnable.start()

	def stop(self):
		self.__delayedEnable.cancel()

	@misc_utils.log_exception(_moduleLogger)
	def _on_delayed_enable(self):
		self.__enableSystemContactSupport.start()
<commit_msg>Reducing noise that misleads about whether a problem occurred<commit_after>import logging

import dbus
import telepathy

try:
	import conic as _conic
	conic = _conic
except (ImportError, OSError):
	conic = None

try:
	import osso as _osso
	osso = _osso
except (ImportError, OSError):
	osso = None

import constants
import util.coroutines as coroutines
import util.go_utils as gobject_utils
import util.tp_utils as telepathy_utils
import util.misc as misc_utils
import gvoice

_moduleLogger = logging.getLogger(__name__)

class NewGVConversations(object):

	def __init__(self, connRef):
		self._connRef = connRef
		self.__callback = None

	def start(self):
		self.__callback = coroutines.func_sink(
			coroutines.expand_positional(
				self._on_conversations_updated
			)
		)
		self._connRef().session.voicemails.updateSignalHandler.register_sink(
			self.__callback
		)
		self._connRef().session.texts.updateSignalHandler.register_sink(
			self.__callback
		)

	def stop(self):
		if self.__callback is None:
			_moduleLogger.info("New conversation monitor stopped without starting")
			return
		self._connRef().session.voicemails.updateSignalHandler.unregister_sink(
			self.__callback
		)
		self._connRef().session.texts.updateSignalHandler.unregister_sink(
			self.__callback
		)
		self.__callback = None

	@misc_utils.log_exception(_moduleLogger)
	def _on_conversations_updated(self, conv, conversationIds):
		_moduleLogger.debug("Incoming messages from: %r" % (conversationIds, ))
		for phoneNumber in conversationIds:
			h = self._connRef().get_handle_by_name(telepathy.HANDLE_TYPE_CONTACT, phoneNumber)
			# Just let the TextChannel decide whether it should be reported to the user or not
			props = self._connRef().generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
			if self._connRef()._channel_manager.channel_exists(props):
				continue

			# Maemo 4.1's RTComm opens a window for a chat regardless if a
			# message is received or not, so we need to do some filtering here
			mergedConv = conv.get_conversation(phoneNumber)
			newConversations = mergedConv.conversations
			newConversations = gvoice.conversations.filter_out_read(newConversations)
			newConversations = gvoice.conversations.filter_out_self(newConversations)
			newConversations = list(newConversations)
			if not newConversations:
				continue

			chan = self._connRef()._channel_manager.channel_for_props(props, signal=True)

class RefreshVoicemail(object):

	def __init__(self, connRef):
		self._connRef = connRef
		self._newChannelSignaller = telepathy_utils.NewChannelSignaller(self._on_new_channel)
		self._outstandingRequests = []
		self._isStarted = False

	def start(self):
		self._newChannelSignaller.start()
		self._isStarted = True

	def stop(self):
		if not self._isStarted:
			_moduleLogger.info("voicemail monitor stopped without starting")
			return
		_moduleLogger.info("Stopping voicemail refresh")
		self._newChannelSignaller.stop()

		# I don't want to trust whether the cancel happens within the current
		# callback or not which could be the deciding factor between invalid
		# iterators or infinite loops
		localRequests = [r for r in self._outstandingRequests]
		for request in localRequests:
			localRequests.cancel()

		self._isStarted = False

	@misc_utils.log_exception(_moduleLogger)
	def _on_new_channel(self, bus, serviceName, connObjectPath, channelObjectPath, channelType):
		if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
			return

		cmName = telepathy_utils.cm_from_path(connObjectPath)
		if cmName == constants._telepathy_implementation_name_:
			_moduleLogger.debug("Ignoring channels from self to prevent deadlock")
			return

		conn = telepathy.client.Connection(serviceName, connObjectPath)
		try:
			chan = telepathy.client.Channel(serviceName, channelObjectPath)
		except dbus.exceptions.UnknownMethodException:
			_moduleLogger.exception("Client might not have implemented a deprecated method")
			return
		missDetection = telepathy_utils.WasMissedCall(
			bus, conn, chan, self._on_missed_call, self._on_error_for_missed
		)
		self._outstandingRequests.append(missDetection)

	@misc_utils.log_exception(_moduleLogger)
	def _on_missed_call(self, missDetection):
		_moduleLogger.info("Missed a call")
		self._connRef().session.voicemailsStateMachine.reset_timers()
		self._outstandingRequests.remove(missDetection)

	@misc_utils.log_exception(_moduleLogger)
	def _on_error_for_missed(self, missDetection, reason):
		_moduleLogger.debug("Error: %r claims %r" % (missDetection, reason))
		self._outstandingRequests.remove(missDetection)

class TimedDisconnect(object):

	def __init__(self, connRef):
		self._connRef = connRef
		self.__delayedDisconnect = gobject_utils.Timeout(self._on_delayed_disconnect)

	def start(self):
		self.__delayedDisconnect.start(seconds=20)

	def stop(self):
		self.__delayedDisconnect.cancel()

	@misc_utils.log_exception(_moduleLogger)
	def _on_delayed_disconnect(self):
		_moduleLogger.info("Timed disconnect occurred")
		self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR)

class AutoDisconnect(object):

	def __init__(self, connRef):
		self._connRef = connRef
		if conic is not None:
			self.__connection = conic.Connection()
		else:
			self.__connection = None

		self.__connectionEventId = None
		self.__delayedDisconnect = gobject_utils.Timeout(self._on_delayed_disconnect)

	def start(self):
		if self.__connection is not None:
			self.__connectionEventId = self.__connection.connect("connection-event", self._on_connection_change)

	def stop(self):
		self._cancel_delayed_disconnect()

	@misc_utils.log_exception(_moduleLogger)
	def _on_connection_change(self, connection, event):
		"""
		@note Maemo specific
		"""
		status = event.get_status()
		error = event.get_error()
		iap_id = event.get_iap_id()
		bearer = event.get_bearer_type()

		if status == conic.STATUS_DISCONNECTED:
			_moduleLogger.info("Disconnected from network, starting countdown to logoff")
			self.__delayedDisconnect.start(seconds=5)
		elif status == conic.STATUS_CONNECTED:
			_moduleLogger.info("Connected to network")
			self._cancel_delayed_disconnect()
		else:
			_moduleLogger.info("Other status: %r" % (status, ))

	@misc_utils.log_exception(_moduleLogger)
	def _cancel_delayed_disconnect(self):
		_moduleLogger.info("Cancelling auto-log off")
		self.__delayedDisconnect.cancel()

	@misc_utils.log_exception(_moduleLogger)
	def _on_delayed_disconnect(self):
		if not self._connRef().session.is_logged_in():
			_moduleLogger.info("Received connection change event when not logged in")
			return
		try:
			self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR)
		except Exception:
			_moduleLogger.exception("Error durring disconnect")

class DisconnectOnShutdown(object):
	"""
	I'm unsure when I get notified of shutdown or if I have enough time to do
	anything about it, but thought this might help
	"""

	def __init__(self, connRef):
		self._connRef = connRef

		self._osso = None
		self._deviceState = None

	def start(self):
		if osso is not None:
			self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
			self._deviceState = osso.DeviceState(self._osso)
			self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
		else:
			_moduleLogger.warning("No device state support")

	def stop(self):
		try:
			self._deviceState.close()
		except AttributeError:
			pass # Either None or close was removed (in Fremantle)
		self._deviceState = None
		try:
			self._osso.close()
		except AttributeError:
			pass # Either None or close was removed (in Fremantle)
		self._osso = None

	@misc_utils.log_exception(_moduleLogger)
	def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
		"""
		@note Hildon specific
		"""
		try:
			self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_REQUESTED)
		except Exception:
			_moduleLogger.exception("Error durring disconnect")

class DelayEnableContactIntegration(object):

	def __init__(self, protocolName):
		self.__enableSystemContactSupport = telepathy_utils.EnableSystemContactIntegration(
			protocolName
		)
		self.__delayedEnable = gobject_utils.Async(self._on_delayed_enable)

	def start(self):
		self.__delayedEnable.start()

	def stop(self):
		self.__delayedEnable.cancel()

	@misc_utils.log_exception(_moduleLogger)
	def _on_delayed_enable(self):
		try:
			self.__enableSystemContactSupport.start()
		except dbus.DBusException, e:
			_moduleLogger.info("Contact integration seems to not be supported (%s)" % e)
<|endoftext|>


<commit_before>#!/usr/bin/env python
#
# Copyright 2002-2003 Michael D. Stenner
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

"""An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.

>>> import urllib2
>>> from keepalive import HTTPHandler
>>> keepalive_handler = HTTPHandler()
>>> opener = urllib2.build_opener(keepalive_handler)
>>> urllib2.install_opener(opener)
>>>
>>> fo = urllib2.urlopen('http://www.python.org')

To remove the handler, simply re-run build_opener with no arguments, and
install that opener.

You can explicitly close connections by using the close_connection()
method of the returned file-like object (described below) or you can
use the handler methods:

  close_connection(host)
  close_all()
  open_connections()

>>> keepalive_handler.close_all()

EXTRA ATTRIBUTES AND METHODS

  Upon a status of 200, the object returned has a few additional
  attributes and methods, which should not be used if you want to
  remain consistent with the normal urllib2-returned objects:

    close_connection()  -  close the connection to the host
    readlines()         -  you know, readlines()
    status              -  the return status (ie 404)
    reason              -  english translation of status (ie 'File not found')

  If you want the best of both worlds, use this inside an
  AttributeError-catching try:

  >>> try: status = fo.status
  >>> except AttributeError: status = None

  Unfortunately, these are ONLY there if status == 200, so it's not
  easy to distinguish between non-200 responses.  The reason is that
  urllib2 tries to do clever things with error codes 301, 302, 401,
  and 407, and it wraps the object upon return.

  You can optionally set the module-level global HANDLE_ERRORS to 0,
  in which case the handler will always return the object directly.
  If you like the fancy handling of errors, don't do this.  If you
  prefer to see your error codes, then do.

"""
from httplib import _CS_REQ_STARTED, _CS_REQ_SENT, CannotSendHeader

import threading
import urllib2
import httplib
import socket

VERSION = (0, 1)
#STRING_VERSION = '.'.join(map(str, VERSION))
DEBUG = 0
HANDLE_ERRORS = 1

class HTTPHandler(urllib2.HTTPHandler):
    def __init__(self):
        self._connections = {}

    def close_connection(self, host):
        """close connection to <host>
        host is the host:port spec, as in 'www.cnn.com:8080' as passed in.
        no error occurs if there is no connection to that host."""
        self._remove_connection(host, close=1)

    def open_connections(self):
        """return a list of connected hosts"""
        retVal = []
        currentThread = threading.currentThread()
        for thread, host in self._connections.keys():
            if thread == currentThread:
                retVal.append(host)
        return retVal

    def close_all(self):
        """close all open connections"""
        for _, conn in self._connections.items():
            conn.close()
        self._connections = {}

    def _remove_connection(self, host, close=0):
        key = self._get_connection_key(host)
        if self._connections.has_key(key):
            if close: self._connections[key].close()
            del self._connections[key]

    def _get_connection_key(self, host):
        return (threading.currentThread(), host)

    def _start_connection(self, h, req):
        h.clearheaders()
        try:
            if req.has_data():
                data = req.get_data()
                h.putrequest('POST', req.get_selector())
                if not req.headers.has_key('Content-type'):
                    h.putheader('Content-type',
                                'application/x-www-form-urlencoded')
                if not req.headers.has_key('Content-length'):
                    h.putheader('Content-length', '%d' % len(data))
            else:
                h.putrequest('GET', req.get_selector())
        except socket.error, err:
            raise urllib2.URLError(err)

        for args in self.parent.addheaders:
            h.putheader(*args)
        for k, v in req.headers.items():
            h.putheader(k, v)
        h.endheaders()
        if req.has_data():
            h.send(data)

    def do_open(self, http_class, req):
        host = req.get_host()
        if not host:
            raise urllib2.URLError('no host given')

        try:
            need_new_connection = 1
            key = self._get_connection_key(host)
            h = self._connections.get(key)
            if not h is None:
                try:
                    self._start_connection(h, req)
                except socket.error, e:
                    r = None
                else:
                    try: r = h.getresponse()
                    except httplib.ResponseNotReady, e: r = None

                if r is None or r.version == 9:
                    # httplib falls back to assuming HTTP 0.9 if it gets a
                    # bad header back.  This is most likely to happen if
                    # the socket has been closed by the server since we
                    # last used the connection.
                    if DEBUG: print "failed to re-use connection to %s" % host
                    h.close()
                else:
                    if DEBUG: print "re-using connection to %s" % host
                    need_new_connection = 0
            if need_new_connection:
                if DEBUG: print "creating new connection to %s" % host
                h = http_class(host)
                self._connections[key] = h
                self._start_connection(h, req)
                r = h.getresponse()
        except socket.error, err:
            raise urllib2.URLError(err)

        # if not a persistent connection, don't try to reuse it
        if r.will_close: self._remove_connection(host)

        if DEBUG:
            print "STATUS: %s, %s" % (r.status, r.reason)
        r._handler = self
        r._host = host
        r._url = req.get_full_url()

        #if r.status == 200 or not HANDLE_ERRORS:
            #return r
        if r.status == 200 or not HANDLE_ERRORS:
            # [speedplane] Must return an adinfourl object
            resp = urllib2.addinfourl(r, r.msg, req.get_full_url())
            resp.code = r.status
            resp.msg = r.reason
            return resp;
        else:
            return self.parent.error('http', req, r, r.status, r.reason, r.msg)

    def http_open(self, req):
        return self.do_open(HTTPConnection, req)

class HTTPResponse(httplib.HTTPResponse):

    # we need to subclass HTTPResponse in order to
    # 1) add readline() and readlines() methods
    # 2) add close_connection() methods
    # 3) add info() and geturl() methods

    # in order to add readline(), read must be modified to deal with a
    # buffer.  example: readline must read a buffer and then spit back
    # one line at a time.  The only real alternative is to read one
    # BYTE at a time (ick).  Once something has been read, it can't be
    # put back (ok, maybe it can, but that's even uglier than this),
    # so if you THEN do a normal read, you must first take stuff from
    # the buffer.

    # the read method wraps the original to accomodate buffering,
    # although read() never adds to the buffer.
    # Both readline and readlines have been stolen with almost no
    # modification from socket.py

    def __init__(self, sock, debuglevel=0, strict=0, method=None):
        if method: # the httplib in python 2.3 uses the method arg
            httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
        else: # 2.2 doesn't
            httplib.HTTPResponse.__init__(self, sock, debuglevel)
        self.fileno = sock.fileno
        self._rbuf = ''
        self._rbufsize = 8096
        self._handler = None # inserted by the handler later
        self._host = None    # (same)
        self._url = None     # (same)

    _raw_read = httplib.HTTPResponse.read

    def close_connection(self):
        self.close()
        self._handler._remove_connection(self._host, close=1)

    def info(self):
        return self.msg

    def geturl(self):
        return self._url

    def read(self, amt=None):
        # the _rbuf test is only in this first if for speed.  It's not
        # logically necessary
        if self._rbuf and not amt is None:
            L = len(self._rbuf)
            if amt > L:
                amt -= L
            else:
                s = self._rbuf[:amt]
                self._rbuf = self._rbuf[amt:]
                return s

        s = self._rbuf + self._raw_read(amt)
        self._rbuf = ''
        return s

    def readline(self, limit=-1):
        data = ""
        i = self._rbuf.find('\n')
        while i < 0 and not (0 < limit <= len(self._rbuf)):
            new = self._raw_read(self._rbufsize)
            if not new: break
            i = new.find('\n')
            if i >= 0: i = i + len(self._rbuf)
            self._rbuf = self._rbuf + new
        if i < 0: i = len(self._rbuf)
        else: i = i+1
        if 0 <= limit < len(self._rbuf): i = limit
        data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
        return data

    def readlines(self, sizehint = 0):
        total = 0
        list = []
        while 1:
            line = self.readline()
            if not line: break
            list.append(line)
            total += len(line)
            if sizehint and total >= sizehint:
                break
        return list

class HTTPConnection(httplib.HTTPConnection):
    # use the modified response class
    response_class = HTTPResponse
    _headers = {}

    def clearheaders(self):
        self._headers.clear()

    def putheader(self, header, value):
        """Send a request header line to the server.

        For example: h.putheader('Accept', 'text/html')
        """
        if self.__state != _CS_REQ_STARTED:
            raise CannotSendHeader()

        self._headers[header] = value

    def endheaders(self):
        """Indicate that the last header line has been sent to the server."""

        if self.__state == _CS_REQ_STARTED:
            self.__state = _CS_REQ_SENT
        else:
            raise CannotSendHeader()

        for header in ['Host', 'Accept-Encoding']:
            if header in self._headers:
                str = '%s: %s' % (header, self._headers[header])
                self._output(str)
                del self._headers[header]

        for header, value in self._headers.items():
            str = '%s: %s' % (header, value)
            self._output(str)

        self._send_output()

#########################################################################
#####   TEST FUNCTIONS
#########################################################################

def error_handler(url):
    global HANDLE_ERRORS
    orig = HANDLE_ERRORS
    keepalive_handler = HTTPHandler()
    opener = urllib2.build_opener(keepalive_handler)
    urllib2.install_opener(opener)
    pos = {0: 'off', 1: 'on'}
    for i in (0, 1):
        print "  fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)
        HANDLE_ERRORS = i
        try:
            fo = urllib2.urlopen(url)
            foo = fo.read()
            fo.close()
            try: status, reason = fo.status, fo.reason
            except AttributeError: status, reason = None, None
        except IOError, e:
            print "  EXCEPTION: %s" % e
            raise
        else:
            print "  status = %s, reason = %s" % (status, reason)
    HANDLE_ERRORS = orig
    hosts = keepalive_handler.open_connections()
    print "open connections:", ' '.join(hosts)
    keepalive_handler.close_all()

def continuity(url):
    import md5
    format = '%25s: %s'

    # first fetch the file with the normal http handler
    opener = urllib2.build_opener()
    urllib2.install_opener(opener)
    fo = urllib2.urlopen(url)
    foo = fo.read()
    fo.close()
    m = md5.new(foo)
    print format % ('normal urllib', m.hexdigest())

    # now install the keepalive handler and try again
    opener = urllib2.build_opener(HTTPHandler())
    urllib2.install_opener(opener)

    fo = urllib2.urlopen(url)
    foo = fo.read()
    fo.close()
    m = md5.new(foo)
    print format % ('keepalive read', m.hexdigest())

    fo = urllib2.urlopen(url)
    foo = ''
    while 1:
        f = fo.readline()
        if f: foo = foo + f
        else: break
    fo.close()
    m = md5.new(foo)
    print format % ('keepalive readline', m.hexdigest())

def comp(N, url):
    print '  making %i connections to:\n  %s' % (N, url)

    sys.stdout.write('  first using the normal urllib handlers')
    # first use normal opener
    opener = urllib2.build_opener()
    urllib2.install_opener(opener)
    t1 = fetch(N, url)
    print '  TIME: %.3f s' % t1

    sys.stdout.write('  now using the keepalive handler       ')
    # now install the keepalive handler and try again
    opener = urllib2.build_opener(HTTPHandler())
    urllib2.install_opener(opener)
    t2 = fetch(N, url)
    print '  TIME: %.3f s' % t2
    print '  improvement factor: %.2f' % (t1/t2, )

def fetch(N, url, delay=0):
    lens = []
    starttime = time.time()
    for i in range(N):
        if delay and i > 0: time.sleep(delay)
        fo = urllib2.urlopen(url)
        foo = fo.read()
        fo.close()
        lens.append(len(foo))
    diff = time.time() - starttime

    j = 0
    for i in lens[1:]:
        j = j + 1
        if not i == lens[0]:
            print "WARNING: inconsistent length on read %i: %i" % (j, i)

    return diff

def test(url, N=10):
    print "checking error hander (do this on a non-200)"
    try: error_handler(url)
    except IOError, e:
        print "exiting - exception will prevent further tests"
        sys.exit()
    print
    print "performing continuity test (making sure stuff isn't corrupted)"
    continuity(url)
    print
    print "performing speed comparison"
    comp(N, url)

if __name__ == '__main__':
    import time
    import sys
    try:
        N = int(sys.argv[1])
        url = sys.argv[2]
    except:
        print "%s <integer> <url>" % sys.argv[0]
    else:
        test(url, N)
<commit_msg>patching and patching and patching<commit_after>#!/usr/bin/env python
#
# Copyright 2002-2003 Michael D. Stenner
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

"""An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.

>>> import urllib2
>>> from keepalive import HTTPHandler
>>> keepalive_handler = HTTPHandler()
>>> opener = urllib2.build_opener(keepalive_handler)
>>> urllib2.install_opener(opener)
>>>
>>> fo = urllib2.urlopen('http://www.python.org')

To remove the handler, simply re-run build_opener with no arguments, and
install that opener.

You can explicitly close connections by using the close_connection()
method of the returned file-like object (described below) or you can
use the handler methods:

  close_connection(host)
  close_all()
  open_connections()

>>> keepalive_handler.close_all()

EXTRA ATTRIBUTES AND METHODS

  Upon a status of 200, the object returned has a few additional
  attributes and methods, which should not be used if you want to
  remain consistent with the normal urllib2-returned objects:

    close_connection()  -  close the connection to the host
    readlines()         -  you know, readlines()
    status              -  the return status (ie 404)
    reason              -  english translation of status (ie 'File not found')

  If you want the best of both worlds, use this inside an
  AttributeError-catching try:

  >>> try: status = fo.status
  >>> except AttributeError: status = None

  Unfortunately, these are ONLY there if status == 200, so it's not
  easy to distinguish between non-200 responses.  The reason is that
  urllib2 tries to do clever things with error codes 301, 302, 401,
  and 407, and it wraps the object upon return.

  You can optionally set the module-level global HANDLE_ERRORS to 0,
  in which case the handler will always return the object directly.
  If you like the fancy handling of errors, don't do this.  If you
  prefer to see your error codes, then do.

"""
from httplib import _CS_REQ_STARTED, _CS_REQ_SENT, _CS_IDLE, CannotSendHeader

import threading
import urllib2
import httplib
import socket

VERSION = (0, 1)
#STRING_VERSION = '.'.join(map(str, VERSION))
DEBUG = 0
HANDLE_ERRORS = 1

class HTTPHandler(urllib2.HTTPHandler):
    def __init__(self):
        self._connections = {}

    def close_connection(self, host):
        """close connection to <host>
        host is the host:port spec, as in 'www.cnn.com:8080' as passed in.
        no error occurs if there is no connection to that host."""
        self._remove_connection(host, close=1)

    def open_connections(self):
        """return a list of connected hosts"""
        retVal = []
        currentThread = threading.currentThread()
        for thread, host in self._connections.keys():
            if thread == currentThread:
                retVal.append(host)
        return retVal

    def close_all(self):
        """close all open connections"""
        for _, conn in self._connections.items():
            conn.close()
        self._connections = {}

    def _remove_connection(self, host, close=0):
        key = self._get_connection_key(host)
        if self._connections.has_key(key):
            if close: self._connections[key].close()
            del self._connections[key]

    def _get_connection_key(self, host):
        return (threading.currentThread(), host)

    def _start_connection(self, h, req):
        h.clearheaders()
        try:
            if req.has_data():
                data = req.get_data()
                h.putrequest('POST', req.get_selector())
                if not req.headers.has_key('Content-type'):
                    h.putheader('Content-type',
                                'application/x-www-form-urlencoded')
                if not req.headers.has_key('Content-length'):
                    h.putheader('Content-length', '%d' % len(data))
            else:
                h.putrequest('GET', req.get_selector())

            for args in self.parent.addheaders:
                h.putheader(*args)
            for k, v in req.headers.items():
                h.putheader(k, v)
            h.endheaders()
            if req.has_data():
                h.send(data)
        except:
            h.close()
            raise

    def do_open(self, http_class, req):
        host = req.get_host()
        if not host:
            raise urllib2.URLError('no host given')

        try:
            need_new_connection = 1
            key = self._get_connection_key(host)
            h = self._connections.get(key)
            if not h is None:
                try:
                    self._start_connection(h, req)
                except socket.error, e:
                    r = None
                else:
                    try: r = h.getresponse()
                    except httplib.ResponseNotReady, e: r = None

                if r is None or r.version == 9:
                    # httplib falls back to assuming HTTP 0.9 if it gets a
                    # bad header back.  This is most likely to happen if
                    # the socket has been closed by the server since we
                    # last used the connection.
                    if DEBUG: print "failed to re-use connection to %s" % host
                    h.close()
                else:
                    if DEBUG: print "re-using connection to %s" % host
                    need_new_connection = 0
            if need_new_connection:
                if DEBUG: print "creating new connection to %s" % host
                h = http_class(host)
                self._connections[key] = h
                self._start_connection(h, req)
                r = h.getresponse()
        except socket.error, err:
            raise urllib2.URLError(err)

        # if not a persistent connection, don't try to reuse it
        if r.will_close: self._remove_connection(host)

        if DEBUG:
            print "STATUS: %s, %s" % (r.status, r.reason)
        r._handler = self
        r._host = host
        r._url = req.get_full_url()

        #if r.status == 200 or not HANDLE_ERRORS:
            #return r
        if r.status == 200 or not HANDLE_ERRORS:
            # [speedplane] Must return an adinfourl object
            resp = urllib2.addinfourl(r, r.msg, req.get_full_url())
            resp.code = r.status
            resp.msg = r.reason
            return resp;
        else:
            return self.parent.error('http', req, r, r.status, r.reason, r.msg)

    def http_open(self, req):
        return self.do_open(HTTPConnection, req)

class HTTPResponse(httplib.HTTPResponse):

    # we need to subclass HTTPResponse in order to
    # 1) add readline() and readlines() methods
    # 2) add close_connection() methods
    # 3) add info() and geturl() methods

    # in order to add readline(), read must be modified to deal with a
    # buffer.  example: readline must read a buffer and then spit back
    # one line at a time.  The only real alternative is to read one
    # BYTE at a time (ick).  Once something has been read, it can't be
    # put back (ok, maybe it can, but that's even uglier than this),
    # so if you THEN do a normal read, you must first take stuff from
    # the buffer.

    # the read method wraps the original to accomodate buffering,
    # although read() never adds to the buffer.
    # Both readline and readlines have been stolen with almost no
    # modification from socket.py

    def __init__(self, sock, debuglevel=0, strict=0, method=None):
        if method: # the httplib in python 2.3 uses the method arg
            httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
        else: # 2.2 doesn't
            httplib.HTTPResponse.__init__(self, sock, debuglevel)
        self.fileno = sock.fileno
        self._rbuf = ''
        self._rbufsize = 8096
        self._handler = None # inserted by the handler later
        self._host = None    # (same)
        self._url = None     # (same)

    _raw_read = httplib.HTTPResponse.read

    def close_connection(self):
        self.close()
        self._handler._remove_connection(self._host, close=1)

    def info(self):
        return self.msg

    def geturl(self):
        return self._url

    def read(self, amt=None):
        # the _rbuf test is only in this first if for speed.  It's not
        # logically necessary
        if self._rbuf and not amt is None:
            L = len(self._rbuf)
            if amt > L:
                amt -= L
            else:
                s = self._rbuf[:amt]
                self._rbuf = self._rbuf[amt:]
                return s

        s = self._rbuf + self._raw_read(amt)
        self._rbuf = ''
        return s

    def readline(self, limit=-1):
        data = ""
        i = self._rbuf.find('\n')
        while i < 0 and not (0 < limit <= len(self._rbuf)):
            new = self._raw_read(self._rbufsize)
            if not new: break
            i = new.find('\n')
            if i >= 0: i = i + len(self._rbuf)
            self._rbuf = self._rbuf + new
        if i < 0: i = len(self._rbuf)
        else: i = i+1
        if 0 <= limit < len(self._rbuf): i = limit
        data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
        return data

    def readlines(self, sizehint = 0):
        total = 0
        list = []
        while 1:
            line = self.readline()
            if not line: break
            list.append(line)
            total += len(line)
            if sizehint and total >= sizehint:
                break
        return list

class HTTPConnection(httplib.HTTPConnection):
    # use the modified response class
    response_class = HTTPResponse
    _headers = {}

    def clearheaders(self):
        self._headers.clear()

    def putheader(self, header, value):
        """Send a request header line to the server.

        For example: h.putheader('Accept', 'text/html')
        """
        if self.__state != _CS_REQ_STARTED:
            raise CannotSendHeader()

        self._headers[header] = value

    def endheaders(self):
        """Indicate that the last header line has been sent to the server."""

        if self.__state == _CS_REQ_STARTED:
            self.__state = _CS_REQ_SENT
        else:
            raise CannotSendHeader()

        for header in ['Host', 'Accept-Encoding']:
            if header in self._headers:
                str = '%s: %s' % (header, self._headers[header])
                self._output(str)
                del self._headers[header]

        for header, value in self._headers.items():
            str = '%s: %s' % (header, value)
            self._output(str)

        self._send_output()

#########################################################################
#####   TEST FUNCTIONS
#########################################################################

def error_handler(url):
    global HANDLE_ERRORS
    orig = HANDLE_ERRORS
    keepalive_handler = HTTPHandler()
    opener = urllib2.build_opener(keepalive_handler)
    urllib2.install_opener(opener)
    pos = {0: 'off', 1: 'on'}
    for i in (0, 1):
        print "  fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)
        HANDLE_ERRORS = i
        try:
            fo = urllib2.urlopen(url)
            foo = fo.read()
            fo.close()
            try: status, reason = fo.status, fo.reason
            except AttributeError: status, reason = None, None
        except IOError, e:
            print "  EXCEPTION: %s" % e
            raise
        else:
            print "  status = %s, reason = %s" % (status, reason)
    HANDLE_ERRORS = orig
    hosts = keepalive_handler.open_connections()
    print "open connections:", ' '.join(hosts)
    keepalive_handler.close_all()

def continuity(url):
    import md5
    format = '%25s: %s'

    # first fetch the file with the normal http handler
    opener = urllib2.build_opener()
    urllib2.install_opener(opener)
    fo = urllib2.urlopen(url)
    foo = fo.read()
    fo.close()
    m = md5.new(foo)
    print format % ('normal urllib', m.hexdigest())

    # now install the keepalive handler and try again
    opener = urllib2.build_opener(HTTPHandler())
    urllib2.install_opener(opener)

    fo = urllib2.urlopen(url)
    foo = fo.read()
    fo.close()
    m = md5.new(foo)
    print format % ('keepalive read', m.hexdigest())

    fo = urllib2.urlopen(url)
    foo = ''
    while 1:
        f = fo.readline()
        if f: foo = foo + f
        else: break
    fo.close()
    m = md5.new(foo)
    print format % ('keepalive readline', m.hexdigest())

def comp(N, url):
    print '  making %i connections to:\n  %s' % (N, url)

    sys.stdout.write('  first using the normal urllib handlers')
    # first use normal opener
    opener = urllib2.build_opener()
    urllib2.install_opener(opener)
    t1 = fetch(N, url)
    print '  TIME: %.3f s' % t1

    sys.stdout.write('  now using the keepalive handler       ')
    # now install the keepalive handler and try again
    opener = urllib2.build_opener(HTTPHandler())
    urllib2.install_opener(opener)
    t2 = fetch(N, url)
    print '  TIME: %.3f s' % t2
    print '  improvement factor: %.2f' % (t1/t2, )

def fetch(N, url, delay=0):
    lens = []
    starttime = time.time()
    for i in range(N):
        if delay and i > 0: time.sleep(delay)
        fo = urllib2.urlopen(url)
        foo = fo.read()
        fo.close()
        lens.append(len(foo))
    diff = time.time() - starttime

    j = 0
    for i in lens[1:]:
        j = j + 1
        if not i == lens[0]:
            print "WARNING: inconsistent length on read %i: %i" % (j, i)

    return diff

def test(url, N=10):
    print "checking error hander (do this on a non-200)"
    try: error_handler(url)
    except IOError, e:
        print "exiting - exception will prevent further tests"
        sys.exit()
    print
    print "performing continuity test (making sure stuff isn't corrupted)"
    continuity(url)
    print
    print "performing speed comparison"
    comp(N, url)

if __name__ == '__main__':
    import time
    import sys
    try:
        N = int(sys.argv[1])
        url = sys.argv[2]
    except:
        print "%s <integer> <url>" % sys.argv[0]
    else:
        test(url, N)
<|endoftext|>


<commit_before>import os
import shutil
import sys
import cv2

def run():
    FOLDER_NAME = "crop_this"  # The folder it looks for in the current directory
    face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

    cropped_folder = "cropped_it"

    if not os.path.isdir(FOLDER_NAME):
        print("\nNo folder named '" + FOLDER_NAME + "' found in the current directory.\nExiting...")
        return

    if os.path.isdir(cropped_folder):
        shutil.rmtree(cropped_folder)

    os.mkdir(cropped_folder)
    for folder in os.listdir(FOLDER_NAME):
        i = 0
        os.chdir(FOLDER_NAME + "/" + folder)

        images = [img for img in os.listdir() if img[-4:] == ".jpg"]

        print(images)
        os.mkdir("../../" + cropped_folder + '/' + folder)

        for image in images:

            img = cv2.imread(image)
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            print(image + " read.")

            faces = face_cascade.detectMultiScale(gray, 1.2, 5)

            if len(faces) is not 0:
                print("face detected")
                for (x, y, w, h) in faces:
                    cropped = img[y:y + h, x:x + w]

                    filepath = "../../" + cropped_folder + '/' + folder + '/' + image
                    cv2.imwrite(filepath, cropped)
                    break
                i += 1
            else:
                print("face not detected")

        os.chdir("../..")
        pass

    cv2.destroyAllWindows()
    sys.exit(0)

if __name__ == '__main__':
    run()
<commit_msg>Updated face_cropper.py<commit_after>import os
import shutil
import sys
import cv2

def run():
    FOLDER_NAME = "crop_this"
    cropped_folder = "cropped_it"

    face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

    if not os.path.isdir(FOLDER_NAME):
        print("\nNo folder named '" + FOLDER_NAME + "' found in the current directory.\nExiting...")
        return

    if os.path.isdir(cropped_folder):
        shutil.rmtree(cropped_folder)

    os.mkdir(cropped_folder)

    for folder in os.listdir(FOLDER_NAME):

        os.chdir(FOLDER_NAME + "/" + folder)

        # '.jpg' can be changed to the required file format to look for.
        images = [img for img in os.listdir() if img[-4:] == ".jpg"]

        os.mkdir("../../" + cropped_folder + '/' + folder)

        for image in images:

            img = cv2.imread(image)
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            print("Processing " + image)

            # Scale factor (2nd parameter) can be varied according to application - default 1.2
            faces = face_cascade.detectMultiScale(gray, 1.2, 5)

            if len(faces) is not 0:

                print("Face detected")

                for (x, y, w, h) in faces:
                    cropped = img[y:y + h, x:x + w]
                    file_path = "../../" + cropped_folder + '/' + folder + '/' + image
                    cv2.imwrite(file_path, cropped)

                    break
            else:
                print("NO face detected")

        os.chdir("../..")

    sys.exit(0)

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


<commit_before>from time import time
from sys import stdout
from warnings import warn
import shutil,os

def isnotebook():
    try:
        from google import colab
        return True
    except:
        pass
    try:
        shell = get_ipython().__class__.__name__
        if shell == 'ZMQInteractiveShell':
            return True   # Jupyter notebook, Spyder or qtconsole
        elif shell == 'TerminalInteractiveShell':
            return False  # Terminal running IPython
        else:
            return False  # Other type (?)
    except NameError:
        return False      # Probably standard Python interpreter

IN_NOTEBOOK = isnotebook()
if IN_NOTEBOOK:
    try:
        from IPython.display import clear_output, display, HTML
        import matplotlib.pyplot as plt
    except:
        warn("Couldn't import ipywidgets properly, progress bar will use console behavior")
        IN_NOTEBOOK = False

__all__ = ['master_bar', 'progress_bar']

def format_time(t):
    t = int(t)
    h,m,s = t//3600, (t//60)%60, t%60
    if h!= 0: return f'{h}:{m:02d}:{s:02d}'
    else:     return f'{m:02d}:{s:02d}'

class ProgressBar():
    update_every = 0.2

    def __init__(self, gen, total=None, display=True, leave=True, parent=None, auto_update=True):
        self._gen = gen
        self.auto_update = auto_update
        self.total = len(gen) if total is None else total
        self.parent = parent
        if parent is None: self.leave,self.display = leave,display
        else:
            self.leave,self.display=False,False
            parent.add_child(self)
        self.comment = ''
        if not self.auto_update:
            self.on_iter_begin()
            self.update(0)

    def on_iter_begin(self): pass
    def on_interrupt(self): pass
    def on_iter_end(self): pass
    def on_update(self, val, text): pass

    def __iter__(self):
        self.on_iter_begin()
        self.update(0)
        try:
            for i,o in enumerate(self._gen):
                yield o
                if self.auto_update: self.update(i+1)
        except:
            self.on_interrupt()
            raise
        self.on_iter_end()

    def update(self, val):
        if val == 0:
            self.start_t = self.last_t = time()
            self.pred_t = 0
            self.last_v,self.wait_for = 0,1
            self.update_bar(0)
        elif val >= self.last_v + self.wait_for or val == self.total:
            cur_t = time()
            avg_t = (cur_t - self.start_t) / val
            self.wait_for = max(int(self.update_every / (avg_t+1e-8)),1)
            self.pred_t = avg_t * self.total
            self.last_v,self.last_t = val,cur_t
            self.update_bar(val)
            if not self.auto_update and val >= self.total:
                self.on_iter_end()

    def update_bar(self, val):
        elapsed_t = self.last_t - self.start_t
        remaining_t = format_time(self.pred_t - elapsed_t)
        elapsed_t = format_time(elapsed_t)
        end = '' if len(self.comment) == 0 else f' {self.comment}'
        if self.total == 0:
            warn("Your generator is empty.")
            self.on_update(0, '100% [0/0]')
        else: self.on_update(val, f'{100 * val/self.total:.2f}% [{val}/{self.total} {elapsed_t}<{remaining_t}{end}]')

class MasterBar():
    def __init__(self, gen, cls, total=None): self.first_bar = cls(gen, total=total, display=False)

    def __iter__(self):
        self.on_iter_begin()
        for o in self.first_bar: yield o
        self.on_iter_end()

    def on_iter_begin(self): self.start_t = time()
    def on_iter_end(self): pass
    def add_child(self, child): pass
    def write(self, line):      pass
    def update_graph(self, graphs, x_bounds, y_bounds): pass

def html_progress_bar(value, total, label, interrupted=False):
    bar_style = 'progress-bar-interrupted' if interrupted else ''
    return f"""

      <progress value='{value}' class='{bar_style}' max='{total}', style='width:300px; height:20px; vertical-align: middle;'></progress>
      {label}

    """

def text2html_table(items):
    "Put the texts in `items` in an HTML table."
    html_code = f"""
\n"""
    html_code += f"""
\n
\n"""
    for i in items[0]: html_code += f"      {i}	\n"
    html_code += f"
\n
\n
\n"
    for line in items[1:]:
        html_code += "
\n"
        for i in line: html_code += f"      {i}	\n"
        html_code += "
\n"
    html_code += "
\n

"
    return html_code

class NBProgressBar(ProgressBar):
    def __init__(self, gen, total=None, display=True, leave=True, parent=None, auto_update=True):
        self.progress = html_progress_bar(0, len(gen) if total is None else total, "")
        super().__init__(gen, total, display, leave, parent, auto_update)

    def on_iter_begin(self):
        if self.display: self.out = display(HTML(self.progress), display_id=True)
        self.is_active=True

    def on_interrupt(self):
        self.on_update(0, 'Interrupted', interrupted=True)
        if self.parent is not None: self.parent.on_interrupt()
        self.on_iter_end()

    def on_iter_end(self):
        if not self.leave and self.display: clear_output()#self.out.update(HTML(''))
        self.is_active=False

    def on_update(self, val, text, interrupted=False):
        self.progress = html_progress_bar(val, self.total, text, interrupted)
        if self.display: self.out.update(HTML(self.progress))
        elif self.parent is not None: self.parent.show()

class NBMasterBar(MasterBar):
    names = ['train', 'valid']
    def __init__(self, gen, total=None, hide_graph=False, order=None, clean_on_interrupt=False):
        super().__init__(gen, NBProgressBar, total)
        self.report, self.clean_on_interrupt = [], clean_on_interrupt
        self.text,self.lines = "",[]
        self.html_code = '\n'.join([self.first_bar.progress, self.text])
        if order is None: order = ['pb1', 'text', 'pb2']
        self.inner_dict = {'pb1':self.first_bar.progress, 'text':self.text}
        self.hide_graph,self.order = hide_graph,order

    def on_iter_begin(self):
        super().on_iter_begin()
        self.out = display(HTML(self.html_code), display_id=True)

    def on_interrupt(self):
        if self.clean_on_interrupt: self.out.update(HTML(''))

    def on_iter_end(self):
        if hasattr(self, 'fig'):
            plt.close()
            self.out2.update(self.fig)
        total_time = format_time(time() - self.start_t)
        if self.text.endswith('
'): self.text = self.text[:-3]
        self.out.update(HTML(f'Total time: {total_time}
' + self.text))

    def add_child(self, child):
        self.child = child
        self.inner_dict['pb2'] = self.child.progress
        self.show()

    def show(self):
        self.inner_dict['pb1'], self.inner_dict['text'] = self.first_bar.progress, self.text
        if 'pb2' in self.inner_dict: self.inner_dict['pb2'] = self.child.progress
        to_show = [name for name in self.order if name in self.inner_dict.keys()]
        self.html_code = '\n'.join([self.inner_dict[n] for n in to_show])
        self.out.update(HTML(self.html_code))

    def write(self, line, table=False):
        if not table: self.text += line + "
"
        else:
            self.lines.append(line)
            self.text = text2html_table(self.lines)

    def show_imgs(self, imgs, titles=None, cols=4, imgsize=4, figsize=None):
        if self.hide_graph: return
        rows = len(imgs)//cols if len(imgs)%cols == 0 else len(imgs)//cols + 1
        plt.close()
        if figsize is None: figsize = (imgsize*cols, imgsize*rows)
        self.fig, axs = plt.subplots(rows, cols, figsize=figsize)
        if titles is None: titles = [None] * len(imgs)
        for img, ax, title in zip(imgs, axs.flatten(), titles): img.show(ax=ax, title=title)
        for ax in axs.flatten()[len(imgs):]: ax.axis('off')
        if not hasattr(self, 'out2'): self.out2 = display(self.fig, display_id=True)
        else: self.out2.update(self.fig)

    def update_graph(self, graphs, x_bounds=None, y_bounds=None):
        if self.hide_graph: return
        if not hasattr(self, 'fig'):
            self.fig, self.ax = plt.subplots(1, figsize=(6,4))
            self.out2 = display(self.ax.figure, display_id=True)
        self.ax.clear()
        if len(self.names) < len(graphs): self.names += [''] * (len(graphs) - len(self.names))
        for g,n in zip(graphs,self.names): self.ax.plot(*g, label=n)
        self.ax.legend(loc='upper right')
        if x_bounds is not None: self.ax.set_xlim(*x_bounds)
        if y_bounds is not None: self.ax.set_ylim(*y_bounds)
        self.out2.update(self.ax.figure)

class ConsoleProgressBar(ProgressBar):
    fill:str='█'

    def __init__(self, gen, total=None, display=True, leave=True, parent=None, auto_update=True, txt_len=60):
        cols,_ = shutil.get_terminal_size((100, 40))
        self.length = cols-txt_len
        self.max_len,self.prefix = 0,''
        super().__init__(gen, total, display, leave, parent, auto_update)

    def on_interrupt(self):
        self.on_iter_end()

    def on_iter_end(self):
        if not self.leave and printing():
            print(f'\r{self.prefix}' + ' ' * (self.max_len - len(f'\r{self.prefix}')), end = '\r')

    def on_update(self, val, text):
        if self.display:
            filled_len = int(self.length * val // self.total)
            bar = self.fill * filled_len + '-' * (self.length - filled_len)
            to_write = f'\r{self.prefix} |{bar}| {text}'
            if len(to_write) > self.max_len: self.max_len=len(to_write)
            if printing(): WRITER_FN(to_write, end = '\r')

class ConsoleMasterBar(MasterBar):
    def __init__(self, gen, total=None, hide_graph=False, order=None, clean_on_interrupt=False):
        super().__init__(gen, ConsoleProgressBar, total)

    def add_child(self, child):
        self.child = child
        self.child.prefix = f'Epoch {self.first_bar.last_v+1}/{self.first_bar.total} :'
        self.child.display = True

    def on_iter_begin(self):
        super().on_iter_begin()
        if SAVE_PATH is not None and os.path.exists(SAVE_PATH) and not SAVE_APPEND:
            with open(SAVE_PATH, 'w') as f: f.write('')

    def write(self, line, table=False):
        if table:
            text = ''
            if not hasattr(self, 'names'):
                self.names = [name + ' ' * (8-len(name)) if len(name) < 8 else name for name in line]
                text = '  '.join(self.names)
            else:
                for (t,name) in zip(line,self.names): text += t + ' ' * (2 + len(name)-len(t))
            print_and_maybe_save(text)
        else: print_and_maybe_save(line)

    def on_iter_end(self):
        total_time = format_time(time() - self.start_t)
        print_and_maybe_save(f'Total time: {total_time}')

    def show_imgs(*args): pass
    def update_graph(*args): pass

NO_BAR = False
WRITER_FN = print
SAVE_PATH = None
SAVE_APPEND = False

def print_and_maybe_save(line):
    WRITER_FN(line)
    if SAVE_PATH is not None:
        attr = "a" if os.path.exists(SAVE_PATH) else "w"
        with open(SAVE_PATH, attr) as f: f.write(line + '\n')

def printing():
    return False if NO_BAR else (stdout.isatty() or IN_NOTEBOOK)

if IN_NOTEBOOK: master_bar, progress_bar = NBMasterBar, NBProgressBar
else:           master_bar, progress_bar = ConsoleMasterBar, ConsoleProgressBar

def force_console_behavior():
    return ConsoleMasterBar, ConsoleProgressBar
<commit_msg>Add arg for total time<commit_after>from time import time
from sys import stdout
from warnings import warn
import shutil,os

def isnotebook():
    try:
        from google import colab
        return True
    except:
        pass
    try:
        shell = get_ipython().__class__.__name__
        if shell == 'ZMQInteractiveShell':
            return True   # Jupyter notebook, Spyder or qtconsole
        elif shell == 'TerminalInteractiveShell':
            return False  # Terminal running IPython
        else:
            return False  # Other type (?)
    except NameError:
        return False      # Probably standard Python interpreter

IN_NOTEBOOK = isnotebook()
if IN_NOTEBOOK:
    try:
        from IPython.display import clear_output, display, HTML
        import matplotlib.pyplot as plt
    except:
        warn("Couldn't import ipywidgets properly, progress bar will use console behavior")
        IN_NOTEBOOK = False

__all__ = ['master_bar', 'progress_bar']

def format_time(t):
    t = int(t)
    h,m,s = t//3600, (t//60)%60, t%60
    if h!= 0: return f'{h}:{m:02d}:{s:02d}'
    else:     return f'{m:02d}:{s:02d}'

class ProgressBar():
    update_every = 0.2

    def __init__(self, gen, total=None, display=True, leave=True, parent=None, auto_update=True):
        self._gen = gen
        self.auto_update = auto_update
        self.total = len(gen) if total is None else total
        self.parent = parent
        if parent is None: self.leave,self.display = leave,display
        else:
            self.leave,self.display=False,False
            parent.add_child(self)
        self.comment = ''
        if not self.auto_update:
            self.on_iter_begin()
            self.update(0)

    def on_iter_begin(self): pass
    def on_interrupt(self): pass
    def on_iter_end(self): pass
    def on_update(self, val, text): pass

    def __iter__(self):
        self.on_iter_begin()
        self.update(0)
        try:
            for i,o in enumerate(self._gen):
                yield o
                if self.auto_update: self.update(i+1)
        except:
            self.on_interrupt()
            raise
        self.on_iter_end()

    def update(self, val):
        if val == 0:
            self.start_t = self.last_t = time()
            self.pred_t = 0
            self.last_v,self.wait_for = 0,1
            self.update_bar(0)
        elif val >= self.last_v + self.wait_for or val == self.total:
            cur_t = time()
            avg_t = (cur_t - self.start_t) / val
            self.wait_for = max(int(self.update_every / (avg_t+1e-8)),1)
            self.pred_t = avg_t * self.total
            self.last_v,self.last_t = val,cur_t
            self.update_bar(val)
            if not self.auto_update and val >= self.total:
                self.on_iter_end()

    def update_bar(self, val):
        elapsed_t = self.last_t - self.start_t
        remaining_t = format_time(self.pred_t - elapsed_t)
        elapsed_t = format_time(elapsed_t)
        end = '' if len(self.comment) == 0 else f' {self.comment}'
        if self.total == 0:
            warn("Your generator is empty.")
            self.on_update(0, '100% [0/0]')
        else: self.on_update(val, f'{100 * val/self.total:.2f}% [{val}/{self.total} {elapsed_t}<{remaining_t}{end}]')

class MasterBar():
    def __init__(self, gen, cls, total=None): self.first_bar = cls(gen, total=total, display=False)

    def __iter__(self):
        self.on_iter_begin()
        for o in self.first_bar: yield o
        self.on_iter_end()

    def on_iter_begin(self): self.start_t = time()
    def on_iter_end(self): pass
    def add_child(self, child): pass
    def write(self, line):      pass
    def update_graph(self, graphs, x_bounds, y_bounds): pass

def html_progress_bar(value, total, label, interrupted=False):
    bar_style = 'progress-bar-interrupted' if interrupted else ''
    return f"""

      <progress value='{value}' class='{bar_style}' max='{total}', style='width:300px; height:20px; vertical-align: middle;'></progress>
      {label}

    """

def text2html_table(items):
    "Put the texts in `items` in an HTML table."
    html_code = f"""
\n"""
    html_code += f"""
\n
\n"""
    for i in items[0]: html_code += f"      {i}	\n"
    html_code += f"
\n
\n
\n"
    for line in items[1:]:
        html_code += "
\n"
        for i in line: html_code += f"      {i}	\n"
        html_code += "
\n"
    html_code += "
\n

"
    return html_code

class NBProgressBar(ProgressBar):
    def __init__(self, gen, total=None, display=True, leave=True, parent=None, auto_update=True):
        self.progress = html_progress_bar(0, len(gen) if total is None else total, "")
        super().__init__(gen, total, display, leave, parent, auto_update)

    def on_iter_begin(self):
        if self.display: self.out = display(HTML(self.progress), display_id=True)
        self.is_active=True

    def on_interrupt(self):
        self.on_update(0, 'Interrupted', interrupted=True)
        if self.parent is not None: self.parent.on_interrupt()
        self.on_iter_end()

    def on_iter_end(self):
        if not self.leave and self.display: clear_output()#self.out.update(HTML(''))
        self.is_active=False

    def on_update(self, val, text, interrupted=False):
        self.progress = html_progress_bar(val, self.total, text, interrupted)
        if self.display: self.out.update(HTML(self.progress))
        elif self.parent is not None: self.parent.show()

class NBMasterBar(MasterBar):
    names = ['train', 'valid']
    def __init__(self, gen, total=None, hide_graph=False, order=None, clean_on_interrupt=False, total_time=False):
        super().__init__(gen, NBProgressBar, total)
        self.report,self.clean_on_interrupt,self.total_time = [],clean_on_interrupt,total_time
        self.text,self.lines = "",[]
        self.html_code = '\n'.join([self.first_bar.progress, self.text])
        if order is None: order = ['pb1', 'text', 'pb2']
        self.inner_dict = {'pb1':self.first_bar.progress, 'text':self.text}
        self.hide_graph,self.order = hide_graph,order

    def on_iter_begin(self):
        super().on_iter_begin()
        self.out = display(HTML(self.html_code), display_id=True)

    def on_interrupt(self):
        if self.clean_on_interrupt: self.out.update(HTML(''))

    def on_iter_end(self):
        if hasattr(self, 'fig'):
            plt.close()
            self.out2.update(self.fig)
        total_time = format_time(time() - self.start_t)
        if self.text.endswith('
'): self.text = self.text[:-3]
        if self.total_time: self.text = f'Total time: {total_time}
' + self.text
        self.out.update(HTML(self.text))

    def add_child(self, child):
        self.child = child
        self.inner_dict['pb2'] = self.child.progress
        self.show()

    def show(self):
        self.inner_dict['pb1'], self.inner_dict['text'] = self.first_bar.progress, self.text
        if 'pb2' in self.inner_dict: self.inner_dict['pb2'] = self.child.progress
        to_show = [name for name in self.order if name in self.inner_dict.keys()]
        self.html_code = '\n'.join([self.inner_dict[n] for n in to_show])
        self.out.update(HTML(self.html_code))

    def write(self, line, table=False):
        if not table: self.text += line + "
"
        else:
            self.lines.append(line)
            self.text = text2html_table(self.lines)

    def show_imgs(self, imgs, titles=None, cols=4, imgsize=4, figsize=None):
        if self.hide_graph: return
        rows = len(imgs)//cols if len(imgs)%cols == 0 else len(imgs)//cols + 1
        plt.close()
        if figsize is None: figsize = (imgsize*cols, imgsize*rows)
        self.fig, axs = plt.subplots(rows, cols, figsize=figsize)
        if titles is None: titles = [None] * len(imgs)
        for img, ax, title in zip(imgs, axs.flatten(), titles): img.show(ax=ax, title=title)
        for ax in axs.flatten()[len(imgs):]: ax.axis('off')
        if not hasattr(self, 'out2'): self.out2 = display(self.fig, display_id=True)
        else: self.out2.update(self.fig)

    def update_graph(self, graphs, x_bounds=None, y_bounds=None):
        if self.hide_graph: return
        if not hasattr(self, 'fig'):
            self.fig, self.ax = plt.subplots(1, figsize=(6,4))
            self.out2 = display(self.ax.figure, display_id=True)
        self.ax.clear()
        if len(self.names) < len(graphs): self.names += [''] * (len(graphs) - len(self.names))
        for g,n in zip(graphs,self.names): self.ax.plot(*g, label=n)
        self.ax.legend(loc='upper right')
        if x_bounds is not None: self.ax.set_xlim(*x_bounds)
        if y_bounds is not None: self.ax.set_ylim(*y_bounds)
        self.out2.update(self.ax.figure)

class ConsoleProgressBar(ProgressBar):
    fill:str='█'

    def __init__(self, gen, total=None, display=True, leave=True, parent=None, auto_update=True, txt_len=60):
        cols,_ = shutil.get_terminal_size((100, 40))
        self.length = cols-txt_len
        self.max_len,self.prefix = 0,''
        super().__init__(gen, total, display, leave, parent, auto_update)

    def on_interrupt(self):
        self.on_iter_end()

    def on_iter_end(self):
        if not self.leave and printing():
            print(f'\r{self.prefix}' + ' ' * (self.max_len - len(f'\r{self.prefix}')), end = '\r')

    def on_update(self, val, text):
        if self.display:
            filled_len = int(self.length * val // self.total)
            bar = self.fill * filled_len + '-' * (self.length - filled_len)
            to_write = f'\r{self.prefix} |{bar}| {text}'
            if len(to_write) > self.max_len: self.max_len=len(to_write)
            if printing(): WRITER_FN(to_write, end = '\r')

class ConsoleMasterBar(MasterBar):
    def __init__(self, gen, total=None, hide_graph=False, order=None, clean_on_interrupt=False, total_time=False):
        super().__init__(gen, ConsoleProgressBar, total)
        self.total_time = total_time

    def add_child(self, child):
        self.child = child
        self.child.prefix = f'Epoch {self.first_bar.last_v+1}/{self.first_bar.total} :'
        self.child.display = True

    def on_iter_begin(self):
        super().on_iter_begin()
        if SAVE_PATH is not None and os.path.exists(SAVE_PATH) and not SAVE_APPEND:
            with open(SAVE_PATH, 'w') as f: f.write('')

    def write(self, line, table=False):
        if table:
            text = ''
            if not hasattr(self, 'names'):
                self.names = [name + ' ' * (8-len(name)) if len(name) < 8 else name for name in line]
                text = '  '.join(self.names)
            else:
                for (t,name) in zip(line,self.names): text += t + ' ' * (2 + len(name)-len(t))
            print_and_maybe_save(text)
        else: print_and_maybe_save(line)

    def on_iter_end(self):
        total_time = format_time(time() - self.start_t)
        if self.total_time: print_and_maybe_save(f'Total time: {total_time}')

    def show_imgs(*args): pass
    def update_graph(*args): pass

NO_BAR = False
WRITER_FN = print
SAVE_PATH = None
SAVE_APPEND = False

def print_and_maybe_save(line):
    WRITER_FN(line)
    if SAVE_PATH is not None:
        attr = "a" if os.path.exists(SAVE_PATH) else "w"
        with open(SAVE_PATH, attr) as f: f.write(line + '\n')

def printing():
    return False if NO_BAR else (stdout.isatty() or IN_NOTEBOOK)

if IN_NOTEBOOK: master_bar, progress_bar = NBMasterBar, NBProgressBar
else:           master_bar, progress_bar = ConsoleMasterBar, ConsoleProgressBar

def force_console_behavior():
    return ConsoleMasterBar, ConsoleProgressBar
<|endoftext|>


<commit_before># ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------

from __future__ import absolute_import, unicode_literals

import copy
import logging
import warnings

from django import forms
from django.contrib.admin.options import InlineModelAdmin
from django.contrib.admin.utils import unquote
from django.contrib.auth import get_permission_codename
from django.http import Http404

from feincms import settings, ensure_completely_loaded
from feincms.extensions import ExtensionModelAdmin
from feincms.signals import itemeditor_post_save_related

# ------------------------------------------------------------------------
FEINCMS_CONTENT_FIELDSET_NAME = 'FEINCMS_CONTENT'
FEINCMS_CONTENT_FIELDSET = (FEINCMS_CONTENT_FIELDSET_NAME, {'fields': ()})

logger = logging.getLogger(__name__)

# ------------------------------------------------------------------------
class ItemEditorForm(forms.ModelForm):
    """
    The item editor form contains hidden region and ordering fields and should
    be used for all content type inlines.
    """

    region = forms.CharField(widget=forms.HiddenInput())
    ordering = forms.IntegerField(widget=forms.HiddenInput())

# ------------------------------------------------------------------------
class FeinCMSInline(InlineModelAdmin):
    """
    Custom ``InlineModelAdmin`` subclass used for content types.
    """

    form = ItemEditorForm
    extra = 0
    fk_name = 'parent'
    template = 'admin/feincms/content_inline.html'

# ------------------------------------------------------------------------
class ItemEditor(ExtensionModelAdmin):
    """
    The ``ItemEditor`` is a drop-in replacement for ``ModelAdmin`` with the
    speciality of knowing how to work with :class:`feincms.models.Base`
    subclasses and associated content types.

    It does not have any public API except from everything inherited from'
    the standard ``ModelAdmin`` class.
    """

    def __init__(self, model, admin_site):
        ensure_completely_loaded()

        super(ItemEditor, self).__init__(model, admin_site)

    def get_inline_instances(self, request, *args, **kwargs):
        inline_instances = super(ItemEditor, self).get_inline_instances(
            request, *args, **kwargs)
        self.append_feincms_inlines(inline_instances, request)
        return inline_instances

    def append_feincms_inlines(self, inline_instances, request):
        """
        Append generated FeinCMS content inlines to native django inlines.
        """
        for inline_class in self.get_feincms_inlines(self.model, request):
            inline_instance = inline_class(self.model, self.admin_site)
            inline_instances.append(inline_instance)

    def can_add_content(self, request, content_type):
        perm = '.'.join((
            content_type._meta.app_label,
            get_permission_codename('add', content_type._meta)))
        return request.user.has_perm(perm)

    def get_feincms_inlines(self, model, request):
        """ Generate genuine django inlines for registered content types. """
        model._needs_content_types()

        inlines = []
        for content_type in model._feincms_content_types:
            if not self.can_add_content(request, content_type):
                continue

            attrs = {
                '__module__': model.__module__,
                'model': content_type,
            }

            if hasattr(content_type, 'feincms_item_editor_inline'):
                inline = content_type.feincms_item_editor_inline
                attrs['form'] = inline.form

                if hasattr(content_type, 'feincms_item_editor_form'):
                    warnings.warn(
                        'feincms_item_editor_form on %s is ignored because '
                        'feincms_item_editor_inline is set too' % content_type,
                        RuntimeWarning)

            else:
                inline = FeinCMSInline
                attrs['form'] = getattr(
                    content_type, 'feincms_item_editor_form', inline.form)

            name = '%sFeinCMSInline' % content_type.__name__
            # TODO: We generate a new class every time. Is that really wanted?
            inline_class = type(str(name), (inline,), attrs)
            inlines.append(inline_class)
        return inlines

    def get_content_type_map(self, request):
        """ Prepare mapping of content types to their prettified names. """
        content_types = []
        for content_type in self.model._feincms_content_types:
            if self.model == content_type._feincms_content_class:
                content_name = content_type._meta.verbose_name
                content_types.append(
                    (content_name, content_type.__name__.lower()))
        return content_types

    def get_extra_context(self, request):
        """ Return extra context parameters for add/change views. """

        extra_context = {
            'model': self.model,
            'available_templates': getattr(
                self.model, '_feincms_templates', ()),
            'has_parent_attribute': hasattr(self.model, 'parent'),
            'content_types': self.get_content_type_map(request),
            'FEINCMS_JQUERY_NO_CONFLICT': settings.FEINCMS_JQUERY_NO_CONFLICT,
            'FEINCMS_CONTENT_FIELDSET_NAME': FEINCMS_CONTENT_FIELDSET_NAME,
        }

        for processor in self.model.feincms_item_editor_context_processors:
            extra_context.update(processor(request))

        return extra_context

    def add_view(self, request, **kwargs):
        if not self.has_add_permission(request):
            logger.warning(
                "Denied adding %s to \"%s\" (no add permission)",
                self.model,
                request.user
            )
            raise Http404

        context = {}

        # insert dummy object as 'original' so template code can grab defaults
        # for template, etc.
        context['original'] = self.model()

        # If there are errors in the form, we need to preserve the object's
        # template as it was set when the user attempted to save it, so that
        # the same regions appear on screen.
        if request.method == 'POST' and \
                hasattr(self.model, '_feincms_templates'):
            context['original'].template_key = request.POST['template_key']

        context.update(self.get_extra_context(request))
        context.update(kwargs.get('extra_context', {}))
        kwargs['extra_context'] = context
        return super(ItemEditor, self).add_view(request, **kwargs)

    def render_change_form(self, request, context, **kwargs):
        if kwargs.get('add'):
            if request.method == 'GET' and 'adminform' in context:
                if 'template_key' in context['adminform'].form.initial:
                    context['original'].template_key = (
                        context['adminform'].form.initial['template_key'])
                # ensure that initially-selected template in form is also
                # used to render the initial regions in the item editor
        return super(
            ItemEditor, self).render_change_form(request, context, **kwargs)

    def change_view(self, request, object_id, **kwargs):
        obj = self.get_object(request, unquote(object_id))
        if not self.has_change_permission(request, obj):
            logger.warning(
                "Denied editing %s to \"%s\" (no edit permission)",
                self.model,
                request.user
            )
            raise Http404

        context = {}
        context.update(self.get_extra_context(request))
        context.update(kwargs.get('extra_context', {}))
        kwargs['extra_context'] = context
        return super(ItemEditor, self).change_view(
            request, object_id, **kwargs)

    # The next two add support for sending a "saving done" signal as soon as
    # all relevant data have been saved (especially all foreign key relations)
    # This can be used to keep functionality dependend on item content happy.
    # NOTE: These two can (and probably should) be replaced by overriding
    # `save_related` as soon as we don't depend on Django<1.4 any more.
    def response_add(self, request, obj, *args, **kwargs):
        r = super(ItemEditor, self).response_add(request, obj, *args, **kwargs)
        itemeditor_post_save_related.send(
            sender=obj.__class__, instance=obj, created=True)
        return r

    def response_change(self, request, obj, *args, **kwargs):
        r = super(ItemEditor, self).response_change(
            request, obj, *args, **kwargs)
        itemeditor_post_save_related.send(
            sender=obj.__class__, instance=obj, created=False)
        return r

    @property
    def change_form_template(self):
        return self.get_template_list()

    def get_template_list(self):
        # retained for backwards-compatibility, change_form_template wraps it
        opts = self.model._meta
        return [
            'admin/feincms/%s/%s/item_editor.html' % (
                opts.app_label, opts.object_name.lower()),
            'admin/feincms/%s/item_editor.html' % opts.app_label,
            'admin/feincms/item_editor.html',
        ]

    def get_fieldsets(self, request, obj=None):
        """
        Insert FEINCMS_CONTENT_FIELDSET it not present.
        Is it reasonable to assume this should always be included?
        """

        fieldsets = copy.deepcopy(
            super(ItemEditor, self).get_fieldsets(request, obj))

        if FEINCMS_CONTENT_FIELDSET_NAME not in dict(fieldsets).keys():
            fieldsets.append(FEINCMS_CONTENT_FIELDSET)

        return fieldsets

    # These next are only used if later we use a subclass of this class
    # which also inherits from VersionAdmin.
    revision_form_template = "admin/feincms/revision_form.html"

    recover_form_template = "admin/feincms/recover_form.html"

    def render_revision_form(self, request, obj, version, context,
                             revert=False, recover=False):
        context.update(self.get_extra_context(request))
        return super(ItemEditor, self).render_revision_form(
            request, obj, version, context, revert, recover)
<commit_msg>Avoid an import error<commit_after># ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------

from __future__ import absolute_import, unicode_literals

import copy
import logging
import warnings

from django import forms
from django.contrib.admin.options import InlineModelAdmin
try:
    from django.contrib.admin.utils import unquote
except ImportError:  # Django 1.6
    from django.contrib.admin.util import unquote
from django.contrib.auth import get_permission_codename
from django.http import Http404

from feincms import settings, ensure_completely_loaded
from feincms.extensions import ExtensionModelAdmin
from feincms.signals import itemeditor_post_save_related

# ------------------------------------------------------------------------
FEINCMS_CONTENT_FIELDSET_NAME = 'FEINCMS_CONTENT'
FEINCMS_CONTENT_FIELDSET = (FEINCMS_CONTENT_FIELDSET_NAME, {'fields': ()})

logger = logging.getLogger(__name__)

# ------------------------------------------------------------------------
class ItemEditorForm(forms.ModelForm):
    """
    The item editor form contains hidden region and ordering fields and should
    be used for all content type inlines.
    """

    region = forms.CharField(widget=forms.HiddenInput())
    ordering = forms.IntegerField(widget=forms.HiddenInput())

# ------------------------------------------------------------------------
class FeinCMSInline(InlineModelAdmin):
    """
    Custom ``InlineModelAdmin`` subclass used for content types.
    """

    form = ItemEditorForm
    extra = 0
    fk_name = 'parent'
    template = 'admin/feincms/content_inline.html'

# ------------------------------------------------------------------------
class ItemEditor(ExtensionModelAdmin):
    """
    The ``ItemEditor`` is a drop-in replacement for ``ModelAdmin`` with the
    speciality of knowing how to work with :class:`feincms.models.Base`
    subclasses and associated content types.

    It does not have any public API except from everything inherited from'
    the standard ``ModelAdmin`` class.
    """

    def __init__(self, model, admin_site):
        ensure_completely_loaded()

        super(ItemEditor, self).__init__(model, admin_site)

    def get_inline_instances(self, request, *args, **kwargs):
        inline_instances = super(ItemEditor, self).get_inline_instances(
            request, *args, **kwargs)
        self.append_feincms_inlines(inline_instances, request)
        return inline_instances

    def append_feincms_inlines(self, inline_instances, request):
        """
        Append generated FeinCMS content inlines to native django inlines.
        """
        for inline_class in self.get_feincms_inlines(self.model, request):
            inline_instance = inline_class(self.model, self.admin_site)
            inline_instances.append(inline_instance)

    def can_add_content(self, request, content_type):
        perm = '.'.join((
            content_type._meta.app_label,
            get_permission_codename('add', content_type._meta)))
        return request.user.has_perm(perm)

    def get_feincms_inlines(self, model, request):
        """ Generate genuine django inlines for registered content types. """
        model._needs_content_types()

        inlines = []
        for content_type in model._feincms_content_types:
            if not self.can_add_content(request, content_type):
                continue

            attrs = {
                '__module__': model.__module__,
                'model': content_type,
            }

            if hasattr(content_type, 'feincms_item_editor_inline'):
                inline = content_type.feincms_item_editor_inline
                attrs['form'] = inline.form

                if hasattr(content_type, 'feincms_item_editor_form'):
                    warnings.warn(
                        'feincms_item_editor_form on %s is ignored because '
                        'feincms_item_editor_inline is set too' % content_type,
                        RuntimeWarning)

            else:
                inline = FeinCMSInline
                attrs['form'] = getattr(
                    content_type, 'feincms_item_editor_form', inline.form)

            name = '%sFeinCMSInline' % content_type.__name__
            # TODO: We generate a new class every time. Is that really wanted?
            inline_class = type(str(name), (inline,), attrs)
            inlines.append(inline_class)
        return inlines

    def get_content_type_map(self, request):
        """ Prepare mapping of content types to their prettified names. """
        content_types = []
        for content_type in self.model._feincms_content_types:
            if self.model == content_type._feincms_content_class:
                content_name = content_type._meta.verbose_name
                content_types.append(
                    (content_name, content_type.__name__.lower()))
        return content_types

    def get_extra_context(self, request):
        """ Return extra context parameters for add/change views. """

        extra_context = {
            'model': self.model,
            'available_templates': getattr(
                self.model, '_feincms_templates', ()),
            'has_parent_attribute': hasattr(self.model, 'parent'),
            'content_types': self.get_content_type_map(request),
            'FEINCMS_JQUERY_NO_CONFLICT': settings.FEINCMS_JQUERY_NO_CONFLICT,
            'FEINCMS_CONTENT_FIELDSET_NAME': FEINCMS_CONTENT_FIELDSET_NAME,
        }

        for processor in self.model.feincms_item_editor_context_processors:
            extra_context.update(processor(request))

        return extra_context

    def add_view(self, request, **kwargs):
        if not self.has_add_permission(request):
            logger.warning(
                "Denied adding %s to \"%s\" (no add permission)",
                self.model,
                request.user
            )
            raise Http404

        context = {}

        # insert dummy object as 'original' so template code can grab defaults
        # for template, etc.
        context['original'] = self.model()

        # If there are errors in the form, we need to preserve the object's
        # template as it was set when the user attempted to save it, so that
        # the same regions appear on screen.
        if request.method == 'POST' and \
                hasattr(self.model, '_feincms_templates'):
            context['original'].template_key = request.POST['template_key']

        context.update(self.get_extra_context(request))
        context.update(kwargs.get('extra_context', {}))
        kwargs['extra_context'] = context
        return super(ItemEditor, self).add_view(request, **kwargs)

    def render_change_form(self, request, context, **kwargs):
        if kwargs.get('add'):
            if request.method == 'GET' and 'adminform' in context:
                if 'template_key' in context['adminform'].form.initial:
                    context['original'].template_key = (
                        context['adminform'].form.initial['template_key'])
                # ensure that initially-selected template in form is also
                # used to render the initial regions in the item editor
        return super(
            ItemEditor, self).render_change_form(request, context, **kwargs)

    def change_view(self, request, object_id, **kwargs):
        obj = self.get_object(request, unquote(object_id))
        if not self.has_change_permission(request, obj):
            logger.warning(
                "Denied editing %s to \"%s\" (no edit permission)",
                self.model,
                request.user
            )
            raise Http404

        context = {}
        context.update(self.get_extra_context(request))
        context.update(kwargs.get('extra_context', {}))
        kwargs['extra_context'] = context
        return super(ItemEditor, self).change_view(
            request, object_id, **kwargs)

    # The next two add support for sending a "saving done" signal as soon as
    # all relevant data have been saved (especially all foreign key relations)
    # This can be used to keep functionality dependend on item content happy.
    # NOTE: These two can (and probably should) be replaced by overriding
    # `save_related` as soon as we don't depend on Django<1.4 any more.
    def response_add(self, request, obj, *args, **kwargs):
        r = super(ItemEditor, self).response_add(request, obj, *args, **kwargs)
        itemeditor_post_save_related.send(
            sender=obj.__class__, instance=obj, created=True)
        return r

    def response_change(self, request, obj, *args, **kwargs):
        r = super(ItemEditor, self).response_change(
            request, obj, *args, **kwargs)
        itemeditor_post_save_related.send(
            sender=obj.__class__, instance=obj, created=False)
        return r

    @property
    def change_form_template(self):
        return self.get_template_list()

    def get_template_list(self):
        # retained for backwards-compatibility, change_form_template wraps it
        opts = self.model._meta
        return [
            'admin/feincms/%s/%s/item_editor.html' % (
                opts.app_label, opts.object_name.lower()),
            'admin/feincms/%s/item_editor.html' % opts.app_label,
            'admin/feincms/item_editor.html',
        ]

    def get_fieldsets(self, request, obj=None):
        """
        Insert FEINCMS_CONTENT_FIELDSET it not present.
        Is it reasonable to assume this should always be included?
        """

        fieldsets = copy.deepcopy(
            super(ItemEditor, self).get_fieldsets(request, obj))

        if FEINCMS_CONTENT_FIELDSET_NAME not in dict(fieldsets).keys():
            fieldsets.append(FEINCMS_CONTENT_FIELDSET)

        return fieldsets

    # These next are only used if later we use a subclass of this class
    # which also inherits from VersionAdmin.
    revision_form_template = "admin/feincms/revision_form.html"

    recover_form_template = "admin/feincms/recover_form.html"

    def render_revision_form(self, request, obj, version, context,
                             revert=False, recover=False):
        context.update(self.get_extra_context(request))
        return super(ItemEditor, self).render_revision_form(
            request, obj, version, context, revert, recover)
<|endoftext|>