[
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# IPython Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# dotenv\n.env\n\n# virtualenv\nvenv/\nENV/\n\n# Spyder project settings\n.spyderproject\n\n# Rope project settings\n.ropeproject\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2017, Lofty Labs\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# django-developer-panel\n![Django Developer Panel Interface](https://hirelofty-prod.s3.amazonaws.com/media/images/Screen_Shot_2017-05-12_at_5.20.30_PM.original.png)\n\n## Installation\n\nInstall this module with pip:\n\n    pip install django-developer-panel\n\nAdd the Developer Panel middleware to your application's `settings.py` and make sure `DEBUG` is enabled:\n\n    DEBUG = True\n\n    MIDDLEWARE = [\n        'djdev_panel.middleware.DebugMiddleware',  # <--- this guy\n        'django.middleware.security.SecurityMiddleware',\n        'django.contrib.sessions.middleware.SessionMiddleware',\n        'django.middleware.common.CommonMiddleware',\n        'django.middleware.csrf.CsrfViewMiddleware',\n        'django.contrib.auth.middleware.AuthenticationMiddleware',\n        'django.contrib.messages.middleware.MessageMiddleware',\n        'django.middleware.clickjacking.XFrameOptionsMiddleware',\n    ]\n\nYou're ready to go.  Install the [Chrome plugin](https://github.com/loftylabs/djdevpanel-devtools) and get started!\n"
  },
  {
    "path": "djdev_panel/__init__.py",
    "content": ""
  },
  {
    "path": "djdev_panel/middleware.py",
    "content": "import json\nfrom collections import OrderedDict\nfrom collections import defaultdict\n\nimport django\nimport inspect\nimport re\n\nfrom django.conf import settings\nfrom django.core import serializers\nfrom django.core.checks import run_checks\nfrom django.utils.encoding import force_text\n\nfrom django.db import connection\nfrom django.views.debug import get_safe_settings\n\nfrom django.utils.functional import Promise\nfrom django.core.serializers.json import DjangoJSONEncoder\n\ntry:\n    from django.urls import resolve\nexcept ImportError:\n    from django.core.urlresolvers import resolve\n\nfrom django.views.generic.base import ContextMixin\n\n_HTML_TYPES = ('text/html', 'application/xhtml+xml')\n\n\nclass LazyEncoder(DjangoJSONEncoder):\n    def default(self, obj):\n        if isinstance(obj, Promise):\n            return force_text(obj)\n        return super(LazyEncoder, self).default(obj)\n\n\ndef debug_payload(request, response, view_data):\n\n    current_session = {}\n\n    if 'django.contrib.sessions' in settings.INSTALLED_APPS:\n        if request.session.items():\n            for k,v in request.session.items():\n                current_session[k] = v\n\n    if request.user.is_anonymous:\n        user_data = \"[\\\"Anonymous User\\\"]\"\n    else:\n        user_data = serializers.serialize(\"json\", [request.user])\n\n    resolved_url = resolve(request.path)\n\n    view = {\n        'view_name': resolved_url._func_path,\n        'view_args': resolved_url.args,\n        'view_kwargs': resolved_url.kwargs,\n        'view_methods': VIEW_METHOD_DATA,\n        'cbv': view_data.get('cbv', False),\n        'bases': view_data.get('bases', []),\n    }\n\n    checks = {}\n    raw_checks = run_checks(include_deployment_checks=True)\n\n    for check in raw_checks:\n        checks[check.id] = check.msg\n\n    json_friendly_settings = OrderedDict()\n    s = get_safe_settings()\n    for key in sorted(s.keys()):\n        json_friendly_settings[key] = str(s[key])\n\n    payload = {\n        'version': django.VERSION,\n        'current_user': json.loads(user_data)[0],\n        'db_queries': connection.queries,\n        'session': current_session,\n        'view_data': view,\n        'url_name': resolved_url.url_name,\n        'url_namespaces': resolved_url.namespaces,\n        'checks': checks,\n        'settings': json_friendly_settings\n    }\n\n    payload_script = \"<script>var dj_chrome = {};</script>\".format(json.dumps(payload,\n                                                                              cls=LazyEncoder))\n\n    return payload_script\n\n\nVIEW_METHOD_WHITEIST = [\n    'get_context_data',\n    'get_template_names',\n    'get_queryset',\n    'get_object',\n    'get_form_class',\n    'get_form_kwargs',\n    'get_redirect_field_name',\n    'get_slug_field',\n    'get_context_object_name',\n    'get_login_url',\n    'http_method_not_allowed',\n]\n\nVIEW_METHOD_DATA = {}\nPATCHED_METHODS = defaultdict(list)\n\n\ndef record_view_data(f):\n    def wrapper(self, *args, **kwargs):\n        retval = f(self, *args, **kwargs)\n\n        VIEW_METHOD_DATA[f.__name__] = {\n            'args': repr(args),\n            'kwargs': repr(kwargs),\n            'return': repr(retval)\n        }\n\n        return retval\n    return wrapper\n\n\ndef decorate_method(klass, method):\n    attached_method = getattr(klass, method)\n    patched_method = record_view_data(attached_method)\n    setattr(klass, method, patched_method)\n    return patched_method\n\n\nclass DebugMiddleware:\n    \"\"\"\n    Should be new-style and old-style compatible.\n    \"\"\"\n\n    def __init__(self, next_layer=None):\n        \"\"\"We allow next_layer to be None because old-style middlewares\n        won't accept any argument.\n        \"\"\"\n        self.get_response = next_layer\n\n    def process_view(self, request, view_func, view_args, view_kwargs):\n        \"\"\"\n        Collect data on Class-Based Views\n        \"\"\"\n\n        # Purge data in view method cache\n        # Python 3's keys() method returns an iterator, so force evaluation before iterating.\n        view_keys = list(VIEW_METHOD_DATA.keys())\n        for key in view_keys:\n            del VIEW_METHOD_DATA[key]\n\n        self.view_data = {}\n\n        try:\n            cbv = view_func.view_class\n        except AttributeError:\n            cbv = False\n\n        if cbv:\n\n            self.view_data['cbv'] = True\n            klass = view_func.view_class\n            self.view_data['bases'] = [base.__name__ for base in inspect.getmro(klass)]\n            # Inject with drugz\n\n            for member in inspect.getmembers(view_func.view_class):\n                # Check that we are interested in capturing data for this method\n                # and ensure that a decorated method is not decorated multiple times.\n                if member[0] in VIEW_METHOD_WHITEIST and member[0] not in PATCHED_METHODS[klass]:\n                    decorate_method(klass, member[0])\n                    PATCHED_METHODS[klass].append(member[0])\n\n    def process_template_response(self, request, response):\n\n        if response.context_data is None:\n            return response\n\n        view = response.context_data.get('view', None)\n\n        if ContextMixin in self.view_data.get('bases', []):\n            self.view_data['context'] = view.get_context_data()\n\n        return response\n\n    def process_request(self, request):\n        \"\"\"Let's handle old-style request processing here, as usual.\"\"\"\n        # Do something with request\n        # Probably return None\n        # Or return an HttpResponse in some cases\n\n    def process_response(self, request, response):\n        \"\"\"Let's handle old-style response processing here, as usual.\"\"\"\n\n        # For debug only.\n        if not settings.DEBUG:\n            return response\n\n        # Check for responses where the data can't be inserted.\n        content_encoding = response.get('Content-Encoding', '')\n        content_type = response.get('Content-Type', '').split(';')[0]\n        if any((getattr(response, 'streaming', False),\n                'gzip' in content_encoding,\n                content_type not in _HTML_TYPES)):\n            return response\n\n        content = force_text(response.content, encoding=settings.DEFAULT_CHARSET)\n\n        pattern = re.escape('</body>')\n        bits = re.split(pattern, content, flags=re.IGNORECASE)\n\n        if len(bits) > 1:\n            bits[-2] += debug_payload(request, response, self.view_data)\n            response.content = \"</body>\".join(bits)\n            if response.get('Content-Length', None):\n                response['Content-Length'] = len(response.content)\n\n        return response\n\n    def __call__(self, request):\n        \"\"\"Handle new-style middleware here.\"\"\"\n        response = self.process_request(request)\n        if response is None:\n            # If process_request returned None, we must call the next middleware or\n            # the view. Note that here, we are sure that self.get_response is not\n            # None because this method is executed only in new-style middlewares.\n            response = self.get_response(request)\n        response = self.process_response(request, response)\n        return response\n"
  },
  {
    "path": "setup.py",
    "content": "from distutils.core import setup\n\nsetup(\n    name='django-developer-panel',\n    version='0.1.2',\n    description='Django bindings for the Django Developer Panel Chrome extension.',\n    author='Casey Kinsey / Lofty Labs',\n    author_email='casey@hirelofty.com',\n    url='https://github.com/loftylabs/django-developer-panel',\n    packages=['djdev_panel'],\n)\n"
  }
]