mongoengine-0.10.6/0000775000175000017500000000000012651364023013511 5ustar travistravismongoengine-0.10.6/docs/0000775000175000017500000000000012651364023014441 5ustar travistravismongoengine-0.10.6/docs/Makefile0000664000175000017500000000610012651363712016102 0ustar travistravis# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MongoEngine.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MongoEngine.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." mongoengine-0.10.6/docs/apireference.rst0000664000175000017500000000731412651363712017634 0ustar travistravis============= API Reference ============= Connecting ========== .. autofunction:: mongoengine.connect .. autofunction:: mongoengine.register_connection Documents ========= .. autoclass:: mongoengine.Document :members: .. attribute:: objects A :class:`~mongoengine.queryset.QuerySet` object that is created lazily on access. .. autoclass:: mongoengine.EmbeddedDocument :members: .. autoclass:: mongoengine.DynamicDocument :members: .. autoclass:: mongoengine.DynamicEmbeddedDocument :members: .. autoclass:: mongoengine.document.MapReduceDocument :members: .. autoclass:: mongoengine.ValidationError :members: .. autoclass:: mongoengine.FieldDoesNotExist Context Managers ================ .. autoclass:: mongoengine.context_managers.switch_db .. autoclass:: mongoengine.context_managers.switch_collection .. autoclass:: mongoengine.context_managers.no_dereference .. autoclass:: mongoengine.context_managers.query_counter Querying ======== .. automodule:: mongoengine.queryset :synopsis: Queryset level operations .. autoclass:: mongoengine.queryset.QuerySet :members: :inherited-members: .. automethod:: QuerySet.__call__ .. autoclass:: mongoengine.queryset.QuerySetNoCache :members: .. automethod:: mongoengine.queryset.QuerySetNoCache.__call__ .. autofunction:: mongoengine.queryset.queryset_manager Fields ====== .. autoclass:: mongoengine.base.fields.BaseField .. autoclass:: mongoengine.fields.StringField .. autoclass:: mongoengine.fields.URLField .. autoclass:: mongoengine.fields.EmailField .. autoclass:: mongoengine.fields.IntField .. autoclass:: mongoengine.fields.LongField .. autoclass:: mongoengine.fields.FloatField .. autoclass:: mongoengine.fields.DecimalField .. autoclass:: mongoengine.fields.BooleanField .. autoclass:: mongoengine.fields.DateTimeField .. autoclass:: mongoengine.fields.ComplexDateTimeField .. autoclass:: mongoengine.fields.EmbeddedDocumentField .. autoclass:: mongoengine.fields.GenericEmbeddedDocumentField .. autoclass:: mongoengine.fields.DynamicField .. autoclass:: mongoengine.fields.ListField .. autoclass:: mongoengine.fields.EmbeddedDocumentListField .. autoclass:: mongoengine.fields.SortedListField .. autoclass:: mongoengine.fields.DictField .. autoclass:: mongoengine.fields.MapField .. autoclass:: mongoengine.fields.ReferenceField .. autoclass:: mongoengine.fields.GenericReferenceField .. autoclass:: mongoengine.fields.CachedReferenceField .. autoclass:: mongoengine.fields.BinaryField .. autoclass:: mongoengine.fields.FileField .. autoclass:: mongoengine.fields.ImageField .. autoclass:: mongoengine.fields.SequenceField .. autoclass:: mongoengine.fields.ObjectIdField .. autoclass:: mongoengine.fields.UUIDField .. autoclass:: mongoengine.fields.GeoPointField .. autoclass:: mongoengine.fields.PointField .. autoclass:: mongoengine.fields.LineStringField .. autoclass:: mongoengine.fields.PolygonField .. autoclass:: mongoengine.fields.MultiPointField .. autoclass:: mongoengine.fields.MultiLineStringField .. autoclass:: mongoengine.fields.MultiPolygonField .. autoclass:: mongoengine.fields.GridFSError .. autoclass:: mongoengine.fields.GridFSProxy .. autoclass:: mongoengine.fields.ImageGridFsProxy .. autoclass:: mongoengine.fields.ImproperlyConfigured Embedded Document Querying ========================== .. versionadded:: 0.9 Additional queries for Embedded Documents are available when using the :class:`~mongoengine.EmbeddedDocumentListField` to store a list of embedded documents. A list of embedded documents is returned as a special list with the following methods: .. autoclass:: mongoengine.base.datastructures.EmbeddedDocumentList :members: Misc ==== .. autofunction:: mongoengine.common._import_class mongoengine-0.10.6/docs/_themes/0000775000175000017500000000000012651364023016065 5ustar travistravismongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/0000775000175000017500000000000012651364023021431 5ustar travistravismongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/search.html0000775000175000017500000000277512651363712023606 0ustar travistravis{# basic/search.html ~~~~~~~~~~~~~~~~~ Template for the search page. :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. #} {%- extends "layout.html" %} {% set title = _('Search') %} {% set script_files = script_files + ['_static/searchtools.js'] %} {% block extrahead %} {# this is used when loading the search index using $.ajax fails, such as on Chrome for documents on localhost #} {{ super() }} {% endblock %} {% block body %} {% if search_performed %}

{{ _('Search Results') }}

{% if not search_results %}

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

{% endif %} {% endif %}
{% if search_results %}
    {% for href, caption, context in search_results %}
  • {{ caption }}

    {{ context|e }}

  • {% endfor %}
{% endif %}
{% endblock %} mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/layout.html0000775000175000017500000001123012651363712023640 0ustar travistravis{# TEMPLATE VAR SETTINGS #} {%- set url_root = pathto('', 1) %} {%- if url_root == '#' %}{% set url_root = '' %}{% endif %} {%- if not embedded and docstitle %} {%- set titlesuffix = " — "|safe + docstitle|e %} {%- else %} {%- set titlesuffix = "" %} {%- endif %} {% block htmltitle %} {{ title|striptags|e }}{{ titlesuffix }} {% endblock %} {# FAVICON #} {% if favicon %} {% endif %} {# CANONICAL #} {%- if theme_canonical_url %} {%- endif %} {# CSS #} {# JS #} {% if not embedded %} {%- for scriptfile in script_files %} {%- endfor %} {% if use_opensearch %} {% endif %} {% endif %} {# RTD hosts these file themselves, so just load on non RTD builds #} {% if not READTHEDOCS %} {% endif %} {% for cssfile in css_files %} {% endfor %} {%- block linktags %} {%- if hasdoc('about') %} {%- endif %} {%- if hasdoc('genindex') %} {%- endif %} {%- if hasdoc('search') %} {%- endif %} {%- if hasdoc('copyright') %} {%- endif %} {%- if parents %} {%- endif %} {%- if next %} {%- endif %} {%- if prev %} {%- endif %} {%- endblock %} {%- block extrahead %} {% endblock %}
{# SIDE NAV, TOGGLES ON MOBILE #}
{# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} {# PAGE CONTENT #}
{% include "breadcrumbs.html" %} {% block body %}{% endblock %} {% include "footer.html" %}
{% include "versions.html" %} mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/theme.conf0000775000175000017500000000016112651363712023407 0ustar travistravis[theme] inherit = basic stylesheet = css/theme.css [options] typekit_id = hiw1hhg analytics_id = canonical_url =mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/breadcrumbs.html0000775000175000017500000000145312651363712024622 0ustar travistravis
mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/versions.html0000775000175000017500000000225212651363712024177 0ustar travistravis{% if READTHEDOCS %} {# Add rst-badge after rst-versions for small badge style. #}
Read the Docs v: {{ current_version }}
Versions
{% for slug, url in versions %}
{{ slug }}
{% endfor %}
Downloads
{% for type, url in downloads %}
{{ type }}
{% endfor %}
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.
{% endif %} mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/searchbox.html0000775000175000017500000000041712651363712024306 0ustar travistravis
mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/__init__.py0000775000175000017500000000056312651363712023555 0ustar travistravis"""Sphinx ReadTheDocs theme. From https://github.com/ryan-roemer/sphinx-bootstrap-theme. """ import os VERSION = (0, 1, 5) __version__ = ".".join(str(v) for v in VERSION) __version_full__ = __version__ def get_html_theme_path(): """Return list of HTML theme paths.""" cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) return cur_dir mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/0000775000175000017500000000000012651364023022720 5ustar travistravismongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/css/0000775000175000017500000000000012651364023023510 5ustar travistravismongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/css/theme.css0000775000175000017500000025701312651363712025343 0ustar travistravis*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.font-smooth,.icon:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-tag-input-group .wy-tag .wy-tag-remove:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-tag-input-group,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:fontawesome-webfont;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#fontawesome-webfont") format("svg")}.icon:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-tag-input-group .wy-tag .wy-tag-remove:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before{display:inline-block;font-family:fontawesome-webfont;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .icon,a .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success a .wy-input-context,a .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger a .wy-input-context,a .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning a .wy-input-context,a .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info a .wy-input-context,a .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag a .wy-tag-remove,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink{display:inline-block;text-decoration:inherit}.icon-large:before{vertical-align:-10%;font-size:1.33333em}.btn .icon,.btn .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success .btn .wy-input-context,.btn .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .btn .wy-input-context,.btn .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .btn .wy-input-context,.btn .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info .btn .wy-input-context,.btn .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag .btn .wy-tag-remove,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.nav .icon,.nav .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success .nav .wy-input-context,.nav .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .nav .wy-input-context,.nav .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .nav .wy-input-context,.nav .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info .nav .wy-input-context,.nav .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag .nav .wy-tag-remove,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink{display:inline}.btn .icon.icon-large,.btn .wy-inline-validate.wy-inline-validate-success .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-success .btn .icon-large.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-danger .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .btn .icon-large.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-warning .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .btn .icon-large.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-info .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-info .btn .icon-large.wy-input-context,.btn .wy-tag-input-group .wy-tag .icon-large.wy-tag-remove,.wy-tag-input-group .wy-tag .btn .icon-large.wy-tag-remove,.btn .rst-content .icon-large.admonition-title,.rst-content .btn .icon-large.admonition-title,.btn .rst-content h1 .icon-large.headerlink,.rst-content h1 .btn .icon-large.headerlink,.btn .rst-content h2 .icon-large.headerlink,.rst-content h2 .btn .icon-large.headerlink,.btn .rst-content h3 .icon-large.headerlink,.rst-content h3 .btn .icon-large.headerlink,.btn .rst-content h4 .icon-large.headerlink,.rst-content h4 .btn .icon-large.headerlink,.btn .rst-content h5 .icon-large.headerlink,.rst-content h5 .btn .icon-large.headerlink,.btn .rst-content h6 .icon-large.headerlink,.rst-content h6 .btn .icon-large.headerlink,.btn .rst-content dl dt .icon-large.headerlink,.rst-content dl dt .btn .icon-large.headerlink,.nav .icon.icon-large,.nav .wy-inline-validate.wy-inline-validate-success .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-success .nav .icon-large.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-danger .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .nav .icon-large.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-warning .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .nav .icon-large.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-info .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-info .nav .icon-large.wy-input-context,.nav .wy-tag-input-group .wy-tag .icon-large.wy-tag-remove,.wy-tag-input-group .wy-tag .nav .icon-large.wy-tag-remove,.nav .rst-content .icon-large.admonition-title,.rst-content .nav .icon-large.admonition-title,.nav .rst-content h1 .icon-large.headerlink,.rst-content h1 .nav .icon-large.headerlink,.nav .rst-content h2 .icon-large.headerlink,.rst-content h2 .nav .icon-large.headerlink,.nav .rst-content h3 .icon-large.headerlink,.rst-content h3 .nav .icon-large.headerlink,.nav .rst-content h4 .icon-large.headerlink,.rst-content h4 .nav .icon-large.headerlink,.nav .rst-content h5 .icon-large.headerlink,.rst-content h5 .nav .icon-large.headerlink,.nav .rst-content h6 .icon-large.headerlink,.rst-content h6 .nav .icon-large.headerlink,.nav .rst-content dl dt .icon-large.headerlink,.rst-content dl dt .nav .icon-large.headerlink{line-height:0.9em}.btn .icon.icon-spin,.btn .wy-inline-validate.wy-inline-validate-success .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-success .btn .icon-spin.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-danger .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .btn .icon-spin.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-warning .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .btn .icon-spin.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-info .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-info .btn .icon-spin.wy-input-context,.btn .wy-tag-input-group .wy-tag .icon-spin.wy-tag-remove,.wy-tag-input-group .wy-tag .btn .icon-spin.wy-tag-remove,.btn .rst-content .icon-spin.admonition-title,.rst-content .btn .icon-spin.admonition-title,.btn .rst-content h1 .icon-spin.headerlink,.rst-content h1 .btn .icon-spin.headerlink,.btn .rst-content h2 .icon-spin.headerlink,.rst-content h2 .btn .icon-spin.headerlink,.btn .rst-content h3 .icon-spin.headerlink,.rst-content h3 .btn .icon-spin.headerlink,.btn .rst-content h4 .icon-spin.headerlink,.rst-content h4 .btn .icon-spin.headerlink,.btn .rst-content h5 .icon-spin.headerlink,.rst-content h5 .btn .icon-spin.headerlink,.btn .rst-content h6 .icon-spin.headerlink,.rst-content h6 .btn .icon-spin.headerlink,.btn .rst-content dl dt .icon-spin.headerlink,.rst-content dl dt .btn .icon-spin.headerlink,.nav .icon.icon-spin,.nav .wy-inline-validate.wy-inline-validate-success .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-success .nav .icon-spin.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-danger .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .nav .icon-spin.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-warning .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .nav .icon-spin.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-info .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-info .nav .icon-spin.wy-input-context,.nav .wy-tag-input-group .wy-tag .icon-spin.wy-tag-remove,.wy-tag-input-group .wy-tag .nav .icon-spin.wy-tag-remove,.nav .rst-content .icon-spin.admonition-title,.rst-content .nav .icon-spin.admonition-title,.nav .rst-content h1 .icon-spin.headerlink,.rst-content h1 .nav .icon-spin.headerlink,.nav .rst-content h2 .icon-spin.headerlink,.rst-content h2 .nav .icon-spin.headerlink,.nav .rst-content h3 .icon-spin.headerlink,.rst-content h3 .nav .icon-spin.headerlink,.nav .rst-content h4 .icon-spin.headerlink,.rst-content h4 .nav .icon-spin.headerlink,.nav .rst-content h5 .icon-spin.headerlink,.rst-content h5 .nav .icon-spin.headerlink,.nav .rst-content h6 .icon-spin.headerlink,.rst-content h6 .nav .icon-spin.headerlink,.nav .rst-content dl dt .icon-spin.headerlink,.rst-content dl dt .nav .icon-spin.headerlink{display:inline-block}.btn.icon:before,.wy-inline-validate.wy-inline-validate-success .btn.wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .btn.wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .btn.wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .btn.wy-input-context:before,.wy-tag-input-group .wy-tag .btn.wy-tag-remove:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.icon:hover:before,.wy-inline-validate.wy-inline-validate-success .btn.wy-input-context:hover:before,.wy-inline-validate.wy-inline-validate-danger .btn.wy-input-context:hover:before,.wy-inline-validate.wy-inline-validate-warning .btn.wy-input-context:hover:before,.wy-inline-validate.wy-inline-validate-info .btn.wy-input-context:hover:before,.wy-tag-input-group .wy-tag .btn.wy-tag-remove:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before{opacity:1}.btn-mini .icon:before,.btn-mini .wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .btn-mini .wy-input-context:before,.btn-mini .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .btn-mini .wy-input-context:before,.btn-mini .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .btn-mini .wy-input-context:before,.btn-mini .wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .btn-mini .wy-input-context:before,.btn-mini .wy-tag-input-group .wy-tag .wy-tag-remove:before,.wy-tag-input-group .wy-tag .btn-mini .wy-tag-remove:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before{font-size:14px;vertical-align:-15%}li .icon,li .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success li .wy-input-context,li .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger li .wy-input-context,li .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning li .wy-input-context,li .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info li .wy-input-context,li .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag li .wy-tag-remove,li .rst-content .admonition-title,.rst-content li .admonition-title,li .rst-content h1 .headerlink,.rst-content h1 li .headerlink,li .rst-content h2 .headerlink,.rst-content h2 li .headerlink,li .rst-content h3 .headerlink,.rst-content h3 li .headerlink,li .rst-content h4 .headerlink,.rst-content h4 li .headerlink,li .rst-content h5 .headerlink,.rst-content h5 li .headerlink,li .rst-content h6 .headerlink,.rst-content h6 li .headerlink,li .rst-content dl dt .headerlink,.rst-content dl dt li .headerlink{display:inline-block}li .icon-large:before,li .icon-large:before{width:1.875em}ul.icons{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.icons li .icon,ul.icons li .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success ul.icons li .wy-input-context,ul.icons li .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger ul.icons li .wy-input-context,ul.icons li .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning ul.icons li .wy-input-context,ul.icons li .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info ul.icons li .wy-input-context,ul.icons li .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag ul.icons li .wy-tag-remove,ul.icons li .rst-content .admonition-title,.rst-content ul.icons li .admonition-title,ul.icons li .rst-content h1 .headerlink,.rst-content h1 ul.icons li .headerlink,ul.icons li .rst-content h2 .headerlink,.rst-content h2 ul.icons li .headerlink,ul.icons li .rst-content h3 .headerlink,.rst-content h3 ul.icons li .headerlink,ul.icons li .rst-content h4 .headerlink,.rst-content h4 ul.icons li .headerlink,ul.icons li .rst-content h5 .headerlink,.rst-content h5 ul.icons li .headerlink,ul.icons li .rst-content h6 .headerlink,.rst-content h6 ul.icons li .headerlink,ul.icons li .rst-content dl dt .headerlink,.rst-content dl dt ul.icons li .headerlink{width:0.8em}ul.icons li .icon-large:before,ul.icons li .icon-large:before{vertical-align:baseline}.icon-glass:before{content:"\f000"}.icon-music:before{content:"\f001"}.icon-search:before{content:"\f002"}.icon-envelope-alt:before{content:"\f003"}.icon-heart:before{content:"\f004"}.icon-star:before{content:"\f005"}.icon-star-empty:before{content:"\f006"}.icon-user:before{content:"\f007"}.icon-film:before{content:"\f008"}.icon-th-large:before{content:"\f009"}.icon-th:before{content:"\f00a"}.icon-th-list:before{content:"\f00b"}.icon-ok:before{content:"\f00c"}.icon-remove:before,.wy-tag-input-group .wy-tag .wy-tag-remove:before{content:"\f00d"}.icon-zoom-in:before{content:"\f00e"}.icon-zoom-out:before{content:"\f010"}.icon-power-off:before,.icon-off:before{content:"\f011"}.icon-signal:before{content:"\f012"}.icon-gear:before,.icon-cog:before{content:"\f013"}.icon-trash:before{content:"\f014"}.icon-home:before{content:"\f015"}.icon-file-alt:before{content:"\f016"}.icon-time:before{content:"\f017"}.icon-road:before{content:"\f018"}.icon-download-alt:before{content:"\f019"}.icon-download:before{content:"\f01a"}.icon-upload:before{content:"\f01b"}.icon-inbox:before{content:"\f01c"}.icon-play-circle:before{content:"\f01d"}.icon-rotate-right:before,.icon-repeat:before{content:"\f01e"}.icon-refresh:before{content:"\f021"}.icon-list-alt:before{content:"\f022"}.icon-lock:before{content:"\f023"}.icon-flag:before{content:"\f024"}.icon-headphones:before{content:"\f025"}.icon-volume-off:before{content:"\f026"}.icon-volume-down:before{content:"\f027"}.icon-volume-up:before{content:"\f028"}.icon-qrcode:before{content:"\f029"}.icon-barcode:before{content:"\f02a"}.icon-tag:before{content:"\f02b"}.icon-tags:before{content:"\f02c"}.icon-book:before{content:"\f02d"}.icon-bookmark:before{content:"\f02e"}.icon-print:before{content:"\f02f"}.icon-camera:before{content:"\f030"}.icon-font:before{content:"\f031"}.icon-bold:before{content:"\f032"}.icon-italic:before{content:"\f033"}.icon-text-height:before{content:"\f034"}.icon-text-width:before{content:"\f035"}.icon-align-left:before{content:"\f036"}.icon-align-center:before{content:"\f037"}.icon-align-right:before{content:"\f038"}.icon-align-justify:before{content:"\f039"}.icon-list:before{content:"\f03a"}.icon-indent-left:before{content:"\f03b"}.icon-indent-right:before{content:"\f03c"}.icon-facetime-video:before{content:"\f03d"}.icon-picture:before{content:"\f03e"}.icon-pencil:before{content:"\f040"}.icon-map-marker:before{content:"\f041"}.icon-adjust:before{content:"\f042"}.icon-tint:before{content:"\f043"}.icon-edit:before{content:"\f044"}.icon-share:before{content:"\f045"}.icon-check:before{content:"\f046"}.icon-move:before{content:"\f047"}.icon-step-backward:before{content:"\f048"}.icon-fast-backward:before{content:"\f049"}.icon-backward:before{content:"\f04a"}.icon-play:before{content:"\f04b"}.icon-pause:before{content:"\f04c"}.icon-stop:before{content:"\f04d"}.icon-forward:before{content:"\f04e"}.icon-fast-forward:before{content:"\f050"}.icon-step-forward:before{content:"\f051"}.icon-eject:before{content:"\f052"}.icon-chevron-left:before{content:"\f053"}.icon-chevron-right:before{content:"\f054"}.icon-plus-sign:before{content:"\f055"}.icon-minus-sign:before{content:"\f056"}.icon-remove-sign:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.icon-ok-sign:before{content:"\f058"}.icon-question-sign:before{content:"\f059"}.icon-info-sign:before{content:"\f05a"}.icon-screenshot:before{content:"\f05b"}.icon-remove-circle:before{content:"\f05c"}.icon-ok-circle:before{content:"\f05d"}.icon-ban-circle:before{content:"\f05e"}.icon-arrow-left:before{content:"\f060"}.icon-arrow-right:before{content:"\f061"}.icon-arrow-up:before{content:"\f062"}.icon-arrow-down:before{content:"\f063"}.icon-mail-forward:before,.icon-share-alt:before{content:"\f064"}.icon-resize-full:before{content:"\f065"}.icon-resize-small:before{content:"\f066"}.icon-plus:before{content:"\f067"}.icon-minus:before{content:"\f068"}.icon-asterisk:before{content:"\f069"}.icon-exclamation-sign:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.icon-gift:before{content:"\f06b"}.icon-leaf:before{content:"\f06c"}.icon-fire:before{content:"\f06d"}.icon-eye-open:before{content:"\f06e"}.icon-eye-close:before{content:"\f070"}.icon-warning-sign:before{content:"\f071"}.icon-plane:before{content:"\f072"}.icon-calendar:before{content:"\f073"}.icon-random:before{content:"\f074"}.icon-comment:before{content:"\f075"}.icon-magnet:before{content:"\f076"}.icon-chevron-up:before{content:"\f077"}.icon-chevron-down:before{content:"\f078"}.icon-retweet:before{content:"\f079"}.icon-shopping-cart:before{content:"\f07a"}.icon-folder-close:before{content:"\f07b"}.icon-folder-open:before{content:"\f07c"}.icon-resize-vertical:before{content:"\f07d"}.icon-resize-horizontal:before{content:"\f07e"}.icon-bar-chart:before{content:"\f080"}.icon-twitter-sign:before{content:"\f081"}.icon-facebook-sign:before{content:"\f082"}.icon-camera-retro:before{content:"\f083"}.icon-key:before{content:"\f084"}.icon-gears:before,.icon-cogs:before{content:"\f085"}.icon-comments:before{content:"\f086"}.icon-thumbs-up-alt:before{content:"\f087"}.icon-thumbs-down-alt:before{content:"\f088"}.icon-star-half:before{content:"\f089"}.icon-heart-empty:before{content:"\f08a"}.icon-signout:before{content:"\f08b"}.icon-linkedin-sign:before{content:"\f08c"}.icon-pushpin:before{content:"\f08d"}.icon-external-link:before{content:"\f08e"}.icon-signin:before{content:"\f090"}.icon-trophy:before{content:"\f091"}.icon-github-sign:before{content:"\f092"}.icon-upload-alt:before{content:"\f093"}.icon-lemon:before{content:"\f094"}.icon-phone:before{content:"\f095"}.icon-unchecked:before,.icon-check-empty:before{content:"\f096"}.icon-bookmark-empty:before{content:"\f097"}.icon-phone-sign:before{content:"\f098"}.icon-twitter:before{content:"\f099"}.icon-facebook:before{content:"\f09a"}.icon-github:before{content:"\f09b"}.icon-unlock:before{content:"\f09c"}.icon-credit-card:before{content:"\f09d"}.icon-rss:before{content:"\f09e"}.icon-hdd:before{content:"\f0a0"}.icon-bullhorn:before{content:"\f0a1"}.icon-bell:before{content:"\f0a2"}.icon-certificate:before{content:"\f0a3"}.icon-hand-right:before{content:"\f0a4"}.icon-hand-left:before{content:"\f0a5"}.icon-hand-up:before{content:"\f0a6"}.icon-hand-down:before{content:"\f0a7"}.icon-circle-arrow-left:before{content:"\f0a8"}.icon-circle-arrow-right:before{content:"\f0a9"}.icon-circle-arrow-up:before{content:"\f0aa"}.icon-circle-arrow-down:before{content:"\f0ab"}.icon-globe:before{content:"\f0ac"}.icon-wrench:before{content:"\f0ad"}.icon-tasks:before{content:"\f0ae"}.icon-filter:before{content:"\f0b0"}.icon-briefcase:before{content:"\f0b1"}.icon-fullscreen:before{content:"\f0b2"}.icon-group:before{content:"\f0c0"}.icon-link:before{content:"\f0c1"}.icon-cloud:before{content:"\f0c2"}.icon-beaker:before{content:"\f0c3"}.icon-cut:before{content:"\f0c4"}.icon-copy:before{content:"\f0c5"}.icon-paperclip:before,.icon-paper-clip:before{content:"\f0c6"}.icon-save:before{content:"\f0c7"}.icon-sign-blank:before{content:"\f0c8"}.icon-reorder:before{content:"\f0c9"}.icon-list-ul:before{content:"\f0ca"}.icon-list-ol:before{content:"\f0cb"}.icon-strikethrough:before{content:"\f0cc"}.icon-underline:before{content:"\f0cd"}.icon-table:before{content:"\f0ce"}.icon-magic:before{content:"\f0d0"}.icon-truck:before{content:"\f0d1"}.icon-pinterest:before{content:"\f0d2"}.icon-pinterest-sign:before{content:"\f0d3"}.icon-google-plus-sign:before{content:"\f0d4"}.icon-google-plus:before{content:"\f0d5"}.icon-money:before{content:"\f0d6"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-columns:before{content:"\f0db"}.icon-sort:before{content:"\f0dc"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-up:before{content:"\f0de"}.icon-envelope:before{content:"\f0e0"}.icon-linkedin:before{content:"\f0e1"}.icon-rotate-left:before,.icon-undo:before{content:"\f0e2"}.icon-legal:before{content:"\f0e3"}.icon-dashboard:before{content:"\f0e4"}.icon-comment-alt:before{content:"\f0e5"}.icon-comments-alt:before{content:"\f0e6"}.icon-bolt:before{content:"\f0e7"}.icon-sitemap:before{content:"\f0e8"}.icon-umbrella:before{content:"\f0e9"}.icon-paste:before{content:"\f0ea"}.icon-lightbulb:before{content:"\f0eb"}.icon-exchange:before{content:"\f0ec"}.icon-cloud-download:before{content:"\f0ed"}.icon-cloud-upload:before{content:"\f0ee"}.icon-user-md:before{content:"\f0f0"}.icon-stethoscope:before{content:"\f0f1"}.icon-suitcase:before{content:"\f0f2"}.icon-bell-alt:before{content:"\f0f3"}.icon-coffee:before{content:"\f0f4"}.icon-food:before{content:"\f0f5"}.icon-file-text-alt:before{content:"\f0f6"}.icon-building:before{content:"\f0f7"}.icon-hospital:before{content:"\f0f8"}.icon-ambulance:before{content:"\f0f9"}.icon-medkit:before{content:"\f0fa"}.icon-fighter-jet:before{content:"\f0fb"}.icon-beer:before{content:"\f0fc"}.icon-h-sign:before{content:"\f0fd"}.icon-plus-sign-alt:before{content:"\f0fe"}.icon-double-angle-left:before{content:"\f100"}.icon-double-angle-right:before{content:"\f101"}.icon-double-angle-up:before{content:"\f102"}.icon-double-angle-down:before{content:"\f103"}.icon-angle-left:before{content:"\f104"}.icon-angle-right:before{content:"\f105"}.icon-angle-up:before{content:"\f106"}.icon-angle-down:before{content:"\f107"}.icon-desktop:before{content:"\f108"}.icon-laptop:before{content:"\f109"}.icon-tablet:before{content:"\f10a"}.icon-mobile-phone:before{content:"\f10b"}.icon-circle-blank:before{content:"\f10c"}.icon-quote-left:before{content:"\f10d"}.icon-quote-right:before{content:"\f10e"}.icon-spinner:before{content:"\f110"}.icon-circle:before{content:"\f111"}.icon-mail-reply:before,.icon-reply:before{content:"\f112"}.icon-github-alt:before{content:"\f113"}.icon-folder-close-alt:before{content:"\f114"}.icon-folder-open-alt:before{content:"\f115"}.icon-expand-alt:before{content:"\f116"}.icon-collapse-alt:before{content:"\f117"}.icon-smile:before{content:"\f118"}.icon-frown:before{content:"\f119"}.icon-meh:before{content:"\f11a"}.icon-gamepad:before{content:"\f11b"}.icon-keyboard:before{content:"\f11c"}.icon-flag-alt:before{content:"\f11d"}.icon-flag-checkered:before{content:"\f11e"}.icon-terminal:before{content:"\f120"}.icon-code:before{content:"\f121"}.icon-reply-all:before{content:"\f122"}.icon-mail-reply-all:before{content:"\f122"}.icon-star-half-full:before,.icon-star-half-empty:before{content:"\f123"}.icon-location-arrow:before{content:"\f124"}.icon-crop:before{content:"\f125"}.icon-code-fork:before{content:"\f126"}.icon-unlink:before{content:"\f127"}.icon-question:before{content:"\f128"}.icon-info:before{content:"\f129"}.icon-exclamation:before{content:"\f12a"}.icon-superscript:before{content:"\f12b"}.icon-subscript:before{content:"\f12c"}.icon-eraser:before{content:"\f12d"}.icon-puzzle-piece:before{content:"\f12e"}.icon-microphone:before{content:"\f130"}.icon-microphone-off:before{content:"\f131"}.icon-shield:before{content:"\f132"}.icon-calendar-empty:before{content:"\f133"}.icon-fire-extinguisher:before{content:"\f134"}.icon-rocket:before{content:"\f135"}.icon-maxcdn:before{content:"\f136"}.icon-chevron-sign-left:before{content:"\f137"}.icon-chevron-sign-right:before{content:"\f138"}.icon-chevron-sign-up:before{content:"\f139"}.icon-chevron-sign-down:before{content:"\f13a"}.icon-html5:before{content:"\f13b"}.icon-css3:before{content:"\f13c"}.icon-anchor:before{content:"\f13d"}.icon-unlock-alt:before{content:"\f13e"}.icon-bullseye:before{content:"\f140"}.icon-ellipsis-horizontal:before{content:"\f141"}.icon-ellipsis-vertical:before{content:"\f142"}.icon-rss-sign:before{content:"\f143"}.icon-play-sign:before{content:"\f144"}.icon-ticket:before{content:"\f145"}.icon-minus-sign-alt:before{content:"\f146"}.icon-check-minus:before{content:"\f147"}.icon-level-up:before{content:"\f148"}.icon-level-down:before{content:"\f149"}.icon-check-sign:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f14a"}.icon-edit-sign:before{content:"\f14b"}.icon-external-link-sign:before{content:"\f14c"}.icon-share-sign:before{content:"\f14d"}.icon-compass:before{content:"\f14e"}.icon-collapse:before{content:"\f150"}.icon-collapse-top:before{content:"\f151"}.icon-expand:before{content:"\f152"}.icon-euro:before,.icon-eur:before{content:"\f153"}.icon-gbp:before{content:"\f154"}.icon-dollar:before,.icon-usd:before{content:"\f155"}.icon-rupee:before,.icon-inr:before{content:"\f156"}.icon-yen:before,.icon-jpy:before{content:"\f157"}.icon-renminbi:before,.icon-cny:before{content:"\f158"}.icon-won:before,.icon-krw:before{content:"\f159"}.icon-bitcoin:before,.icon-btc:before{content:"\f15a"}.icon-file:before{content:"\f15b"}.icon-file-text:before{content:"\f15c"}.icon-sort-by-alphabet:before{content:"\f15d"}.icon-sort-by-alphabet-alt:before{content:"\f15e"}.icon-sort-by-attributes:before{content:"\f160"}.icon-sort-by-attributes-alt:before{content:"\f161"}.icon-sort-by-order:before{content:"\f162"}.icon-sort-by-order-alt:before{content:"\f163"}.icon-thumbs-up:before{content:"\f164"}.icon-thumbs-down:before{content:"\f165"}.icon-youtube-sign:before{content:"\f166"}.icon-youtube:before{content:"\f167"}.icon-xing:before{content:"\f168"}.icon-xing-sign:before{content:"\f169"}.icon-youtube-play:before{content:"\f16a"}.icon-dropbox:before{content:"\f16b"}.icon-stackexchange:before{content:"\f16c"}.icon-instagram:before{content:"\f16d"}.icon-flickr:before{content:"\f16e"}.icon-adn:before{content:"\f170"}.icon-bitbucket:before{content:"\f171"}.icon-bitbucket-sign:before{content:"\f172"}.icon-tumblr:before{content:"\f173"}.icon-tumblr-sign:before{content:"\f174"}.icon-long-arrow-down:before{content:"\f175"}.icon-long-arrow-up:before{content:"\f176"}.icon-long-arrow-left:before{content:"\f177"}.icon-long-arrow-right:before{content:"\f178"}.icon-apple:before{content:"\f179"}.icon-windows:before{content:"\f17a"}.icon-android:before{content:"\f17b"}.icon-linux:before{content:"\f17c"}.icon-dribbble:before{content:"\f17d"}.icon-skype:before{content:"\f17e"}.icon-foursquare:before{content:"\f180"}.icon-trello:before{content:"\f181"}.icon-female:before{content:"\f182"}.icon-male:before{content:"\f183"}.icon-gittip:before{content:"\f184"}.icon-sun:before{content:"\f185"}.icon-moon:before{content:"\f186"}.icon-archive:before{content:"\f187"}.icon-bug:before{content:"\f188"}.icon-vk:before{content:"\f189"}.icon-weibo:before{content:"\f18a"}.icon-renren:before{content:"\f18b"}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso{padding:12px;line-height:24px;margin-bottom:24px}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:transparent;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso{background:#f3f6f6}.wy-alert.wy-alert-neutral strong,.rst-content .wy-alert-neutral.note strong,.rst-content .wy-alert-neutral.attention strong,.rst-content .wy-alert-neutral.caution strong,.rst-content .wy-alert-neutral.danger strong,.rst-content .wy-alert-neutral.error strong,.rst-content .wy-alert-neutral.hint strong,.rst-content .wy-alert-neutral.important strong,.rst-content .wy-alert-neutral.tip strong,.rst-content .wy-alert-neutral.warning strong,.rst-content .wy-alert-neutral.seealso strong{color:#404040}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a{color:#2980b9}.wy-tray-container{position:fixed;top:-50px;left:0;width:100%;-webkit-transition:top 0.2s ease-in;-moz-transition:top 0.2s ease-in;transition:top 0.2s ease-in}.wy-tray-container.on{top:0}.wy-tray-container li{display:none;width:100%;background:#343131;padding:12px 24px;color:#fff;margin-bottom:6px;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1),0px -1px 2px -1px rgba(255,255,255,0.5) inset}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.btn{display:inline-block;*display:inline;zoom:1;line-height:normal;white-space:nowrap;vertical-align:baseline;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:100%;padding:6px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);border-bottom:solid 3px rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:500;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear;outline-none:false}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;color:#fff;outline:0}.btn:active{border-top:solid 3px rgba(0,0,0,0.1);border-bottom:solid 1px rgba(0,0,0,0.1);box-shadow:0px 1px 2px -1px rgba(0,0,0,0.5) inset}.btn[disabled]{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#343131}.btn-invert:hover{background-color:#413d3d !important}.btn-link{background-color:transparent !important;color:#2980b9;border-color:transparent}.btn-link:hover{background-color:transparent !important;color:#409ad5;border-color:transparent}.btn-link:active{background-color:transparent !important;border-color:transparent;border-top:solid 1px transparent;border-bottom:solid 3px transparent}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown:hover .wy-dropdown-menu{display:block}.wy-dropdown .caret:after{font-family:fontawesome-webfont;content:"\f0d7";font-size:70%}.wy-dropdown-menu{position:absolute;top:100%;left:0;display:none;float:left;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button{-webkit-appearance:button;cursor:pointer;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#2980b9}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e9322d}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:0.5em 0;color:#404040 !important;display:block}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.wy-control-group{margin-bottom:24px;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 2px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 2px #e74c3c}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}.wy-tag-input-group{padding:4px 4px 0px 4px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}.wy-tag-input-group .wy-tag{display:inline-block;background-color:rgba(0,0,0,0.1);padding:0.5em 0.625em;border-radius:2px;position:relative;margin-bottom:4px}.wy-tag-input-group .wy-tag .wy-tag-remove{color:#ccc;margin-left:5px}.wy-tag-input-group .wy-tag .wy-tag-remove:hover{color:#e74c3c}.wy-tag-input-group label{margin-left:5px;display:inline-block;margin-bottom:0}.wy-tag-input-group input{border:none;font-size:100%;margin-bottom:4px;box-shadow:none}.wy-form-upload{border:solid 1px #ccc;border-bottom:solid 3px #ccc;background-color:#fff;padding:24px;display:inline-block;text-align:center;cursor:pointer;color:#404040;-webkit-transition:border-color 0.1s ease-in;-moz-transition:border-color 0.1s ease-in;transition:border-color 0.1s ease-in;*zoom:1}.wy-form-upload:before,.wy-form-upload:after{display:table;content:""}.wy-form-upload:after{clear:both}@media screen and (max-width: 480px){.wy-form-upload{width:100%}}.wy-form-upload .image-drop{display:none}.wy-form-upload .image-desktop{display:none}.wy-form-upload .image-loading{display:none}.wy-form-upload .wy-form-upload-icon{display:block;font-size:32px;color:#b3b3b3}.wy-form-upload .image-drop .wy-form-upload-icon{color:#27ae60}.wy-form-upload p{font-size:90%}.wy-form-upload .wy-form-upload-image{float:left;margin-right:24px}@media screen and (max-width: 480px){.wy-form-upload .wy-form-upload-image{width:100%;margin-bottom:24px}}.wy-form-upload img{max-width:125px;max-height:125px;opacity:0.9;-webkit-transition:opacity 0.1s ease-in;-moz-transition:opacity 0.1s ease-in;transition:opacity 0.1s ease-in}.wy-form-upload .wy-form-upload-content{float:left}@media screen and (max-width: 480px){.wy-form-upload .wy-form-upload-content{width:100%}}.wy-form-upload:hover{border-color:#b3b3b3;color:#404040}.wy-form-upload:hover .image-desktop{display:block}.wy-form-upload:hover .image-drag{display:none}.wy-form-upload:hover img{opacity:1}.wy-form-upload:active{border-top:solid 3px #ccc;border-bottom:solid 1px #ccc}.wy-form-upload.wy-form-upload-big{width:100%;text-align:center;padding:72px}.wy-form-upload.wy-form-upload-big .wy-form-upload-content{float:none}.wy-form-upload.wy-form-upload-file p{margin-bottom:0}.wy-form-upload.wy-form-upload-file .wy-form-upload-icon{display:inline-block;font-size:inherit}.wy-form-upload.wy-form-upload-drop{background-color:#ddf7e8}.wy-form-upload.wy-form-upload-drop .image-drop{display:block}.wy-form-upload.wy-form-upload-drop .image-desktop{display:none}.wy-form-upload.wy-form-upload-drop .image-drag{display:none}.wy-form-upload.wy-form-upload-loading .image-drag{display:none}.wy-form-upload.wy-form-upload-loading .image-desktop{display:none}.wy-form-upload.wy-form-upload-loading .image-loading{display:block}.wy-form-upload.wy-form-upload-loading .wy-input-prefix{display:none}.wy-form-upload.wy-form-upload-loading p{margin-bottom:0}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}.wy-form-gallery-manage{margin-left:-12px;margin-right:-12px}.wy-form-gallery-manage li{float:left;padding:12px;width:20%;cursor:pointer}@media screen and (max-width: 768px){.wy-form-gallery-manage li{width:25%}}@media screen and (max-width: 480px){.wy-form-gallery-manage li{width:50%}}.wy-form-gallery-manage li:active{cursor:move}.wy-form-gallery-manage li>a{padding:12px;background-color:#fff;border:solid 1px #e1e4e5;border-bottom:solid 3px #e1e4e5;display:inline-block;-webkit-transition:all 0.1s ease-in;-moz-transition:all 0.1s ease-in;transition:all 0.1s ease-in}.wy-form-gallery-manage li>a:active{border:solid 1px #ccc;border-top:solid 3px #ccc}.wy-form-gallery-manage img{width:100%;-webkit-transition:all 0.05s ease-in;-moz-transition:all 0.05s ease-in;transition:all 0.05s ease-in}li.wy-form-gallery-edit{position:relative;color:#fff;padding:24px;width:100%;display:block;background-color:#343131;border-radius:4px}li.wy-form-gallery-edit .arrow{position:absolute;display:block;top:-50px;left:50%;margin-left:-25px;z-index:500;height:0;width:0;border-color:transparent;border-style:solid;border-width:25px;border-bottom-color:#343131}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-controls{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:0.2em 0 0.8em}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-grid-one-col{*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;max-width:1066px;margin-top:1.618em}.wy-grid-one-col:before,.wy-grid-one-col:after{display:table;content:""}.wy-grid-one-col:after{clear:both}.wy-grid-one-col section{display:block;float:left;margin-right:2.35765%;width:100%;background:#fcfcfc;padding:1.618em;margin-right:0}.wy-grid-one-col section:last-child{margin-right:0}.wy-grid-index-card{*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;max-width:460px;margin-top:1.618em;background:#fcfcfc;padding:1.618em}.wy-grid-index-card:before,.wy-grid-index-card:after{display:table;content:""}.wy-grid-index-card:after{clear:both}.wy-grid-index-card header,.wy-grid-index-card section,.wy-grid-index-card aside{display:block;float:left;margin-right:2.35765%;width:100%}.wy-grid-index-card header:last-child,.wy-grid-index-card section:last-child,.wy-grid-index-card aside:last-child{margin-right:0}.wy-grid-index-card.twocol{max-width:768px}.wy-grid-index-card.twocol section{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-grid-index-card.twocol section:last-child{margin-right:0}.wy-grid-index-card.twocol aside{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-grid-index-card.twocol aside:last-child{margin-right:0}.wy-grid-search-filter{*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;margin-bottom:24px}.wy-grid-search-filter:before,.wy-grid-search-filter:after{display:table;content:""}.wy-grid-search-filter:after{clear:both}.wy-grid-search-filter .wy-grid-search-filter-input{display:block;float:left;margin-right:2.35765%;width:74.41059%}.wy-grid-search-filter .wy-grid-search-filter-input:last-child{margin-right:0}.wy-grid-search-filter .wy-grid-search-filter-btn{display:block;float:left;margin-right:2.35765%;width:23.23176%}.wy-grid-search-filter .wy-grid-search-filter-btn:last-child{margin-right:0}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}.link-danger{color:#e74c3c}.link-danger:hover{color:#d62c1a}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}small{font-size:80%}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:"Incosolata","Consolata","Monaco",monospace;color:#e74c3c;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.full-width{width:100%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li{list-style:decimal;margin-left:24px}.wy-type-large{font-size:120%}.wy-type-normal{font-size:100%}.wy-type-small{font-size:100%}.wy-type-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,.rst-content .literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}pre.literal-block{@extends .codeblock;}@media print{.codeblock,.rst-content .literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.tocktree-l2.current>a{background:#c9c9c9}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980b9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag .rst-versions .rst-current-version .wy-tag-remove,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink{color:#fcfcfc}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:fontawesome-webfont;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040} mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/css/badge_only.css0000775000175000017500000000566012651363712026343 0ustar travistravis.font-smooth,.icon:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:fontawesome-webfont;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#fontawesome-webfont") format("svg")}.icon:before{display:inline-block;font-family:fontawesome-webfont;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .icon{display:inline-block;text-decoration:inherit}li .icon{display:inline-block}li .icon-large:before,li .icon-large:before{width:1.875em}ul.icons{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.icons li .icon{width:0.8em}ul.icons li .icon-large:before,ul.icons li .icon-large:before{vertical-align:baseline}.icon-book:before{content:"\f02d"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/favicon.ico0000664000175000017500000001416512651363712025054 0ustar travistravis‰PNG  IHDR00’%ÄbKGDÿÿÿÿÿÿ X÷Ü pHYsHHFÉk> vpAg00ÎîŒWIDATxÚíÛw UÕµ.ðßܧpR¥*‚ Š"ÄFP ±¢(Dc[ì=Æ’X¢Æ€hPŒ±][,5 bAc{¥¨Q@Š€Ô黬ùþØë pcr!&ä¾÷Æ?ãŒuö^s®ï›cÌ1Æ\;ø¿FFœ|àÅ+Û1Ÿ¹¹ìaÂè mf95ùëÖWá9{ׯ öºŒp³1›]‹­Â‰G¤émØCR>’p ¥“Ñš0wšx. g.U¨>»ÈÍ»›x ðiB8ÒM_´!4 —,Ø{ÅõÃEÔMΟÐn1uÇ&WŽ ë¶5—áS>¾Â¨$ÞëWr>%ÏTŽo>‹0°´åF×ww»ok²Éþ„¾–u?…pØe{â=Ún0½õoW‡æzµè†û$e¿DW…Ò‰8XÌì@hJJœ —tÂ3ÊsF…ƒ²GÖwzUBhmÈüQæÅ™ —Pÿt![ý u{äv _Pß¿ðlÜŽ\—d÷¯üpý­?|x…QÁ§-Þ¯[ï¼Ûƒ™dní*†ÌŸÜ®‚0>Œé1›Ð.3¿ß>¸ß!}Çjºõ5„æ®] ÂÝaPùX&BB5áQä°'¶A©hCDQ)š+3ÝmäÂ`烽t‹‡Ïw`¬¢aÛÂæ±µísg™ÉòA¹íÂmÔÝœßýËd;nÿóÒu ówËüM¿ºf…QÉnóeKn™i류^2xç¶„ëK>ì?g›¿Ã`ÂF' ýÚ¿E謺åÙ8Ì„’}±‹uDK‰R`‹FIjåS]—þ‘j9Bm¸âÅî€Ü+…i5Só‹j:嶃º¯óÓ 79i…6ÉAK–ã/†NíóäÃ_Û·ã*.,Üš™ƒK†4;ƒprÉË]6#´Í·û/ÂüpÞ€!˜ì× ]Líñ>aføYæ~œ¤¨—MuÒœäoN Ý 6Iõ`í Üâð„yLŒ•ÐpGá ¨—ÿ–ï’{êä€ÜåÉbPêO ŸûŸ …d܈ÿ0bÄ AàÐÔ–ÄfågëMZ»QËMPÙv‹_b¸ìàÇ…ßlCØT«BßpL‹)X¦!ó6~¤›EX¤Fß®ðøh¼^*šiÑáJe~©/äBÝùfP51;ª÷Ím¹k“"Asbw…a®)zP|<ž[ß‘BËøðÒáÿ0<»ÿ<ð™e¥÷ã}‹:@æaÄΛæ¸fß81äwžˆSÂ];~)[Q‚ÍeÂãȦÁ£Nß®ôÕŸ±Ñnôƒ¯ŠÀ8@M\ afèqXÜêRØ jFæï‚š{rŸB]—ü›—Ì€Ø%ž á[ƒWÃÞE¢ã³Æá×~öð8 S ‹yÖ:ð€‘Ðg• ÕŽ+C|¶á)ÄkÍí*a¶ íÙ{CÂ5.mù[”§¡¤ýŠØÜ­ó«Ü÷»W6…¾$]ñW Ãlþj2ä/IF@¶ga?X>;7ª·È•CÝÖù$㱞 {B¦sµ 8ĦÉ)Ä£=±|Ü_øc¼aâ²3×Ej«í*ö%¦v?79lïd® 7t¶òuï1¸Ïš/Ç.ÆêŒ¼6Xyó\3©Mc{;¥9Æ%ïPÜ,Ç$AõVùÁ°¼Yv2Ôm’/@¾mê:{ÂWa8„'µ[¥ã,s((µyölTøzftíf'³o¦°ìß™y@w0=>Ýä lºnü&F1pGÂû ¾š´í¡è¬sË'O˦+<¿ÚJÿGÒHÔ5Æ¡•c!éj#h˜_8ª³¹ úO¹£¡n~~>ä÷Nƒ¯½ ™y!p¿Îà'ê@u:^ÖlP®¡á|íÕi»Ÿ'}q0a3»çþ•YÐÈ‘ƒöXal‚\ÙÕ¾ÄÒüé haƒ:îtõÞ89Ò¿M4){ÅÚ¯ðFIÒï—¥{ÆÅîÕ¼æe¨{6?–ÏÍ=ËÆg_ƒ†Ã Å¥~@<¼]\Ñax°‚Z¤lÕM¾€¼ŒL6iîkÄÊdÄmÛ“<‘T<¹pR¸mÊ+ÿJ™µB¿‹Êìu량 õ½¿B6Œ:pÌ ?Ú~C”ª([„Rÿܲhüö|µ …JÈŒï@Ò&‚êÏsgÁ’7Öƒ¥/6\Ù…_‚ãÒ½âðW'…Ã@›4­]øÌJW–alSØ‘x^ó8̯+t-­¥é¬ðƒ^Wã%?:;(ÛfѹåVHRNÖl\IúÆU[´Ã[ö­Š{NþòdÔÍÎ7…e‹³P]]ÌÛ.)ô€LÿbÖj\ ^6 loXäù”è÷Ó4Ò4½L!ŽóÜÜG‰ǃ>ú9q¢]¿ìLû›7:nù ftÿ¬Ô»ß#ÍZgN[Ùž|HCIx‚KÊjZ-%žnß}aSíN¸Ú”õÏÇ1¢qhœKa .+æéš¨MS^*Æúøzq“­=;¿ –²÷ÃòÝs¯Bþždd~¾€·9ì©”Ð/R`¯Lõ–éȤúkәש—bœqÓ²Ä>±Ï„ã1*þå›ÓYºé¢¥]KÛúà{ AMêŠ4êNCÊ–¯%e;‡®;DxÔí}î#\àÂÎ#±­åç£ÅnªùtÅ7¤ö¶ö„ð–{ CòwƱPÝ2·+,›œÍAÍsÛC¡Cº©nïp·r°ÇŠ‚¬_:‘)Àï¥öœT_–>|cFáâïã¨oãÎxÏ3HÊ“m'v ??ÙiÉoÈwÈÏ€û?þ¤«ÚïÁFŽÜ§èb/’Íš_g<™yõŸnp1ö²Åžuhc`ÏmÐ4 -¯D¢­¾mü#i Mé ·I1Ú‡WR€¶ ;Cajœ µ·æ÷ƒe²÷Bõ´\sÈí_¸2óö0<<(½oã8õº§5&–—ƒ`=Pp© O@Ò7þê®ÉoUuÙn°üWÙaP0É_@k/‚Ê”øªÁ¬ìRP^zŸbèù/Ÿd&öŒ7Ïy”¸yìþjW’« ³^¹~]Ý›plx ~ÿü¤·¾Û×ׂ€£nÜ}{ÇÍ·º Æ}ýFi³~Äã]Ý~J˜&nÜ uªJoCÔúïÞ¸±rmè|=!ÜTÜcÇØ²K _@Õ½ÙP]—ÛrŸ%Afš!t ?“Óöò2;¤p7>õÛ©®NuÉ*ói¤g¡àsÜ¡ÔfăÔÍÙ•øªG&M¼=^øæ…´í±Ë%sŽ!ûÊ¢`ú~÷¶‡´jøþXïÍÏ]ÙóÎ[ß”·a½ú²ÃÚô#t §mx >pBÛOÅÐF±@9ãïܸÑÅ+Òþû'A˜[ =Ùw’àú–Ü©PUŸíÙ_'Ç@¦U¨ƒð¬bA8,…0T'5>BªÏLõÒ¿I@Eªg‹ÉFÄîjëN ¾§ÓûíHØîÁžηǔa,Ùoæñ®%y¯¡4{¹cšÝÍû;(¯%÷ÆÏV¶+_Mîn²™sJ6n=7»¥ùú„Ÿ„“Â;Ø]±'”[áú«JãŠß¬X±†‰ŽƒpP1 , /Bõ \±-|pöpÈuMN…X…̘0 L¯K+TêSýÌj@/Lõß!½OkÕõO`n¼ûµ[‰zä¹Þ$OúzÊLÞlýeÓg³yÅþÿgÅs…ûN÷?@wmØ2î°²ÙÆ›®ŽCÖ¿pcèWqq8Ȧ-†ÂwÔ¸ë§ì«„ á*(ŒŠoA]ŸBK¨¾.wÔVåK ¾„°—^àд÷T–JYKV#à¡T·JuT/JŸŸê¢'ÎT“{Ó|ñÕP tæø?ú'—¿Ò’Ò¡áØ%·Óï7¡ƒ#=úõ××͵¨„ÃÎáî•ufBØ¢òDÂWoÛÔV¬çÛôqõõ•¬¦NóùkÓ•9ÀN=±p+,íÔðÔ~…8ÜN†ðq—Þé7 o³Ô^Ýã=bÛTß”êA)ðÅ™¦þïõþg£ˆËâ/ž¼œx¬™#Ž7ù bœP< ‹­µ¬M01츲ß5ª¢;áá‘Ö§¢Fÿ& )+SИkdSO¨)æCŽ“,€\ßÂéPóR¾7ÔLÈ_¹=’b—rïâ Ǧê°ô)f§Í±b/reŠ£ù~©>¾¨Bq¯h‹o_ÌUÛp,ñŽøîÌ8ÛK/¼LÜÔiÞ8?¾ðå`ÂÏJvÎ}ª¯¦Œžõú‚+ñâ0y• §+)“x[ÓìxÂáì²Û±Ç*ŸkÌ뛥=—žiÌ¿ªLÒ$¶†Ú˜5ge?†ìe…Ý!îÛCftèú§DÎñX:ŸRÝH|£7p@ üR¢jLijâ8¼fÖ¼&èä¦ñûºû©JüÀFoÎ",ä^Å~+<¬(£ý{ P/ZÅ®sbfjâõ¥í³AæVÇûÅJŸKW¼‹ÝÆ0×¹üÑÝ3iËŸÎE¨ý¨P<ºüU1k »¤íå ý,Nï›Më…°"Ø5n²‰dmªïåÅCsyõqñ*¿üüb|oœpñ7Æ?¶=1Ƨ>ø˜pQØ%7e%¼g¾>íÿ>(M¤QÊ⇣$Œ ÷‚#쇰Ú³¨¸†]‹Gxat8r/~5Oæï„ºÃò[@îÀÂT7…baur(0`µ¦YX-}l”Lz½dÅÛï€yž¨¿/Å ³vD•’%6¸òÏ7b˜¥t%ü ¼³¬é÷ øŸäÚHëUtZÇ+‘Ѫð&J”DZ¾=RlÌ*º¤ùw‡”öËþƺBuŸÜ2È^Q,¨\_ÜTÃqiv²W hc.õ,srò¨Qã,Rœßœ=“˜™1=ˆÝãì÷AÛ¼ÔXglÕñØxE¶ô/”µ! ©+VÑy×5¼ˆõÃK/F‹Ð½a"ªÓNWb¢øör ŸCvQa_¨Ý·øê^í'ùM!Y‹ž²CšÆöNCIÙ?è!eV4É*Áû>VJ¼*þPµ¢‡V¼1ÔeåTbŽÙ¹ŸŸŽO,; ç:âKXÛ>u9:&_>w!†?Ž„èÓ†ùܾtâµÿzÀW—µð€øFÝ,÷4 $ÎI‡lläV‚ÿ[]žzÈb‹sÏoˆ7Ì›‡3 ?˜¸còÙo7&öíÙz|Ê0äâSÙ)˜Ÿü÷ÿOààb…Ú¨ÃnÉ‚úé„qãʼnãiuëãõ´)6¹˜fæ3qo¨?1?ê:åw‚ü­ÉRϨ‚pnókMIn<ë 8Ü­]2™xYÿuñŒøóW§`;¿èbm¼òáÁÄs’y¯Ì¢0;wÞôã(tLN«]@¡o²W<ƒÛ_˜´ýº#`-BP¸,ùh{ó†> 8­2·ô\âü0©ªœ0I÷ü¯°‘=K6£¡waRhMMçbO'{zrÄŠ¸d…â™ëÉÅ×½-LÍ*‡£ÒùrLÑ»vâõž^ؽ\ñÖõÄ]âŒç£Ðß'/Ÿ@U‡Î:Œæ? ÙüK´ýÈq–2»­;À¿jn8ge{ó÷7Ù ÿ.³Æ~ýÁâa”µÉœ>ûvì:/ø†ä×ÉÔö‹¨»¾ðTikjÏÍ÷€äµX|Ý{¤}À´´"næ9/=,Þê®d2 ÚNŸŒ“5÷ZüY××®#N‰û|t¶Œ[LŸEf‚äiÝ߀ÜSŒ7©cú~CQ^Y× ¯,ÿô›q¥§Ü²ão)Š’õž5ÄyzÐ)itrĽ’+rµIYŸsŒY<¶¾²b$K¿n¨‚$7­Š-„0FIò+t2½ê:â‹qô7àr?õ1fXðÞ5(S'FT&óßÜd{ίŲÒóŠG4¨âöÛߺ{]Ãû?ÀAûßÎÿÓò7ñQ<9nZèUòÅ&ÎZ¸üá’ß–ô—/÷|¦Ç6*œ×pna¬‘îIΊÇ -q £“+%4Þ"n¨ziq{¿å“~ç¨7æ!‰¼z9Z«ýt 2a¿onDIØ®á T—Ž+¾D^Xq´R”ÿüÓ0h×ýÝ´)˜çV>P:9)‹—tô7¬7³bèÐvó2Í6üÃ鋆—Ý_únÅÏ—õÍŒ+ÉÏo m4Ÿq£/Ì÷Ô_·rÛç³ =-ö(Œü¼?*“-¾Ê£]’]~"–gæúv·ß>éãu ãÚË{Àq÷õ½´Ã­ßÚå7Í­©zŒÂc¥³”6ù$™/ìr|ÓQåw6ôظi³öë½ÔУäβ…­~¸Þ!3îª9»uéz׿u\2‡¿×EF‹¸óû7“쟟ñéÙÄIæ›*Â_´lñ0qüþë·_×p}ÿ²æ!è=ïY©k˜Þt^¬+y?ŒÏoRrgû‹“ÃÆaìÖŸ7y¹0:9n‹W:î±ü‰Ú_Ä*¶Ì5Éÿú¥?Õ/­Ë|ô_¿y|½7™;sÇ:³ÊvZÒž|×d—ú§HvÌßRèNæÚ’çÖ5<ÿzYcb¿¸uæå•.´Ì/Î4ípP ¢]úLò\Ù´K¸>¾»4[Ö2ÿTR=}Lû½—¿Uÿø‡gìÿÞ’±³‡¼×·ü´†Ÿæ¯ûaÿ(}/žp{ׯ]ØØ6Þt]Ãòï“5÷€{½få#É©þjëó,5Pó>Ým¶3µ]u¼Ô³aÚ²ÞÙ%WgÎÿ$ëèxƒÙ3NoqpÂ|¿ÚÏ—ÜØü†Š?0·gs•=‹·Z¸Æ“ùß/k\ Ç“âÔ•ô`Ùø¨^ñRbâXs4o”lkõÒdqOÛcúÛ'&cüÊ3³¾Z0·éå¿!wT¸),aܸ ¦O_×0¬;Ys¸G(þj°(ñJÍ[40Ü-ý°y|5¾o›n™êÉxy¾G|;VÆçÞ}S—dN¬œW…kÂI+e_ë öŸ$kN@%aý•ìWýY¿Ù/©±GÿÌ€Ø<^«¿æqW8³âĸ °¬ðÚ'ï&Ës}ò½ªv Gd®Ì¬òC÷x`]ðî䟮Žž´ã=íwRl gÂTëÙH§Ö×¹#ÆX^’-Üзá§U ó£kf×ôÎÍ ora“ýbã+RÞ`ꓵo¬kþ¿ü?+ÿ˜ßx­™IEND®B`‚mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/js/0000775000175000017500000000000012651364023023334 5ustar travistravismongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/js/theme.js0000775000175000017500000000130512651363712025002 0ustar travistravis$( document ).ready(function() { // Shift nav in mobile when clicking the menu. $("[data-toggle='wy-nav-top']").click(function() { $("[data-toggle='wy-nav-shift']").toggleClass("shift"); $("[data-toggle='rst-versions']").toggleClass("shift"); }); // Close menu when you click a link. $(".wy-menu-vertical .current ul li a").click(function() { $("[data-toggle='wy-nav-shift']").removeClass("shift"); $("[data-toggle='rst-versions']").toggleClass("shift"); }); $("[data-toggle='rst-current-version']").click(function() { $("[data-toggle='rst-versions']").toggleClass("shift-up"); }); $("table.docutils:not(.field-list").wrap("
"); }); mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/font/0000775000175000017500000000000012651364023023666 5ustar travistravismongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.eot0000775000175000017500000011103512651363712030462 0ustar travistravis’7‘LPB5fÐFontAwesomeRegular$Version 3.2.0 2013&FontAwesome RegularBSGPÔ‘/‘3{êÍéŒÏÒY£D MåFx™ ¤>°›ÞÞÆ)[1ɵHÑí¦‰-A)FàÕÙœ1ÎÍ.Ó/ dºU'ë&a /‚ž³s%á%„<ÝÔ® ¾€­ÜO%pïVù "˜Ûu¦Kupp^c˜RB`\÷}TÛŒW œÅÖÁË ‹ ýÑ£ÇzÒ®5*ÿݱLzSq>Ð×?­¸Tô†35(¥œ}þ­¿ŽâœáÙ†aKPÔ'¢ÔšìŽÞ2Ë45Û$[GVzi„æ<—QAî2#+¨£%a^êV Pü Ü=öG ð,·¿‹âö'ܧ×Áì *ܰ¢¢Èñ”S„kÄû6>Œ\ö¿²‡ì:ðù^ CÀ»=P O*¦õ-<€‚ @a(0‘•LšĻ ›"žgmL%ŸÆªiC…̼¥ø´‰a’A^É舱þú– h–Q% ./Œ"}„ÐþÒ‚è»JŸÀDÍpb('0E1¯ÝÕç¢ hQšôúQÐJŸî Úà ªDp7Hk‚„3L4K«4dÎÈ S²*¢?á€êe [!•ì=8Ðg¤ÞF"bTÓÅá*¹?¨G.W“À0âQçÁ'ð^xn©?²[Ÿ ä(ý›"´“wðZðhMz"°zõ´hŽ,jç°Î÷D° ö‰„&%X Æ5²š«ôg„"‚`hñÔšl Ã78ªK'ļ³çñmƼÓF“±ˆïŲ€œÕÿÄù{b|ÔÛœ³™¥-4H¡fðP[»ËÑ1<æͥ+ð-w˜ÄE ¢:hÇKŒuOTRT;Õ)/‚- â9˜L2 ÐÂ%3LU®2ñ†Õ8ßÅY€% ­)Gˆ–ªÕ¤5 uÍêúÀÅÇØËÇË:AbÀ65W{‘”D“!dåNóÇ£zúKÇõE°Å<½U³EçqUžä*5Z-Š„}Sq¦*ºÐ(Yéõ!`‘;0.ªAGCF|Á¹yö)5.£ã@²C€Ù4XGZ`-ò¤Ë.a6ŠÃ ˆÒ¸žšâ•.¸m|>d‹;-xP«²KL›H «*KG—Ä%EF9B͆HÒi•0zÖuŠ1Tpß`FöU Ö¡›cc+UŸ²¸³^µÀúóÃÆ4x®¶(øÚF4xˆØìXݽŒð‰·‡z(R6LïV£?ˆS÷G‚Ùcà"W/B]ŽŽO€5Á!Ï.ÁО¯u»_wÎ¥±Q€C\UĶ`ÞÙv=¶£Mð­Hê¯Sײ7]\bB£Øö-—Aãj]¢zxW Wp_5MQºTw"‰A‡äqþ[MÞŒœ<§ùcô¨´v¯C›ýG¬&aYG°ŠÂÑ%ód2Ÿ‚$¶ttB&ÑDÁÁë{‹0RPu›ŒÝ¹¹0¤?,ÔÜØjý³:ÞQ íQ˜"•§0·æ 87Fà‰©„ýŒ¿ŠñAœ¯Ñãº]ž¤˜Hƒ‡P¨î<Ó!ÑoËn¸ø'|§í3rÞKÊå>]Ç.R¡Ó¡šn¾ (ãùZw)¥å¤¶¼ï™up[2•b›gã¾8Ý@W)Ê¿kì’«Yœ8åŒL›<'s†i½³BB Dþ¿ÜÅå(OC0PO©tÞt‚¢Æå6¨/6oÃâM˜ˆ²ÏEÓhÎ.Ì ‘ùä7M­I$t£ù¨–èÒôüÆÐU¿f¢! œ96H"rà‰6ÇA8ʉEÎ"Q)¿f¡RJJhì$Ǧö9[VfÆ%c£xPƒqDPœÍ$艨´,¸µvœŽUäIûm5X¨¤¡L‡¸äšj$q51§hhª«'ÞÛ þõ .L‰©®ï Žœ Lš¹¤àЀÓ®Ýbшط%UFÑUtä¥\¦ÅŸB] Sƾbõ[­›‚3Ÿf€uÀ¦{ÃØéãS6õ>÷ð³%nEqŽ˜`{1Õy*„é¨VJT(B+ñ†i¿0(/å$¥¼šÙˆ;¤¶*aR)öúIà…œ:Raõ3ÙŽìÝö{¸àosC¨ÕÐhááßàÒ"KÍ•£ë©AG‘Ôä<ø^a2¡™PK¡’—¤¹Hy˜ë3ƒNêyŽG°F„¨FÝ) »L–­o‹›7[:£’à T*ÂB¡I‹aÿ Ûñà ·x‹«eÖU,¦ ëÒµÁ›–ôÿSG7¥Ã•Îqo99ÊH+ß,W½üeÓNe{ˆ¹›/#/( ,w{Ù P»zAt‹3ìÄhfš¥'VRÔæõ# ØÓ»ýr뿾²Fîøf™ÚO[r¦±IÉë'"á÷Á-:?ŸË$€d@‰ª£±š©¤ƒõ¾4t_‹I& ŒAµB…Ä“²›$ðqb{FÉ Î$¹ƒÃ ™äÜ☊"¡¿Ï‚)ÌÙúfÌ68AÛ å5Lt|›1ÐTqˆ„ÒÓx|“ “dZI¶¸ÑZ“›ËË÷O‰ê(}ÕÍ™DýLÇȹbä¢A2Fdÿ Ù=ã´Gb4eD êóC"û#'ÁcÇàˆKj}}” 1x&”µ$±©*<›“20ÎÐb_e@Îê•‚ j úo¶œ#•¤)œ…Ï´Yi2uõã¾vù°{@$ˆ÷A9zUë:©Î-k"HŽk‚GiÑAš[=¯"D\d‰‚j´Ï}rž!†× ÔUß›~e…ƒµ´ƒ¦ó:‚ +G›Qlâ'Z‹gÇg‚W%Gn¤Á%úŒiß,ˆ•’‚µˆZ<ñe™åy4‡á °1è|—ÁªŽ`®*Ó%—v%ôƒã¡ÃL+¶t¤³ñ|9­'ÙZ9Ô¹­+ûŸ„¢uÑÂw¨«â¡Ð솹TVÁ>ô.ŒTøíË0U+S>T³JƒÀ|ûQTi¹èLØeD-A)¬p˜%ùF?õELb» ›Ÿ¦˜'ÍŽH¯äÝHMâëÙ¾5E%2£žÓiUOhx#ÃÀr.öñ‰rƒl ¿)5™•¡-‰C.(,oç8œ'Ù­q&¶ |€ãÒ7×Qÿ¸æ oM+£‰}ðQ„.ý†ÐëN—Wއ¬ñ33:½ ;=–Þw€ÎD÷_ ÚjœÀÇezO!fF’Ó²ßNðúqQý¡âÄ=8ö"ÊY—ßsà1]Ü#Ž;’®Á+"ÛHÞZÍ„öy†„äFD˜€¹s}&Í^BâŠ.4§Î9ÊÊ`±äÄ£ÍÛþ'é‡þ“º·ZÞb»0­ÕJw0ŠvT…RW&xÛ²ÅMÝAಈ•€Ò7¬ít=CFé$mÁË¥fj¿vB¦Š¢òñ5lÏu Œ®I£´70<%õh^Þ¡~Ü"_S©Þ&!‹úý¸ê"ÔÎ;1pjÑd¯,*`›¢Ò «€˜¢z¯_!oh‘ÿ»áÐëH(rz¡}v:c%³ÿ—€]x*•†bb™ƒÈï·ûXá|Ð9(®-¿(ž"ÍŠ»—Lyöϳ{"1P»Ë’Z&ÔBâ¦Â€ßxPS ŽÕ¤YuJ¥ÀÈ ¨eœ98GULœcKP‘4IÈÆQƒï†|ŸÌà›ƒj·õé%”H<"`›2ZQuU›1ˆT‹Wv ³Q,ýp[÷ ¾õ@–ŽÂ âÁ fÖ¤Û®£E×Ò³zfDµ2ªÅ%Æ+T‡ácý)ÉìòFÂ9v"bœ†cõ>§F*þðæÞgqR+Õæ€9^NPþ¶ÁbßÞA÷¬nÍhŒˆ¢áöèùZT™÷Ì¢)L!X2ŸNU¶ÈŠ ² Ç=¥J¶¼’+ìPa—ʬá3·ZÍ ©›|Fm-õÇ+}¾’¶ÂxP öA 0ùœû,JÓª”mPNb¤ ;XáSæV]eS”âëΫÓ2)l`3ï~½ ®]÷š#/»Ò•ÝZñ{=dœÍCRÜ‚Z&fƒðã“l>e¶‚¦Žæ«3a Â~‘`r‡ òIÇ´€»PšNCû`šä\ïÑÖÌQšÉXAH$íŒÎøðŠ-;Ê€¥¯Dmé¦ ð-é›~2{ñe.Œ^{sØLË—ñÝzÝ„[¿Ÿ£r$ÁÈ[Ð3%å„•Ì*fŒZÁg§t,Hê9NŒ„M‰ÇO iR?GÞ QÆÑü´tÃïš:–殾öÜ@Ÿ­©•03¦øå®P¤PõXßC‚t“±\ROþVêãÐ-5pg™2{QÊ»o'Úa_Ð äf?‚Mëïwp¡œ<ø˜w7Ô¬-XTaø‡÷RY=‘au»*fïq(‚qê(T-]ÎѺг ÜÉžoÌ#ë<ÎrÁÀÆ0º# |z³÷Pù?/WÞ¦žVdú7¼Ñ–¿v„´ãv㤴OXk›³ Šøù´‚Æñ2ú:ßŒàš¸0N:N8 šÚ·½Ú=/ž2-b‹pƒCâÐÛþËmï³^…N"s›k §¯+Ö .žº¬ƒ5€ÅäÕ:ŒFÅ~Z²ïò‰‰ÍÅ(›` + %ªÖöP_\d5ò®¸q‰K‡<Ñdjáé£ÉƬ%LÖUdF’dÌ?Õ9nm«þÊöÑ,Í(^ôCÕ-½G6ß³´†€-¦ ³¶éOO¿;‡;¶Ú=”dœåisÏ% ìƒ?°2ÏO\Ø’¨”:6åê­mê´æxnŒ8D$bƒä©pD!uŠÑ‡ÙvIðìÀz™áj&›þÝZµ³á°@K¯Ñ£&‚n ƒdÓ;è+™7²¤øi`Ql±šá£NA†‚!|ô RŸŸ†Ý+AGƒý’ˆO’[ä þc×@-¬RšêÎ’D– ó¿«êBÕˆ*H ÿv¿?ëÚväNì‘À"„„’6N&Žø4G é9PHž@ ¨H69µà0°ä«-]«²7Q˜Fp±ñlïBÇ ÌHÑ q^j)º¬$oŒÏËÑ5%H^ÂY…P™èSð6‰"EåÒ×™ûÂnûÓœ“=-±Àô³Ðàõÿåv Eö²Ž+Ç«0W¢o¬¢eè5+4©Qyß àÅàÆž“òÖœM)§w¦TÒVèM÷XR¸P†DwŸÏBÙRªê{¯P‘|ÞæÙÖˆß|»DV'Ä¢Öf¢ß…õ œ³¸ÚÀuIñm¼tÈ‹|ÝVkŘ'm/ü#·–€€ƒ\!aÖ{s@Žý5‰Dˆþ€C «ëkv¥y ÃnLí¤Õ÷!¼;ÐÚÀÎÌèÓŽ· eD=Ý¢<+Aˆ Àõü+?9à|#ù!Ú‚œ77ˆi&P,T*Ó„Ïÿ9 fŒ¤ç_ ‚"gª–‘Îc5‡yˆdGý‰{¶ÅZûˆx,«m?#Íd¬>x dØ·Ñ ÎœýsÛ¦UçÑã+6^v¦Ìþ›Œ´®IV>̲w”ÞGúÐ>Ê4€öKãH\ì ñr{­Â¡]SÓû úÑZ‚¢£hÃq^¢ƒ <®ÖÊãE#"I/C´ï𮬖gvúÔúËIy Ð=FÀrÔåÞŽ’–^©ÏL–¦`·¶W¤4òLÿd⌠£a/¾‰öÝ;è'f,9̤]„N?.^d|ŒK‚6¯ÑÆz§8>‰gƒ›Ó\䯽Á÷ÞŠ@¤#sOÊböTÒ´úÕ• «›rB¡‰@ %ÇXB›ÜG{ÑÜÅh.c*µIŽ¿âbÓT„í‚/ÛQR­‹JÿÉ-=Ð _Š*q}Å”8Ô/³"’¢]íÅ£eFõñ8b¢9Éé¶¾êª%+d)Rá÷ï×ô»Ñ@‡”»:Mœ~!hF¹™B ‘¤¨`’ ­ÅŠj<^•T‚^¾ßÀcl¢w+µX;‚áršÏRŸ*–¦Z¿°ÑÆþ¢î½jËTÕ5L;(˜²Ù[á')¤¼ñ–¦”3¨Ka+„°@.–^°?Sm#íå=c|ˆaÈÛ5ž¸kR‡CA¿ê%¦œåïh¡–Üfyî­Y}ñ ÷´ƒƒ‰h»ç\…ËfŒa­íÐàs™ï…×(N~Æëëº6,ir‰¦‡èÓÂØ«_íˆÔz?`(Íùm %íɉ|Rò"Åc±Ð@ýé} py’¸ž†«À`SAv'PÞûG ìúÇë’aª&,o’^<gj B£¹@üALø»³Ú£‹Žu=lîèör»ñ½…ð×ÂjÐyÒ#R_ [¤*”h€$ó vÁH79öÊ k2U«+4°W`ªDÉ—¬PF¹æ¨eEt+ÅïMüŠdBp¾–ʼáÝÈ+ D(†ù@Û—­F¡ƒ¦Z1B¨iˆûKϵ¥ª8.¡‡Ðrx²ÂB0‡¥Ê¨¨^nx—Ü\'Ù˜>VŠDϼ܆mP·ÂÓ€(x9ÙŒ¶Úæe{Ü ®L[ÂîE[nÿ_F×c— ƒXQ68j-—wkvsl¢[±yìl°¬.„êói N²4!h‰Wy÷\F÷Mb6²œ„ÕOl(X=mW×C±v8ž­£½)Ü\#3ÖÞËÈLÑ,TGĈ{GŸÓ…B2îœFSsLR2Q|£Ò="*R~õò䯮ŒSâ˜Ào”ýPe79E8ËÙõZ„ñ÷²µ ÕD¹`':ôïH©ãN¡R}š§/°èʱô(¨¸«Ð%Fhr™bR½Œ£½˜ÉAê[6 ÓËæµºp°Ìp×B"ДêDlޱæSâÀjAñ:©ECUl.Tð›ŒzÙbA2Ñ€KMH̾ðÉTË {5h¨[2¬`¢ñ}­†œUx ùŠ'¿™£/~ðÕEÀŒM‡{^,ƒyJÊìúêX© u –i4ïdÚó;}h,ƒ’ÆòùÛ\m$v£Êû½ /y—“´ÿ¾Q èí'X%¤¾‘,© ÆÙØÍäÀŸÔZb3ª””É;â@S}Ò RIÍ6ÁN£guc瀹#¡üU @Þi&²¨2’P@‚þ8‰Ö¾u'b/Ч"NÊËYÜËàêíÔPOÜ6k³6iC¯N¿!†HÇ"ÿAÍ`Ëä‡>@q˜N¡ô# “£¢ä@hâŠ((eÏ÷J7„>†i¨_<ÂI¸³Ayñð|ÉŸ­öÂÙ ûsÚ{ ÑßÎdÈ…ú »©´CÝ“Øpæ "´\‘9B–±ºòL@MíŸÚúQhjŽœœºxŽÉþaÙ½bÔh¯ZÒ 09}ûÙq’n«<× ‹±ö+÷¼¯5ÒÓK&h˲Tû–*zý:Ÿ%’[44b›`Ȭ×ï&º•Ã&½TSvžÆšè`7²£þèPrWüyþ4\èéEÌEÌ•¢ªwu#DZã0Cp\ìÁðŒÎF¥ìšccϪvÃÌš/¬LkY ÒœJv~ê‰ËlP‡ŸFbV[Lu>Â!aî¾µfă—€îäš:¹Ù«À¼¢ÍÝ,"Âp‘Ýñ ü¤e©:Ûù‚l·šóœ¤ÉÅ @±£Þ¢âÊqkS€N¡¦S…  b{s<lÌÒ?ÓÌt ÕçW³×çLÀX'Üvµ<¼Èð¦–,¤‰¨FY¼/eÙç—Mz²0†™å+ÔšþÜéy꜊³©Bøþ 0‹x]Åje¯]R\èáú`ÅuöŠ.E­§©. ʻ릙¡B<›¼6¾Áê–råj ÚÇ9Á߃⊋ÍBf-ì,Q…Ï‘N¶#Ð/•ÅÃðþMÈ" q«ÓA@2X~ÏD0á]C´°­ý[iÇpr Å@aTŠUt©©BŸÏ÷oݕҜ]©mþØA«#ÖO‡Â(¿®JŒiÔŒ¬6v[{„q®yÞÈX‘šR6Tí±ávƒ(‡ˆr‹­sŒè5Îs<âØ>¨¤¿•¦$Ç,·mR¢Fž#:ÆlBD`…H9PUôî›ÜÈ‚H4ªáÆ@ÿ˜· €ˆô·!4ò6õž5ÇDc>Ø>˱:½A0a¶0îŒÄÉ@Գثöƒ¢qÄ—f²´Á[æzÖÙ_<¤®~À¦qúŒÜbV‡ù!k29ň¼ÕŒ¶ÀB=Nº´ºÕÿ'¶*ÇW÷³€ôœÒð݉¸~‹Hx8ʘÊþ TÒ±èGFözh[õo5,¶-¾ÃÝX_ƒdƆK@U€•ùÇO¸Y“c` Ô¶49J©=‡¿P_Ìê8tânRç÷‹”Eÿ!Ç5>ÓDÐ ¡.ïÞÅ/Qê¯íÕДÞÒ¨1ˆÎ¬`n³ÝÌ[4N}¾¿Ý¯7}.úµüpN¬°Èê ,nå±{µ´r¸©R Ta¨°±«ûüÅ™œ^<¹èh ×vˆ!î}Û¿ ©÷CEÞËACéJ¶Žl¼Í$Ú„º© eü˜Æ½’8ƒÁœ‘W ‹Uš(í£¤5¥,´9ÌÖd¥8-Ù!Kjéô(˜³+ TXv¶š6F§—“eNÂL2ŽLìf) ¯¥†‰fª þ…‡-Ž3‰a,Ó¤L¼š‡ ÙXh b^’¸äUB› äPâ#IF[c˜K¡å­ä(~Ìï"!H¡UUrX¥Fe‰Â%¯.¨û¥‹]LDDæ;–PÎÀh d4f»XrQqzpzŸ0(#+bvt‰ ‘p¾oê×ìÌš8lœ£ô@Ü™¡G¤\]™œ‰¬”{AÕOÌ’¦?N$•~†; .ÎqY“U[Sèå <•N6wgÍMކ¢OjM–QXó‰cmg:‰G‘‚g˜ …´Ž¿âøá…³‘a©¯]¸“1è "À|btÛè³Ïê”1C<·à²@Ð3Äôp|ß,†¥ŸðÅb (åäÇÕ½0 NGFÆ3 RqðŒL“Ô¦¶BVàˆHk i+F8[}3l&iC¾´Ì£¸ ©D”TNž”ëåò„+‘ŽX $ñõFŒžD?¬¥ØÈz r©8Iì]êu*<à@ˆBmrÌÂm¶«ÔåȆžkLE‚iäŒÚ{« ‘4OJ Há¬Kp Ä\€a’h8S§F#Dy*ß!]Ðoä¾CÄ|O:8Ž£nÅçñXÀGà!r"¦“Œ†yÊ'“ÐûÉýVy#bÑ5*ãi&( ÁÂ6lB§ ÁdT H@/e'äfÎ×`ðFKÒ¹È(OíÇ’‚CžŽ‰Á˜¦$„å/Ô1v|Bxß8ÏMDô÷Áݲ)› Ýfè åѹçe“RÐÏðqd´.Èä(@· \C¼ÐOTèùdË*àâµ)“ ÐH*€ ÕÉš sN·,Qðj)í´™Îz»æÍ °'‹ò¡lB ¯£ÍzÒ&"žåÉøR èo×4¾!y;BBJûª—O] Âð7`цŽ+t9=7°“ŒõÙ92´ržRDÃîü|\¤€o,}±‡!î8ð®õ V4¬ç¤${øÊ ÅHRX«Aˆƒ&Y¤Ò±І8‡ , ¯â‹Z¢sñl!ÒVÀ-…[³vËAmü9ÌeÃUÇEMí­H´Fø‡£€E)µY‹_‹b&_“£ñfbÈ“‘]È1w_ ÕþÓ ÖÃ]1%Ô˜»|)˜«_)‹H.+ø˜¥2Ç\{¤?‹7I§åè›$*âM€ðâÅõõÜPe´‡…mšT*¶ò$× ^À"~¬›íª[ô,†©ó‹È3OÙr4€ÒªBC´÷±EÍB ûÑÑ%a‘¤aÈ+T¸–½‚L 0f(#–"k¥¡†É_°]µÝµ%PAø•yŠÊ›$YÔ M#àLä Ê»¾=H„©­àꡜˆ. òøªöL Šdª0¹æà(ÃÊèd}ÁÔ sýnjҰizS«`j©Æ›mßÌvϺ/5>*ÌM£Ì|ÆÐ"*¶VigÀAú’ üFL ë4¡ÎäÓ$-á­„¿¸­¢M—lúÓÇì6ç >ŽŽã)´¼Æ}fq_WD! ¦eÖðžoÿ(Hƒ¼Ì2ñø?/H=¨?¢*qõ¾qU½h6µ°o¨}Í k-Õe0 $/~»F”BsÏ ¶:˜ýØä´…êcE§v”ñ³FsH¡%ši˜·n„þ”e‚\‰å5ë<žíbÊæ=¹£f>Ë PCE¥y®ÿd,K *3Ô ο´¨3à ˆš¨¾íÑ ûþá…ÿ¼Ù¥Ã*„Fœ”µ5bk¥€e–áV¸àÄ]UOˆÄðu޶bd`G†s{òKUOÇPáú݆gQH@½e*FÙÄ©/j’¯sš¹ÌW°‘í ‹]—Qi ÿÿ“ÙÌyÜåqÉ@J‹Ž¤Sœˆ¿í­ºX¹ˆUƒ)ì…r& U}Ñ“5‡ ²/꼃M?¾Fo_So(qNðV&,±0Å¥CÉ,ëô\ð€ÒLØï¿¯5¹ˆ^È¿ ß#4JoìcÓÔ¬öXøÌmTˆ¥Lªˆ4Mœ&‹s ûaókÿ#-¨PXD, ?ðú–pVþ0¤Ž2i nè·ô)nCwDí¹1¾BȲ¾)xgÚóñ©kê–ã†1‰û>¢WF{Piü´›¯‚1„Uä ;†w,ë5ž¿Äû„—§NtŸÍ3/ÏÞÅ¢ ©\KÂvpr˜Ô’ÀÏ qåúñ'‘‰Ž#±†9T¾Hr6^ò™d$E|cílÇTæCH{÷' *Ý ;±Ôôóe<>¾4õnóÅUêo~øq®Î1‘GâM6QjÕJ¸QÙT}FS𯮺ùé«»@æ®jfM—¯ø‹-’ü37qõUµm´ÑíHÌñ‘Y<€UÆððá¬ìZZ@üt8,ÉÝ–yÌ$Û]­¢!Ó«.Q·‚æoÕñÈ<›eâÀDº‰ÄÁT(^6µÏΠέ¸Þ3¬8µ¸Æ¥!¿†?ã C§a…ÒFjq7Vð"ò#[V0&™Ê_Ä×’ý?ý¼ñ¨ÀýìqˆYí¹[ÅÕË>Z}©‹*"'Dÿ{üc°  Í Zé£3·Á wÌ îìôâ1)_o⬲Ùä\ðÇðÃQ,F‡ k… ži¶0Ð Á”áï´â„†²-¶aŸòòÕ‰Ô¿ð$hJ^H×UyhîÃÙ'¾’7KÓÈ7 bç3ŒãtõŽ*iÄ­Q¤giºeI±„(t%aàá%R„©¢8ß3GTUžª·z$àMºé*~‘…Æ€J–ÑØµn–u˜ B¡4èγRÒ-:‹;qÌÖ½Ñà™(™Ð;ÏQĘ)Ô“1EF÷ËÓv:T~ïýלŽziж4*оT‰\â˜<þÎÍËó韴©5ï¶Ç…ÐÑ!e¾f¾ƒO/wŒž©)’Ê9EÄ˱üyFæìÀtá /t>^G^,°U Ɯًi!sNvSš$ü!òèÔF¼qÄÄoB2¤‘ œÝ`O÷èe‘âæ@5Ö‘¯r;[Uª­ª ­.|"?Î!Í€åÒÿj@=1Ró¡Šàk©k||êü½{Ô%-º®Ç=öÂ'§qø¢b†¶ Éš‰ 6›š¬ï€ òtnK]{ˆ3H³ãêBÍ à¹ÜÊv,n‚)ˆCŠæ«Å‘]|–TQH‰„<£GÉMx4]b  ÐPèã|'øì± *‡ØÏ!ó¶+qwÜtž™Äôn€Ï‰!%|O;„Þ6ØIÕiMùÒ›Ú(½ÜØdVÁe»0BßÝfé\N¥¼ã€ƒ|ãÚb¤_ƒÔK1œLÄÏã¾T%Ÿ’–éYÂ0rÈExìÀשÐy®ˆLÀcº\Y°q½³PLØú5$|A}½ñ@ÉÙt¸-ÿ{Õ}lÓèá“ fŽ~möK¨´¦¦Kƒ¡þýëÚ½hû>‡G~^ +‹±ÑqcEyî'Ûƒ¯›OC0÷«ÎÕCÉ1"嵦4Oë-µŸÜˆ|ˆ’SÃ8cÛZž<=Ç äí•íòM4¦t?ó¶]Ë´º9 ž+J3¼Cd ¦-æd¼®X!Ê!ˆ¸ZVRÒg ø¤å‘’Å›¬‹̲¬\8Ú^òù!Õ#Á®xÚi ¶»_ÈDÆ™.ˆ:‹DªŠ3‘‹ÁdVÜmw»ZláµÓŸ¹yB|t±ŽR†›Aí31>Û$¡UÎl†6í@;ŠáÙ_«ä4tA!²3ç|éõÈ+p[äA¾…kî˜ÖæXA¢ÂkOFšt¦Ün¿”ípP(Æòˆ6Ø•Ô"þ²¨è7/„1¨)A¶@VÀ !1ø( •C@ðª èi.**ÄPXC'‚ ^GT<áúe“c—ãMB)ÛR…ÈÕÈȨ¼Ý¶¾=𻋤ÞêQñ”_’*]‰¯W%çC}÷œ÷Ž3V> ñ‡ÎO\/ññì{§7,YžoÓÓ|²CÇs xš ˜!qF ðײ‚ò·æã`Ê>ÂÖ˜F=Ú"ùžgË^<º‰rãU,dʼ1¡µQ{!ŒXX@“<ÿDß-qÊ@7´âèêø}é#£‡*ÄvÞzýp³wÑŠtï÷™BoèdÀ›öÅÞšÁ z.­î U1âÀ÷ÄT QJä×ÈXyå‹‚zõ/6×!Ò‡]8Sª²rv=‚§ÑSÈËÆ±^©É5ØÝH%M©×#p`E² Ng[Ç™$j…ßUבK½˜>æÅ˜ä:ÔWô¢P*ŸòÎBM›BɯýÃ+6șDžiÿ—RýØgyürÏ%/^Mà¦UÝÀã{U%b¨ ŽÞÁ(êŠÒbÜ9b/eÚ(h”ƒ÷|kBnÁ¹ ÄÀFVKŸ_â#(± Jƒ)™øšgK«À¤+žìC ¥(P‰—*´nŽ4òŠ@²û,ÛNkg€µTÛÊúxXc•6g/9¬XO,xÄüå™q+ÒdaSL§ä“ÄÙ.9¸óÃØˆøÏl¸¹9­éê× {ýeT£òŠ1؆¾Gª†G¨¶‘ü)ÍxaG#Ö\¯ÁóN$ÇæšvØ`KŒöát|ÚNU3ÕÖkJ|Zq9z1!'Å"ùd}¿Ÿ ¬ À¸yuJÉý¤YygF/F·[ëÄr¬š],O'­ãct4÷›ÃŒ”œw¢ 6¾cHì³ÿ`&{SPeÊÖ–ùmÙ'/j7Ïí4‹Gâúˆ¤j|>Ì©( EAÜõúf>b‘„ÑKu rn· ·Ïbšc¿;ágKC;8irA1“/øß j€¾}²yI@èT|ËdPÔ8Ï„™Aù=å)€<Ão *`|f\úQdzé¯ð‘-øÞOCŠÀÆÓÚ¯§bØþÏp²ûçfÊÛ\ÙˆÊóêr+™I±;9•O½D-#³!d¸àq\²`1ö$Ëñ2aù‡±ðYíSéìÌ-ƒ} øûÝLq¤†A-Ü9äIRòÂGn†ê„#¡D#QM7—Ér\"k6Ê ';+[±©]¥wý3IK$½«Ž¹Xðð·k¤‰Û-—H7 À¾ +>%'ûën! kÒ`X=ÁeÂmê^0»ÕŒiÓ-€ÙàGAçM‡øÃ|á3X¼pm•ºÆEP̬3¢0·VÜ^Oñßô,¦ÍBQã<èI°@sÉ2sÄ‹›¢ÅîðMQzÝ&[|I¯Aÿz(Ò mIo†í¹@F‡tæµ;è5< , ´ØvR?›@?}øßeR†Å³ã­®Î(hÓÁ4…o¯ÀôwþžMyKjÛ¶:: òÌAÁ.¯ñðvq×¥Ï2$ºÝ¸¬Pë—C‡òøÀšÝ¡Kn¾sGîÁ36·N GÇÑ…øOÆ’JVî9†ýAߍІÆÚ'"œ§•ÛLüV†ªׯ÷Dàþ±ÇtGyÙyê#BøXDÏ?‘fªD,Ýñ–J×¹Œ^+ž4è’Ë °7¥J‹ný?V99~:?׆(¾"’ täõ‹cfIb øÆ‘ÿ>¶ÿEyÅö?ˆL §úó `rïÕ&ÆÞ¸†ŸšâTýí Æ00â!Ðãk‚‰wgTþå/u¹À<ÿiJí{àÔx“7¬jë#tÍ*&ŽI kçA¡IMûšÕÄk?“Ò `ÞÊ»¨à‚a~)Íì¸@¤‘hÜ+v˜µ„˜OT8‡›Ž Å&…g£¦dv$¿o¸Â¨(F¾ÙpÙŠçQË]§þ컀^4ðQ•TP::4A¤yïO×¶ñèÐWóÞ´?K¢¯½'îK¾Oy©˜sºÿHÓXýà@ë~óJ ,³C©­“=ý.8’¢:d¥žàŽ£a/ô°BÿÙ°<äo ÏãÒ›càð~‡îbÇå‰0E„ §»jßÄÌávT|äé†É”è´v¯®Iôœ¹¼º_ÐôéÏ‹zP¨H–l"ŽÛ§‰W"ój?žš» ÀAF tŠÂñ‘Þb:¬‘ "ÐäÜ 'ÁÛ]`oäèsæ™Å¤. egÐ3,©©(Óè°pUR%D¤Œ5qIÝ£)PðÄLAdRdŒto=k“Bø7·lô¹³á¦c=jÓüiObçcÝ•8$–ÒNeHS´‘}Ùïm„tÙi >'è ´FžÒô—‡§Gù'n2ÓÉJ¦í 3L-%O• 1‰Lûv‹Þ‡L´µˆæo ñ-ŠÈ©DZ_-˜sc‘Iµ/Ü“ÁÒjû]+"׬ˆèy‰Q…8yy C`ŠÃàM:XS¼ÒkŒ]«ñM²È:˜©ˆ¥ÍWq`$E蹘 ±ø{X7žµfQr9”W”Ü6 ­¤ß^@tQ$YF´fhtÊnñKKn€¥(‘H [;}²/#”Ðíf|$T,ç¨@²³æ8@ìhÕ68ò/ðRK±Ñ㾯“6ö{E% Ç™r@MHä Ûý¤—ÓDFJò? *äêÒƒÁŽÍŽÖçhŒBÄS4ȱTe˜,F ¸B¤B’7ûßÃ@պĽÀ­^Ë@ÂË8 þq3"àŽOH¨lN *¸iô¼ÞC-½ðd÷wµ¥M‘P)7¡1ÜBª[×òOÙ o4 o73ð%¥˜Bá™3˜qhKY¶á~õˆeöûI¶Pm2ãÞw" óuß<ây s\fƒ¬…Kx¹Æ%h‹ÀK¦=k“q€+ˆ…ÚØ8ÀÜ@$fÃK R©äÝ¢èHàv0$‡`…¨_åß Ç¦s‚]ÍL‘uÔ¥qqœ[2f¦‚ÉLOQ*kúmò ‰Š=TÍéö4Ô°ù±‚å"rtÀdìD(, Q–Q!݃†ÙA„qË…i S¢á€Åú$>„Ê”ê^¾‰*,;LÚnïä:Pñ‚Ä’‰´îö辂¡¾ä",#f]$óW>ƒád$æXŠ©qt‰µþl¶dd]L& ^Ÿ|§¿új&Gñý3§²\l „àÖ›lU.í@MŽŽ\n)“Ú„ÿ­#œ“2AǼ IÏ·rn™‘’<Æ“w ¤Tƒäz0nÜʃr,(Ô^&¼ãÒ•WŘ08‚åÊvßÂÕ£³ê"÷vþÏc}²BãÜ*@ñI,Ú×wš÷0ƒ×Ùy˜)Ä ·s7›è'ed22|4ýP?Ò•kûhˆfy³î !—™ïÅ›/gG¯„ª#“©¨:ÂZÿñm=Δ~ qN]éæ %¿x`Uœìª×è`èÀ—Ç2#j£8RcŽ\‡\{éˆ8LÕÛˆìCüèÚÌ-Œ\ׯöÐn¨·ÅOŒD`õ<ôqLþGŒe˜Dú o)eD–Ñt§¹Ü¹K…¥•Mn­w¸LQpù,›WŽÍt]e›~¼²ƒr›î–“ë´,; L4¨f`¸&¬·KóX§cñr)F‹¸µg…¤E ЉpÖZ6$x•ñ®Ö®nTù¦yÔ@“ ž¼“«Û·8 ¬\Çã…|.&x½Æz ßÝŠ´ZB%9ø±D„@7ßœ¼ç}œ u¹¨!¹´BåàtÒžŸyí¹gå ¹k‘—9EÒ ñ.ÙŒ¶Ú.¯iù ÔmÀnÛ šì ¶ Ë óÖÎô‹†ßyF ûhžІw)®Aš²9ÈX”Á±Ø“”37U¸wª!Ïu÷-iÝÎ²Óæm¬‰ e"¸F‹ é½4 á$YÀÅ-M‘cŽ Q”Ó^“™Að1^ÛYˆ¦‹…é`ÞGíݘ©wLn3è@Ef¾Ð7¨¯aº¼8iÄpÇ÷èL“žX=GSé ƒ EY>&Rl[²gP`’œk6=€Õ©%i«~ÈAgŠD‘«@ÏÈÚh  ðâOÃ(f7޾ÕX½!×Ý)$ b9S´`XH e ~á¢Ç¸@–ý‚ÀXæÖ”c(@SƒUŸœ®”‰ÿ8ø%¡Q× QâzM¦Æ(܉ÚF}@³ß7˰îqóqÛH{5Y4Ö NÌAIoåØÅ|ž}¿- Dð&>‹/}|CÙà[$:—7û¢ŠÔ*ìt,¤Pþpðh’ iM¼„·- q4„NI9 ‡—‹WìçãâÐzÓH1.#º*Nzt…$S\„´0äÌ.Í2)ú¿n).­û°ÙãÆw,r·ÏË¢[¹;®*\ãpHôޝ,?Åý–q8$Ãà÷ÌŸa>í{¤8M=ˆ£OIºA ™ò€92@¶=é‚|É)…¿®—¾XhäÙq¢-èðXÿÕìh-Uø½2ÅbH«uߤ™–>¢ÎlÁ›ÒOJœ½ï'E8b€jÿj J²öFAö×+ÙM¾&ô¼¬ç XÏ•0:ë§aµ²ØŒP,/Ô(lýãu)¿%’Ä#Êréæ›Íû,—f¶GDÊ·£D^Xƒø%öQÔ§£{Œôi®è xø¢`Ôφ‹›`dÑQ’Ixà IÑõ¸ûõ=aGÖø¾6Ð!ÄÈg$ý©ØYM¦4¶’¤·YA1MNè¶„š¶(4q°¼YlƒùêWügé¾7$† Š£¦ û˨īY‰• ઠ%¡%Úð`¸'¯^F6ÙǵqmÅìâHi*ni,ùc~`½4È—°Ý4ëŸg Ï$¸a¡*xâ àˆ¯‚=õ2Ê0“‹HBÑQ¥@‡5H©³htUL‘áJ6 e)Cà<8-‰2zÃ!è„”âÂwÒpÖŽXÅ*Ê1§’æZ\†»õbÿã—C•‹e¥Ë?ù# ”I0Ö©ñrøÌCiÆ‚¥Ç?ªÃXË‹åßú 0\ok‰“¾…"{5K´û*Ÿç‡è„Þ":r]?ªX9@#‡&‘c²iACƒnt'*Í• ÷P& ´0 옙t€n'Üë6ª¨/h Y;<$âbó1©Ñ?=…àÖ‰”Á+DX8²TÆüÑΤ¶i ýÑ‘]8¹×°‘‚Ší›.×V€94¶ŠqMªLƒt£ª4ˆ`Ê) a–ÝkÈë@µIvn.’4瀲_c?'¦œªú™P+„’UØ“dràŸEá—àr:ëÙgß„®ë¿ÈÈÙ9NM…›zŠŒ}´ªÏ¬š£mN€•×X2PбE ;Ñ‘p€²Q²BOôÓɳyÈ@T‰ŽéUOG÷œòp?È{9‘È'FõrÚ'‹Õ@¤3 fÁCÂ>wRñTj@ŠÛ?#Ëý){ÍÄ€ó‚&½€¡ÉDsrˆ³>0ó+>É:&ࢠ1†Ad'çNf0\œÙêBZ”P€`4G¿%¨C¥@³9 d" A¢R2«¤dæW}Ô˜xF8YMMÙC1ãiBÛƒ´&ÿþ~— ñm˜†€éVÿ“Å’¥Ñ²W˜¯g}Ä.5‡sÅ{x±š.vaïa°^íÍk²ûœQ¨fÎ µ™ÙöÄ\EjÅRýGORbg¾¢åºHä—½ØI×AbŒÎÂE˜×ÁÉä›7Šv×E&˜*þ𥎰ôç… —AãCó.dâÜ£kú‹Ü¦¹²ÚûOÓ ÅT¢º°Ù^‡.Fhã1uÇ5^‰(éYÊÀÊ”I#s’¹Ó4 †^‘Rkì¿á‰jÝíˆfuä¦h ƒg@¹XdÁI!fS4{÷Aé)÷nßâúúš«ËJ¡Bv­õˆ.…û4>Þ†r9Ó,iõÌt&§€+ˆ½r6†¹3]ûù 4…6”=¡M û[Úvàa QÊ‚(E‘ôguUÀ;&´²2= ÓÀ#¸Á•ôLpD,z yDÀWvÄȰi£+›É{$x䉃‚õ-uFPƒÁ]sÈ©DMŽ(4ìyÎÐÐl!³b•áBÒ°õšVƒJ²àËŠk<ò^'Vf G_P±0EÉü3­è|bA8Äó%Q…žÔSãéõ§='Àq¾6%3Yéóú>᪠ÍÓŸ,žqá96JÌví,HÃódzÕÈãL[¢g’;÷>%Èó|~{G¾l~ßx1@Y:MTTÔØú$bŠ„Aµ}÷¬$õ²r¢L´çƒ¤O •ŒÎöįÃ<ý NV ¶!Œ®/Ë›ißâEá£eŽÝ4Žð–]é¤\]¢¸9QÝ-ŠxKû‘œS(á#6d—BBíò,'‚;‡SШ`ärsÀÕù~¸æI³,æ//«Ç {åJÑJ`”ÿ|­†Rì†`Ï4tÓö$âø'wL¥ÿfŸò±Ë0à€W°ã€×ûvâ#m‹sñMuq2 ¦ˆL(óC¶ HSZHó|zˆPp€AŠÒ”öîñÑöâÔ„üÉÇ4=n¤V5ke3ü·]® +£RRd…Ì ]Dˆ7ö×q,{ÈBFðÂÎhˆ@RG~¤{cHfŸˆ öTâl†Ñ*%ºŒîn*8àv^ZQQ[°uý‚ÔQ%Ý sÛ9Ò^¹=ž„’"ÔEŠzS4„h+%üX¿½è¾hæ$6Š@ˆÃj¨0¬'å ø?,ˆ!3øh…Гa@mFÕs”X Õ9t×Õ!”’(õ4½u·>‚3Pâ Ü<Üð_ÛŠ‚|4¯ÂS&g|«Å¸r;•ýàêL„Ò.âQ²:äp DfIÁàP§ÄCÛ4 oþÌdæíHl,p³þƒ¹ ýd  ÂñÌpÏýˆµÞAîoÛD 0(,Á°d„Pýâ›Ü6zdžÜwy›Ÿ ‚„øT£Ÿìð<€r8G:>—”ô#¦Ñèîg\amÝ„eèyDôkû`bºÐ˜~ÇAÎÊÔ‚<2j"HÞ3N±LÁ¼¿Bc®-}ÍÜÕ/…=ØG±ïë÷»®©Ñú»Þü"àÚ˷܆ÎE„8nÌU8&ÃYÙÛž?–µÿ½W&Èvn8«n­ÅÔ2ÙÛ2¦z\îklí[úH¿T”eý3\ü¢@î¼&Ô¸Â%çÔâLìÆE˜aPï´ˆ3 ljO˜õuIpˆ`1ö@¥‹í!&63¯–T1ç?Â;ú¢¹°ÇÿF¸;£Þ]ÇTB³Míy£ ;Ɖ‡lnt$XÓövt2LÄ0[P±i !=#ÊA0T“ÝK/Ô-Ä‘ÛwøÜŸ0·.’4"©W†Ìñm47!Bg¦^P‘¥BF”œÜŠütHûá4xc8¦êûDhä¨óEŠgðe¦ˆ3hêòÙzWóf·Æ ÂH`”"YÙPˆ2Kzk; wÀ‚õIÛˆáÁ\"11¾¯ñá‰Å­bôŠ—Âý¢\8“cNz°Ž—H"‚Ä×jõ}ðÛ:ÈgËuŠr„¼ŸÔšNÿ,Y<ûÆ3ý­Á(ûö ®Ðts|%R¶òæñ¦b-Ôl¦Éª•ÇåF ¥pž+¿`3qƒs¨¾ßáÅ&ÏÕòhö¹Á3 ò*‚«ŸÅ Î0˱¾CZ#‹¸ =«‰a´îh  8k‡v\XXQ"1,ƒx â£Á:ô¿pmzŠ!>Àª–RŒD'¦êåMN"Ûy‹- ® 3#÷íJ¬³{e*n<ß´„Áy&Å©B\m“í*ä„ò™¾[F ¤ˆ÷ØOé/l^þÖºçA[" øÈ)øî0ý‡Ç÷T¬@ÁX—DÜ8b/( |½°ÐWHƒ£¶BBµÙ)3ˆ3¢ Ç’ ±~ó$ªªÈ{:ºŒ:> öZ2c^&òüÄC\V€Ûþ€hÚK§ðÜÎÔiÔ%I°,q?À2ã!±,§a±6ø4Ëh» åö$¿Ów° ßæÝìhÇɸ{¡Šz`±G¨ËyÀìvOmƒ^ˆ’ÅZÎ1PVêê”Éæ9ü¾l]ó‡ŸíÃÔ½¢}LÙ¾: î¿\x¿BnûCú•î¿°>5®’?ò ŸX´Ÿ, @p*øx8C>µ½µv;…^ ÞKëÎÇ4¥p}%xK¨±™ÌR ©¥j“4GZèÜW±‘ Œ0Û¤Ëu< .B;ôŒ4"/ºx\õΈÊEc|ã}Më+ÊsÝKŠ= rÐfÁø#uÆnb²1ÙÏ<°Î ÎñÝ ðj/<$n2ð*ßš˜,¶Óí`R€nPCJ ]…-€ ÀÈps»oÅa D€d¶î"Ž,â ^„‹œ¨]ú::1cw^_¹–“4û±§YýÕ 'Zò¤Ý’CáJBÓÒì)wf¶‘›,È\j>„Ž¿,ʸ}HÜ­š7»Éé – @ÿµŒ VFÖ~dGó¸(¿qy0,|›\‘!*ší¶fÕÐÍâ 7Dˆž.¦/úBм†)§€Ü[§BÕ'á,TŠû¾â<}ŸÂíÜäÊbcS¨ ^ï.5#bâgxP¦˜€KÓÌ’™aD 6‰ñfWžŠeþ&“µ±–ošåœ»(Ñ*Éì+ PÔãÃå*8ê!è_£f‚‘¦kÐg+mÔ–ƒÒÖÆ™¼Âm¹bŠMÀ/ŠdG~ *–~È-*üw"=¥ñE9|GÍ鋈ŽÔWô Ié5"Ì`6:.ß‚ªÿLð‚HÌ*ƒ´î•y‡ ¡Å…z^°N€‡å.øáB¦Ñœ—™í2xÝe©ýå%í¼5.;Áv+$BkœR…ûáPH½†+Ƥ™«Æ‘ß@˜`?‰ áô€²½SÕ]œBâV|tzpØCÒ‡bX‹8T³y™Kæ;ûTšM† ,¡C19ì°% B„à¼Ì´¸"aÉÆ¾/3W¥ ›"ëʇYÝfá5ö}æI‰,=²67ILÉŸ#À{$óL4V9§ ºµïV<‡›èßrnö6 d¼t_Ó@È –;׺náÚ0’¹!–¾I+B%ÃH‰šëF½fdZ ÊÈ/RÔd_ñäAêã€I‹WfrqµÿÎ++aŽšPY†Y¨$/‰ÛXmƒ¢V?®°WÖÊÌô }lÂl`À+X• 3Àã’e‘fG@‘Ïøo·ˆnñŸÒ:Æô,Ö¯ ‰\ $¥×fú/4J„¦Si!dßœ ¤É÷¨ýì£%êêЫ‡Vëœh”yí5»Oüßèl8'Ä«ï)h´ˆûÑâ({%FÇŠKç¶zª=v[`:ú£ Ï$k ÷Ï%FA]ãQ^¨EU YàœÒ +›_úóÕ£¯êý:éÈêÏ•ÂQU)GYT$Ï19T*Õ™¹ºÈ€Lö³}Æœ’_ü %î–Ò‘êŸNÔ,3ö=ëú$ß8†Tœ ìDãánr€DŸ¦™Xà@Æ2„Ȉá'Z¢‘{ è^qÝÔÖ^XDò¸Ï¯˜7ÜeâèÇökTÇd4…ÏÚ•p嫱Ç‘=Ösû³+^€IÏÔ—ŒœŠ¾gt`¬¯i4÷$ô**¢ik•9™®åM$ÍmÜ*¡,x˜R¿MPO“ŽT­ê²tÆ:åÀTGo•Ûïûê¢Fò0½':|ð¨Tnôe3/L-Þ*m¢ 4§¯¡²\êBO¥-³„âÖa·¾a‘ƒ+:7â×q(ËGè`‘ÕÆvŸ ÐGWH §?Ž÷uÏ4ë[í Æ*‚Ìo%äšøY)î@0Æ|¥¡³$€ƒ\‹Æ4p¥¹ ×B#í¿†:XÕ°Õy“¼àå(jq±S ªQ1%ÌÓ‘À7| 4T{Ö6ìgyTÏL2k}ë3Û(îå•aþÚ“Íûªs¤ ®òðÙ”N^5´JiG$ ‰  Á— ÆÞ æøù<•ÁUTÞ§åµI×å0D˜g_Š=áÃ^Ôóa¿¯šGöí‡_ ™Ö£„‚ ()Ì/;û©ófcS‡ s}¦W $!_ö]oBñ ·—‘î'*¨ÀQuW‚·Fªcø@‰0’¯ ˆ[”(‚èi2x‰¹|¼úwÉ1 vɹ‡skÃç¶S¬À‡£ÎƒL.ân«CÙå_í S5¿¶?É®¯Ú7 q?$ì¸Ò­gÃ,“ÑZh&[á9$@Gü•‹ ±Ï±Ç=³p–€Ù>&¡Ùv†>¤ƒ^lãá@`\S+.foÁ.‡]Ât mz©ÉQÎ}–傸ú-~ÛQeb»ÇIýCÆš4…|²HƵÂa²Y}1áÊ|ŒÀ¸èÀø×¬Ë“ºí?M;R—~Gä4˜I8ÉçV-±-tÊ]Xïáˆäè‰ÃÔ>;*îõòÃw'ñvÇÁ$4v$_J{Ñ'È_˜Ü[sÆuA,Öî)z+ÀÏßz ÚE…ñ·~Q4´ØÖÅIp¹;Ò/ÌÍÏ•GÏ<Éß­ÄnÆäsâv1ADS••Rݰ.¨ï~,îÒ†¸šfP"ðÏá‹èþ"cè’çæÉCrá –:ˆK Ä6c°È| »†#ó®)ÿ‹3Âa5éPrAƒæ½ B°¤‚Õ€»zâÊ oª …fÉ ‹êØÙÛ ïÎ ¼)ä±ÂDºCFIj–ÝfaD#˜e#õ\8bÔ¨>ËøVŠ¢ä³#ÓèîµØŽìÁS!Á ûV¸¥íªë®hˆ^ÕÈ0º#±xH"ײ~«àKלݤ·$ƒTĠ/[\wŽuº!t Ëîs{XA³Mëè"G\Ç{¿ bu³™!æÂí»Õ9¹¸I˜v­ñ­Ç#…Ù" r0iNÙRG74„WØcØ<å;L‹onQ$Õ@‡_…v]<_#¹8 |Múf–eÆ@P+ÒZa—;à%Ø>“¯¹ÙãçB؉¨c(G{SãgC§ŠøyZaPÊòvÅÌJƒ¤°+½a_VÏ×L¸§=ºÿÎà],^øÞ ÇÐ’@¤Ó®â Ev+«fs9ßLpª`xAHƼF`×mÞXÍ´^Ò ,tÁj¶æËÔœ- Ô§® Zö¤¢cŒDè7ñ ñAA?àÎd–ƒpü+)SÈnËó< JB Šç„ö^ü‚‚1¬ìÞÜO×[õ\çéOÀ »V¢bH €CåP:`WIé~é˜CXãS¨¡U72GάXÖµ–ÕEØ#’2tHEÕs`õwñ+Á–ÚD·=uÅ~bt[_NÈ«(vË‹l(Báꄽ<(CBÀB±À#À - ~ƒÆ],Õzù³•@˜Ú)›!ÖÇþ¯\|O_ÌOŠë·ð3øýáËFM-±‚÷Ê¡úãF¸Óˆ Iá6yÎuëŽ{pOY'"í’ÊäÜ-ìÌûÿ¤.°ý#dsç!ëáLGoÃv$Ž~øŽ]PN²{¾îNo`Èãobý…Ìøï«f,c÷ºƒê8gм3šsßJè»uôX,zL5‹!œ9·tüqðF5ú¬?XaéÐä·Û²Sµ+ó&3Åa&Ôã³ ÇHÀd7ªö‚t_$Ôn«D¶%DA¾*àHq*X=ñûÇOO¦ïOjúôßRŒ8xŠ$4VØu–|Q: ìü¤¥], 53åÍIaùøÉ0_&,:ÝœD=—œÆG –Ñò…1U^,QÓàë>ô¢Š Þ´ L‘¶5­@ˉâ*/^Én`11QU‚ùUksœ‹ìCxˆï¾‡G!r†æ6¸syxId~ÙÊD]­g>gÍXÄEÛy3Ãn©{±XÈÎó†ÉÈÐóÂHPôW¸½Ø> ߨ'è DÛò²™‡xE&¯"&k8ŒùD=œÞ'Ì!A´õþͱ-´¯òÐ4­Ô¸KS ÛŒTÀ»" ´-WïdH®öy]ˆ6ÅŽŽæ ˜y±g [@—WÙ@‰|¼ƒ`q£é_ºa1®)ŒÂ:l4s\Û/DÉ ±D/µ¦Öõ5IÊc U5¿ßÙZ‹êEMGŸÚí›N¬HÞ}ie¯“š¡kýh’q`j ¡’˜GOß Ðù„ù92¯CÇ'Ø…«:Ò Ù–„b`j ‰ü{ÃŽ]ìŠ1ËFÐ ‰«{@8ú‰wibºxV)Šæ+Q9ždf¦¯‚{@”b,";BšcDþÚQ‘·9üeˆNT&‘…Ë^Ïý2PoN06.6ñym·©«LÌB4â[äb†¡(*»NùBQ°¨TÌ"-᱇< Š 50‘´Æñ‚Øyv†[ˆ˜€âö¹ˆ¿aÛŸ«\_ËÐÜ&p4{d"É‚ˆZ½ªœä¥x9'ƒkÕìÉGñ`œZ”ûá¼DøqåÄ8 Šô#ØÌ8@ãD0A¬k†zD&âíqò¡Còž¥@7ÃŒvúþpADQV)@™è\Ö)P´­{×_\ã…îœ) €”i@Ó²£S»PnÌHa³¨>qaNBñP ë|¢T‘)$Æ¥•ë0çGÃ-* ¢"^ÞצLÿ,ìü¹B0}¡ çãüÊpB¸3P‡öŠÎÒ7ÕBÕ,XÈì q0Û;ÜŒ<x,Ì*;ç÷Œ/'>ê³rba¡b„(XËaiæÍkÅÊ.N‘LèÆÓÀ„vc̉çÎP|L(XIC‚“daLV$亡'?±Jê½J¨µýT,ŒªpÇ™ø%BøÅ»É¬|9YêcµÓ¢}áSã#6ŸRlÉ>Yx¹‚| ­ÛþP U¤8 4œ V ó†ÓþÆ¥CÛ´hè±Ð‘#×'*_ñàŠÖ½à!të.²¥¸¹ÇîŒÃçå+¯±"éÍš-zÂ_ˆF`P4À‹rûÚÓŠq•ùž¿}£èM€4Ë*ƒ0Ίc¯Ä!Ôˆ2'‚ÜàÜ…9®AEoJW¤´„wÆ;kÁÁ¹þùzxšs¸X¨mhbHtóhnHh?£Û¼;ÌMIz›:°öaÞ28z˜ÍЈ}òŠ’©DÓòÀ}†\Ÿ«ØìW¥šPþ…(5€óc‰À§ ¾„R}C(3ÎZé)Ä«»¢=±ð#ˆ¶ŠƒÁîÀ«‹ig‘6‰àæRœ  $qØšHØÎ,>,wÒáÙT]70‚Ñ¡(éÙ4m´ ¹%§p%Šë»]ÎŒÎÉ£w¸WN"%§Y8t>´G<yÕ!¾ËíÔJ,³þy«mÔ…­¢îáW”™’o6`S QÀÉkq% Ža5øg›ôc°ˆ¾DòßVñC÷œKõK*&(£ãdªjš8*€W ?ì{RU•3xÁíGhe?I‘K”°TºX˜äçÆ#FŽ›¤bÈæ#¼$…‚ø‘PÈɼP#^‹Áݵ]yœ¸áB:ˆ `xòÛÆK0Ÿ£© ÷”—ðü’  é´§k]të¿$4Ô¬xc¤ÿ?Ä¿àWÿI³ %‹Ã¹é¢@8[‚°nÍa(¬§p—c×FSdDôå3_ˈVv/3i^fbsxšÂýj3Òþã­õàY¥¹3lÜ4 Ó-°º–›E«Í=s\YÈïÐÞZª Á k]kñË4‘«êm§ì2pÕ¿Ê+¥ ~­qõ!Qª>ë¥ t»ŠwåÀ ý,\Ó›þlMs@“ý' yÈ¡8™hàISú:Õ0ÊìÖ’ßG›ØzK¿‚Ãnx‹–ËFólÁÛ„4™òná·8‘HIÄ œ½Á60~úªÕ!lÿ%øt ©ìºaÊ´¼úT.´R¢xeèN@¼òáÒp aÏ:æ»×i%¶C ÷-0{ó19LõK“l­KMÈ9Ôjô„I€—?iÑÄ•ÌFc?hÜY¼Áƒl l^ª:öö«þñZæ³mqé÷L®µîšÐÉ$ê*'fè X¯e<€·ôú¼MŒqAfŒ£wÖê%îÄëšaDUjZ"Ä2W°XØˆÝ GŸ X tp;ü)Ô?8hq(ô!–^)»wðN>€ÌÆF0µÉn†Slãÿ­C\[Ø,ÓS@S@cë…€CÂ>û÷»µ×`ù «ÿZcÿ4©ÐM,ÆçÒP÷Å­Í5¾„Éùb¸z-à¸6QvÉ8ä/æ@l™ÙËKn¥¶/$·0Ø9ýG•C£L\&ƒéfû}ê@bÍw,™IK9B *x”»É4Bï1"›kk«2ÚÃÂDIx ž0jäÇÁžå¼ÎnåíÊ07ãg‹» Ø”H.“S vªM\ &”k3•¸xO%"•±R$ÂF àË&^ê2Ÿ5z’5»ä& œÏÙ®ÆÝÝ"]ÈÐkH™¸¿xý&U/ï1Iö´¯VS{I^ÚÅ2ÆxšÜpøØÔ”Ö7¹g}BHäzÆ Îkw­.G§(˜D¨£Š± °Ýتmªñl„…Í3£õÖgžTIY¯—Öi5ª‡·"aLã±í¥B9 æÉå¬,(ž—އC+"›Ζgƒž\ýÒÓÆão§JJ“Sb<Ÿ%cûFh©·jÄ*Y¯ÌÊÚƒkb@º3M<3cµÏ½ù•ù4 Xî“JqyA„ï&EÊ4kÄf‘AÐ ð#é_¶Öå²q7È<_©Ðò” ·¼Û‘LÔŠŸö ™Ä1Y^Þê!¢ [—Gx‰vƒcúÃQ`!¦ð&ÙM{®ÕÒ0*n·N .˜ÐD˜‘¾éÓ*A:ôIÝòùèLáb;»(PxýÀÏœ Añ³ŽLÂmSªé©`ÿíë±àã¬Vº¹C®= n²±!²z¬+ÃJµA8HÖÿ=^Ñíƒvψ™9iˆ|ƒÆFtƆÕôAÕ †»ë‘½ˆñe&f'ƒÙ› Ô2|Å`',äÀ“þª3;²„‚(廨§Ïæ€  M|‡€`Û¹üŠÄ\k¨H^ÜV<†§æ0>ê‚@5÷¦]òPWóRz‡èÇiáÕ}ãf°‚æîhiÏ ÇHÛ¼DЙ[µQÁˆ)Ö¬ŠâdÓÒdö ×E üœÙí$Í š%JÈտZR)$s‘VàC”èy–…Ž-@Z6\êüç]è¥8¦ÄlŒ‹‹{´=Ç—4§DÂ;Aº}æïpš7 /;G[© BèH¢"íæ³2Ò)Ä~ÿ4-ÕoZ‚ë?1› Ä®ìVF÷²+DÇ"ÎÆÙTå+¬$z2ë.Àâ™93½‰³d;üÂâ!ë*U¥9Ì™/¥Âæ‡à£A¹#!ìD{âÁmÈ"~`Xºq ŒÜ×S¯EÇk0‘½º¢Ó·3²Îö^ j~€°ã±#뽸®Û>/1ÚX}nŠš‚š¨T9;¢÷qBSfÏòér³ "°fÖÌ÷;럘‘(û8[‰øŠå"SZ¸ †Ç­œ¶˜K`øO«4O!pd7X@N73ŸEÍP앃8r¨N#gm E†dÊ1ŠÖVçªÆn^Ä“,B¡,…»³ul®ñ&˜!œ˜ñ‚KlmӮѷE¦ÅCŠé|mêjН¯P­hÖ?Pö—Ì ¸¦`2ДȂ9z½¢Ë¨Cš4Aq¸³"À•¸‹)ÝŠŠjýOè%cÌwJ<ÂH­..,s+ÏÀZõßq&VMs^‚ÜÅq˜½&|-üí.Ò=,GÂbŒñæ\º0¿Šdõ Íœy#úó‰Û±—5^`}/ œÒx4ñ6QÁ ‡êü:‹ „ã_m™ê75_™—årì1§ì>E|€È 0:™MÀÎv”É}†„Cà ÉK—Ív!+Ì$GaÃSú{{ íkÌJü<’ /´‰;²‰’Wî4´¡º˜E<‡¯ Ö-kõ8ÌèÛ";#7tµÁ„ÿ ¬¯Öšû¨|®P»2¤r=äË x´=Æ®R‡Ï—/oubæÏ%bÓÝ/qOÅÿ[6[Éfµoh­ý•"x(°…€.Óß“@ æ Jìb¾€3n™®Õüçñ§­A T$h¡ðbZ®¤˜ž$üÀXêÌ Øåx¶;Zß7È£ŽÒúáŒwNDIS¡H„UùM›¥R1Ì#Éñuk¾˜Nt[&ZjwQåeì8²`sI0aVÆq„¯mø‘•x!²öš™Û9Éýÿ¾÷.¢#¾ÄPÅžOwìõ+“¥Ø <Œ1…5EöµéYS°ÿØæïÒÖœ ’ŠÓ ©ÿâ:cÓxGdô §–vf=îi— Çϼ!¾ËLÈ>®ÅžçIdW `ŠŽqbá ÿ‚>L.Kœ9OðÚ|‰×†±.Š1Ci6”¶8ÃûI²ÆÎ’eÒG'š»JLŽÒ[ÃYçŒv&+”¿PŒùYŸMaBM5¸Áb¤CçõUAËGm=PSÊ£%ÇÐ Ý jÌ@«–æ«™©ª³³eLè-¦V&uÆç-áË‘(åðL§ÀÚKâ  ¯ø4nGâìÀ¥æheªÉŽ¡Õgò;j¥™FîÍOÁ©g2c$xJ…È£­‘+X§0Mdf°¿Í®©€g*p•††¦|ùô£Z(F_ŒÀ'+^OÙf›š‘E„´õ¹iVÄšBsu•=²™²€„µyY"T,®T±? |z ˜ùlF?5ííµ> RlOO;Yj I?.°8¥ù6o&‡ñ3U‡¡áà™a”À ãï¬_ÝΕèÒ”L?áLV ò©-”É%šÆ•F¯дí†×&µ¡PžÈÃüY‚&˜Ãì ®Ó]WóPÄþH ê"•Õfÿ}?©ó“„b`õ'AvšÝ_# ¡€s\Ö¢ÑV§®‰9W‡¥Ù ˽¦Î3šÊŽ]æ”÷äqLíÃ?a~SNQÂt^0„j—vERÁòï̃ŒĵB ¬Rds›`óýõ'¼Aæ¸ÍV #“-*å V ¨hkT¾U­2 *K*Ýl ÃU£Ó<AiÑÙŸs$<‡ðèl÷Á“‡˜»àtؘ7þÁ&J+ê3Ÿ²?œêï,fùL‡¦“‰4ôÖ„+êÚ%sßÀ6³äy*m±†9PzË[(-ÁóCÁ;TjÙ.¼IQ‹o_kn‰TúêÌ¢@3hå“+zÅÛÏþ#£`­©ågªœ²vU”;p†nƒzhbýB‰b„#‘^@à€ïo\a‡=Ê]p=m4%ÁFi6²”õÄ®!æªÝ[Fö›ÝŽ%•»Z¡ðR…Ï{Í„òMÉHà‘}BQó^vym^= š¶rÇÏŽ{Š V²d¾ J>gÆ£™Ž­Tvn^Cäè€SúË„ºR(²¥pö’û ›Z‘!¨4±³Z0’(=Û²LYËŸî`FdÍŒ ˶/» p€Ëm#0ËñféÞ©öýÒº ütÏ£qe@[DZTæ .Æêy$Îßîó´ßrh`@=:'˜8Vª ÁÎV`å­°ÐKÁ1 GÁä}ê$70¹¢’†siYÂ2?z}JôAÙÌ„"wØZåø­zZwY  ïv'Hþ»ó6æ§Ä$8ĉØ?¶½i'ÙËEh¡¦}“ ¹¡Eb5ùçz_¾ñêS¢Ó¹R–ñõeqÅ X­Œ ±þJ ÔDhq >ú0Ã⹜º‚ /òÃP~‹ÀmàMÙ±6â–‚Ó“¡…’þ‡ÀÇ0q¦´²`1 ‘¡A›ƒüfÑf¾iþÅÕ¼6Ÿ$mÐ/ÊÐ¥æNµªpäªhµå‚ôÍnñC‚5¿¼V“«áKEKä<†|Fgh¥Øxäªêæµg: ·‡Ÿ#xüYëNnüu¸37ä€ûOÖBwvÓBîì-¸fz¡HËCÛŸ?ÁþD,h˜ÓúHð[ùLŠþx•t OY¦ö­`¯¯Àr¨÷ k¤¶$¬-ߨ3•Ù4§Û€d@H‚K)òûÂ-±CŠ:]mK@–æÉÀ«ÑØ›·fN3†ÿaZ Xc}{6c¥kùk /©››0•ÒTN ª¼¢Z]F,;´8 EÓÄJÏ)°zYø’—Êßp© (DB²0Ù©Gÿñ@ ®+™U5ˆN@äšp„Ÿ°ÀÇÝ-ÅâJpoðŽð%Òz9Îpí0lų°–I>"{b`þÞEØOè—‰$÷Ô® ÁœØÚi!‡ÓÈAlØ¡†\u,Aì××]=ò"Á/€i ±cô?ï¨ÖÔЗbY­$b#“ÑCá‘3ç×ûÜf*j ÈDF†êl|3b³IÅ)ïÿ np2‡ßëÄá+q¦±:&VÖò·‡¿wÁ·ÔIÔzÏU ™y:‘ŠÔ§W}6Õûf#–h’Kç±C#6òìnˆP·2J´Ï y†ì¾yÇ&‡±–>]€è8_ºB…¤pÏR `?gcFФ9VŠ&$MùÄß`B^rUñyA}4'Íúöý᪊“nà 䉕ô{O.„rU^Ld!¨|€c#TnO$vÉ×€étÃpRÞw®’°Š ^Æl„ü*¼¯Û ƒÃ5ìL/¦Ù[˜õ(ßvãTõuÆ¡Ù3hH–—ˆàt;!dg‘ ò×êF$» ¼R‡Dn^e\»Wªœ/ÇkÑ_çÞÝ_œX£'jJM¦ L‘Z²ÙflÑ4è‘Ñ›F—ŽèŒª„`¿ÒìŸN$,-€ ²å)Hø½Á"ëï¨;6ÜÒÍ:vÐi'Z¬>úGÚæ(ñAp‹ãÁر5je‚ÞX¬!ÆD¢BØP• Í 9! •,¿øÓdTª°T…à†(fÌîÜhìêYXç sɆ8#GQÄF3Æ­nÏ7Þ•ÙbEtïm ¥ÒˆtΤ¤¼ÁùœÏÅúˆ@O‡¿Í×H2ÛTÀ߃ þvO¥t#åÙî=Ê$¡Åꆨòf4díO1 ų¤fÊê(#ªey€ t`Ãþ[¼¶Ê6%“¾úöWU’žwÒÅÂ\¤8*lÁ’8ÔQJ`(òøžBë#ßð\ˆ_¼ $ô;M°ÿ³þN÷=1N]áÒ ‰54«IM©>Áù¬(Ì 0Ìù ÕWѬ9º¡pª¸®ëã9lÃÀ¤uÝÎ`‡Tµü€•RÁš­ÜÊ+¦¿X[ †2[ž£ ¬9?SÊtå×£bvlËÄ‚5Æ?€ü.‘qúÍ\îá|"e§5¸ÌX³]wT’ɇÍê— ¢)Š/˜~ˆ›A|À]‚~§7ÕÛT}z/Ï ±åLÉÆCBX ¬g™#'g«0[Réöi*ƒy¬ñí»ÕádߢQ†ú)ô 'wDã­]±ôq”e÷%S_®|Tóµ­wpà?PŽÇi,¢ÛäÆiüí¾êL„QÍídÉH â ä#Ÿ«Ì2ºìÈG`êóŠŒ&áöá¯ÚÀio,™w€Ý¡ù%jâY°g«¢*Æð˜â§¨Þ&æ  ™¤ñC×ÏÀÚÇ'Ý  §™òÿœaš›Ši^­@[ ËX]Fªˆg ¡Ç1å̶4pïÅ%[Ëo¯P.fä,Î_Þ_KmÉ zí25 ؼà(Û)“ÈVÕäZ„5€yÛÀöˆx +³ill$hD`é8§•zF3d<^Ã,ï8³4ç{vÜØ~°åšp‹0‰fÆËp·ãÇHlF ©T¿€zPŠ0[l/«Zú´5f…Þ ­¯…ˆ¢ÍOÁ/˜¯=(T 7©Ð7øìTB¾{¸4·öèÔèÍq wÁì˜XÐ&ìcç¶’u_Ê™3™o¡–ëßNPM9˜Õ»UãëßÌŸcÀ Ä=<†ÏÒ]½ôR/˜*¡üŽØjïç_úÁÆAO¼?œ¨šg}Ž­™ 2Ç“¸åe8=yÈô6€ªk誽zˆR¸à¸ZfÄE䲩!EÝð6ôçp õ§nE€ñ­²^’(áëBiÕ’¾Ô'lKjöX£¸Wx6d[.Ë’¨3¡JQ¬Ý³ìY´n"ãúP%þ×d˜×Q¾WL€žCE.žÕ–ô*ÙØŽ_í½ÁVÎÕqJ£j‘TÇϯ<xúyø´‚);Œ‰­Z¨"ÑÈÐHG* ÕÁÛ‘Ä%hË­`w”™hójq°ž§—qA—w}7Pýݸ÷Sù‡ƒWÕàÂÐü6¼´¼ÃeUköBÍéÀW«©*B3&I(_ &t8¦îù,ÿ(GŠ:J+ä1¶xùâËlŽg€1â#È1]BŒÊÜìÆa÷-Hºë/7Z‹Ëà'i¬"^(óvPXs¡´êx‚xƒäIÒ»ÑTÚ/¤çO 5ðŽûA4Æ6r¼®h4­º‰£iöBOQh1%%‚ræ,ècáÚŽt¾S¸ßFÀú5 ¼Zz§ LìëØÜkøÒnŠ‘Èý† Ì<ȲE£Ã,ëí­K.Pð%³íÎH‰Xã\¨ôâ”ÀÍŸË[ÈÀ=n/ ½¬(|\ظ§" ÅÃõP3Ò@Þ| t’P ?A_-$qÝž¬³áñTÛ{„ªC•YµDˆ¨8tX½yˆàllPpšÒtvY-´þ¹Èd¡×K•CWîoòÏ ¢)ÄA:Ùg–ì)Ÿ»›Ð§ 5¬‰â˜¢bxÕL<1¯9g–q¿cÿ÷½Q8Pš¡Ì©PJƒv_ß\Ý’ rXâMá¶x‡XÊ:#­¹’"ø¹‚F‡„1Îñá}‹«ø×[²å_(3RF. ¡JŽ <gÎådT O{ÞÜõ“Àad‰Ei„LAèC›´s‚-˱è~5‘aŠó,7ÈÅåƒ+ǰpƒ9µÞØ` $$¤vÙ‰öƨôñKQí|/¶«&âŠY¶~¼9œ’»‘*‡ç´ÑÊv-¨°a…ª[‹:UK‘‰¾EvY9µ&cPD).©&²uÂ$™Æ :'à9Ûü'ß0šó`li¨è~HFÛùì;uBÚò섾hçŠÈØà”è_âÙléî®áZ:h|Î;£Â»…²è×TDÿ:Ä2."pàT„–4cž/ëZÍ•í.D*oWvЀTÊ[T¹éA,¢IPd\ }/"ÔbmãØA ‰†c2¡ù‘S4z„μ 3p ð™ß5C <#õ¹$$2µÃD Q¬në-¨ÚVT²ª ®a¦o¦Š›$>ú¬ÊN·LkKèËkµõŽèÜb¹þñœŸ½sìt~¢Ÿr õ©±`Ã꟒WÓ¶. ‘ ¦"Á°Ô¸/êdàñêÆxB®ù8;Th6ZŒªãÐÿ$ÄB7;·’2Ie™ò„C7–y^—`™Ik7)â$”¡½­ÑŒùQËÝL˜¸ÙS}Ñ%«Í"q ÀM‰, ‹n•LºÜŠÏØssSñá°m¤.þÑüã!“¶-êR»cú’¶²@½k¢‹OÌtý¹zÇšЬ±OŽi¨æãê•;È)EoÙêíÜ€2kÔÆÛ³d]“z ÒŠSÈ Èî0’›¥…bÖ!˜0ÛªA× ½>XÃ¥ôš):Äl~+ƒæd4¢Dä6g©vøÃ8jéqÊs]|ÈÒ³JûwXÑ +,YŒ"æPj¼e½ý(tº™´Dv:¦è@œà¸4ÉÍ^ñýÍ«€ŒA-Äóà#nv;©M¼GêCKZYË6n2bÍvO9_åU†^˜èÁ|\`‹†þ À•“ ²K·“¨ÏwwèK¯‰åéG;#·EFè‰ÎH@àº!ÁK·Ž ÚÆˆº­–Ù‰³.$¬kŒ§Ü;–£·X*°70¹&©¨œ»‰×¯™¬gvÝKJ(¥¸…†€*¹8¢²UK ÍÅ5’ŠüOcLKoNö„¤«’†ÎªD¥p!ì›d0z((MŒ fÀ•–‹ jTĵÔôb”X=lL@BФ§ý…RH6]I5RÃ?pDVßF€xi øЧ—áèQ«TÔYÚÔ(Lj~~XóV0†‘‘bá\B.‚ɼa&Ö©m7FeüdÖQ¸S—Äõ7YÔµF;wìê¢zˆeÎŒ™ô:Ó>mMþO ôÂNŠàýcOQ?ß ˆ1Ný¿¦=ÿwu›Ìg#’ˆs³ÌTp¥¾»¨QŸ„®”â½3ŒJ(¥7Éã‘s»4ÉØ‘c/Üå> CüËg^¥c|!ÀéÇsIºœ¦/ø´¸µ®,à¯\JöPŒûÿPïÈÉ ¿=cFn„|è—ÿð°5#¹¬‹¹u*Ãg(Uè¼â2ß79Ô<ý,NÈEà~ÓZ×Xs6z-Ê%"‰æ…›Jä³/äàŠÙ"÷Ã’3¢{¾ãÆålE’Jïå„M‰âsÈ'—ƒ»NÖe‚~! ‡×a¯‹Z»1x@”ƒk`);Žl̤vy yM@+×gfÊ<û0ÖLZ†èc%xŠð}qãE“»ýpQÍH¼^a+!q£Ÿ\úí/]lž†©5•Ù€/½‹f»#¬€û/ T3ñ—{‰Ê¢€6hQÑܶYÔ°qt)U.­h*Ò„$YƒÂ·éÖôµ˜®öÒàÓzÀ %¥4Ðu„1]I±E\)ŸJæSCff2jt@þ³Y|õšÒ\Iô~U… æä"³œ™¿îVÞOe•-´çH¸ÈÈv®Å´ 4mE€"¾å€u¿‰"Ä ¹óI>• ¤:ZõÇ‘ÂÖOžÎ?÷ıÓò´î@»'QÜ•¥dk*iã[ÊÀp^|Çen-y=¢.åÄ«?'.‹^=tQðœmy2»„v„pã%t“ZE·Y,Öñ‰KtUíΜÉ:‘áp!"¹‹ƒ4؛Ί_Ëo¶Pï² "aÈš/.ö· Y# ž¬'e†VãÉxĶAG˜Éo #šúÃ}€îi·²ôxrÉõºû|2?¹øë*»¡‡s(Øû P›áþ;ý#æ;m8ð’Ëñ9ëmÓJ³ÆÎaZf6oúŒÁØË}«O§A1ß¿· è"±y¼Å£›K¹õÞ?"†Ëè$ºî «bƒh-ÂwÌá¶›évÒ+²^'Tð¿Å.i¶·ÐÙ¹`T‡8Zœ7­Ž”üÿM•5ª‰Á„_äÛC Ê ·;Œ†Ì;>tŒ{×êãÎ*ígì7W.Z㎙ÕÈÊœ¶ëÙJ™•(qKâ¿Y–àãÝ8Â):©Y—©Pb¦MwM4¾Þ«¤û ›;jK‚šcÒ-‰Ñn}‰΢>ÊÛ©²FˆX#Ñ.Ú¨DŽäi¯¬~©CÃba´3ªsŸ©©âÞC“–¢ùä­Ó_ÊyŠ è¯¾hNÌ.˜‰f¾hãHØã.çQVÍ•Kc²¤Ç¶“Úq0öœc |ßKÞŽh×®Ž,àØ\^e/1œ­™Q¶¿=’fOè¥L9}r ÕiÅ3IÂÅC’P£I\ób5´nbÊÛ3Žxsw)1:3¿Ðzs,@¶ùÅ#®ÿb®–Æë• «´‹U‘×Èœ0*Ø™Æ) ØÃÚ Ò¡ l"IÊm0ôÆqÛý½Drô›Õ%,ÓWbgé!„pèFBÝži†À *ý6¹æ"8ÇUÉ(? ácȯ·­d6ÙîX$0ç˜Xl@š×eé™K>‚¬ÆmÈ’”ãØ±JVʰ”HãPí`Fe½$¤zåÞ¤G£Ïêß „q³ „0Ñ[ö̆Œ=é·‘ïb¨²ßl³CöÆt‘+›d˜«‚•{Ì‚) ¼}xé$Ž×§³ ½&²¹…/¯iî6y¨ê5¸R§EŠFFÓ\³3cm=qú/aZRš¢¹õe% Ìýw?²ÊªV ¹(:JÝ‹™‹kèÆøÄ¢‚^2PZK%còêù&-«ÍÇF¦ì@K#Ɖ>%’°ŠMÕ9 N<ˆ‚V.-òl—ÃÔú^à%ŒXX¶‰½áh­Ñï¢jÌâ)<’bÝù} dyCVZ<éYô»hêæ¯ Î…#öУ±Ã†ÿÈ P'4z–ìÀjJÙÓƒOƒq®I€ÜÈû'߉$¦Od9¡@Õ¤RgFÊÛq† µÊ?—¯¹‘€»NOb£ý?ª­7UÐËý(ÜäœÕ' ¡3Se49n``„§•)o´ÜÌo&ÛJ{¤8´ “¦¶YÁ¢$ ôÂhØIbBó'Ú0æå¢Z“äxžƒ[B•¢”úžÃŒEõòÙ_OFÈ Àl+i5áœD²£­†ëº~ϵ±ÒO@JÍœU o¯ïzR§iÙ*Bž64gy"`<êSø”ÞÃg¡ùܼÉâŸiŠãfÙZ%÷Oç˜L'ëÁ*\LÒC¥P”\ä -sï, ®@•·'(  :ý Í€mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.woff0000775000175000017500000012506412651363712030643 0ustar travistraviswOFFª44äFFTMDepaÑGDEF` ´OS/2€>`‹zcmapÀ jé5°ògaspàglyfè–¯Ä2‡÷head™˜16\Ã"hhea™Ì$ ‚áhmtx™ìÂÙìloca›°õqµêmaxpž¨ ÝnamežÈf¸<ešpost 0 ù¾2Í¿webfª,ŒRQ¸Ì=¢ÏËT‚0ÍÞ<ÑxÚc`d``àb `b`d`dl’,` ºxÚc`f}È8•…¥‡Å˜¡ B33Dù8AAeQ1ƒƒÂW6†ÿ@>£2bDR¢ÀÀR xÚÍ‘½JQ…çn~Ô {ÇßÄ(Ëf@}‚°±ZRØX·Hò!•eð $¥6ADtV–â $Y,åLÔ¨¹n\lÄÂs†‡¯˜!¢ųF*rRÕ(©÷œTÛÑö©D)²É¥6P‡ÎéÊ͸î~Áó/ 6ᣂháœá}Üc,¶8².Eñ¥"5iHkHÆDÜ ïøƒG_xF%” Ž&ÑÆ).qƒW6¥$e ¤.Í ÏÜšªÙ3»fÇl…™ÁÝà¢?ìz]7Ï«œçgy™—x‘xžç˜9Á+&môX¿êý¬GúI?ê‡Ùƒø)•¦O¨²"³¾âüM¥³Ó©•ÊýØHþŠûâ2tnÿÿxÚ¼½ |TåÕ0~Ïs·™;ûÜY’Éd2ûd$d¶Y†°“ÙĈ¢.  ŠÛ(ˆâ.*¥¶jÔª¥«]ì×jñÚjW}mkWý÷‹mµý^­µ¶?[!sùÎóÜ™É$$¢}ÿï™{Ÿ}=ÏóœsžsÎå·ãx»„Næ¸lÈâ!Ç´Üv2¼] Û.qÇ8ú¸ª3¨ÿÌœô¨˜çêÑã’Áêð¸¤H(Oe’!ÄÓ©H†: =ÚR¼r¾xÜ7š§OÈon‰6zż·1*.ˆ`t‘‹§âøÇs䊖ˆ·Î`¨cubÖÑ‚‡ËJ­$ÕC’^‡8Þ›Êd!“ìðHܼ-ç¯;Ë<|M¿`Mq¼7àsf{c§<>ܱ´ÅínYz.¾¤ö/ÅÙÕü÷’ pB'GXòØ™ a×í\þ»N>¢qbwf¢AÁãtã0x„¼öv‡öÈp)/¤2QíÈ×^»S;~ô’KŽ‚^r5¬‰L²žX˧â°úê±—ÕŽßùÚ×´#1:;܉¼Ì‰ç㺹AŽ‹9$Y­¤GñX<ápyp¬3Ž.ÒÊãHn—×ã ³IGŸÍd{ ëÐ''í Óƒ•Æ´<˜Ì]ÔÐ~Q.ù öXPµˆ‹ ¢d6ËYÔ{¾ó²4+œmu¸Z³áYÒËßÉœ™ßØw,×·qcŸXèÛä¹hà¥}-í3f´·ì{)-rU‰Óè0(¢jùìÎÉ3|1§3æ›!>v¸åî¡ãš[ eèsLû–çü'à¶ ilaG€x{xœP:¦üý)gñ.%2ÐÕ¦ô\ɲhtÙ%×÷ŒhoïÎ;É:Cô¬sï˜ûÚ¿Zç¢ÑÜâ–½öÿ½Qü¬^öqîF¸°£*Gç-&â4«R0ÍÆÔL‡WqL|Ú}«ÁíRÝZ¯Ö‹ê&«µ{k;áý×Õ.õux¿“¿ÒãÓÔ̲Å]o~ûms½[²Â?`S;f\ ßojÒf.6Ò%B*u)ô!f©åcb¹S7CØÚº£GµuбöÀ•ð}Ö®¦©›E\ÐÔ×j7ôh¿ÒÖÿû¼RnfLJ´’¶!ǾK T• $Õ#Òñï ++ rv)·g‚bþà•£‡®<(»ƒ™›»}+oÜãÊ>c÷æ™ [Ö ¯kϽþ:ôì½âÖ[¯HoÞuîYs[Ò-ø×8÷¬swmæÿ¤Ç¿Îq&º¦dZ¯ knãz¹Ó¸³¸K¸ë¸;¹‡¸/sœ˜NÅ[ ,ÕƒË3¬OáG*Π¾´ `büÇLªú&.&ÈÇ}lg›â!pq_‘£Ÿ£ÜXŒX•SËW§:U™¸ ?` IÂ…”«DÁý“9‹> ÖèS ?>æä«“h÷Ÿ¢À§±ºE¶ˆ ðRõ|ÒÝzÜÕ„;E<Ï ¤4.50"ì9ææóSÅŽn£) Oò“*ÏèO¦ŠáØbeûÏɰÈ[oU7è­rLðÃÿ°b}„ëlÔ £Ï17ÉWûŠù©ã>zÊj7™“>à×gqÌÉOzÊU…!M:ÿ¿ÏÂGUcFYaǹ©ãªÝÿæX <»®ä¬ÒMÂW9úðÌ¥p@<Õ xJñÑÒMþâ´›ýËü7k‡ü~ê€8¹‹úù÷–±(ÿͰ•úý~í7änôb¹ŸxGt ¹ÇE]6 #в㩬q|ù—lÑÉJÖ~«ýV/ âè*ÕñRé¿ÅÐõWJÑñ}¿ˆàÙ8OŸáýA§%¢ÏÍlÜ–ñÑá©<ˆ „yqSa^§ZFT ¢-#ˆ‚Œ9Çácýé©ð12rRNê|³ I{jkÿHZuŸlœ—k? j?Zû‹Z+É}¼V³ö~ä––Î}‰.·&.M1#" AlM:åÌf<^$[±õ Àƒ/Ñ ˆ?z=Nºgë;4ų÷¼¤ýQûöÇ—ö<|°å‚† ­yÓ¶å·}ùè-Ë·mj¶¶6|¸˜Ø2€$ÿišrÏKàÿô7 ï¢ µ¥ù‚à’_^¹“c®-WþrIð‚ækð"íY²¤È6hÂ6hü'Vpı}‹UÀE’˜C÷ÓöMæ‡Sù9§µ`u²ä?ž{¨ìp^ª1'äðù^‘¹ uÃ¥£ôeåi¼wŒyDê›Fc\ŒþT<,¹<‚p}Ê8#.œ‘®QIÆÿ´Õ¸\2¤x‚¢ŽˆßcP+ÐÁÀœ-‡&qgÿg=ÄíÍ"j”E«­ cPáîÐ+‡½BÙÍßR]‘Eбî.ÙzË´6»E®ÿÕ þM·+6«éÚ„l°-rÖYÿ—Ån7=m­mœ«}w{,–ñ‰ï0Ú,æë£,±Ï†‰‰‡Öp.úÙCü™XÇZ³O‰Ýa¼Ðk»¹Ãï°|ÓîÞf4]šQ,f“{}mÇŒ:â¶°´­­3—›ÍŠ%z§²­:±²;i°ê‰ÛýÄÍÎŽ.«ÃÈln.w¾Ž‡Tϲx ¿Šô¯+@éÖ€ŽnH’Ei„%R^ÓYFßâ²3ÄiesŠ˜ÂÍ[m<ŸãmÖâÚeEû®"ó—8­CûFŸb “^`[JO˜¥¶¶:ù`Y§pü×Gæ1aÉ.#!Æû0xt`å•»Vòßdµ?K¥bO8õõïÃ;Oä9•­Ö5ìx ÿJÁ“E@‹°“!AaR×J¥ýØíð⦂ø©–?8⪤—ôÂÿî1Xx‹¡8P0›-†QÈ×ÿÁ–Æ« !A-H[Šè¿ÕâdÎ#‘ɜ‗(u²ÆçûÉwiß´-ß |FÿŠt}pvñÜJ–Ú‚P<+a"6éE°Î œ6ÔØ©q$W(fíûæzzÐg‡,É7ù¡ÓßÈc kùz3Ì4+Ú–Ñ<ñ,oôk?ðéu³µé½{Úf«ï;%°áì­´‚=Á g'¸›âÖŽ;;›„¾#Œèà0|ð˜öëcƒÆcpéKÚCÚFí¡—^‚³á8›Ñ*pCa¡¨aªƒ¥ä¬ê¤/½Äæ±ñ¥¥¸=:Ú9Hó­@É™wK:㊠u“À`Jíȼ„ÔN7 ¦› KFwŽ6°¦\–§-.¨§ùf'y¿µ˜³8Á…Áڻ˜kõÁƨ Vbˆ CÇ$6L+]Q<ì#~ØÉ¤³ÝŽô¦jʰœÀçâLàRýªEß7-è<þvO™Ž(hãb\7b(¥½°üVÇÍ´×ÓÑKWxdŠÇ@=ëfxÁãGƒ–sfœZ®Ááh$Í€_\óã)=ýƒàÞws'þGS½–SU(Ô7% Àè¦\¬ÔpQ†¸*íª€ŠÛ‘Äí¢,=` Uð" )ÚwL5&­`3<¶pðï'˜9xð$¨!Cf³ö£rvÕÅàÆª ;IG¤= t&i«¾Ç雡Ζ ˜ LÝÖU-T › 9ÛÏ>´©÷º´a¶É 9­f3ôZÁ|HS ƒ ŠXèʇP+$ø‡WÈŠACVÕwc¯êáO@ððJ±ë|A×Y#Cqß1ܨjßQÒ>>çK+ïÔ’¯ðð®f'fRxD 0Îäï{ºIcm8\[üuwÕÙ¸:Ê!¡x8;ti…›)–½0,ŽØv{0j Á]ôä±%N­`4¨1’©NU+üøÃV=TÚ”¬ìE‰x/Ä#a+Aœ-ÙAÏûz°Ë’PA2“žýˆÚqR›ކ[îýaùÚýÊbÙn50‚á"íG_CÕîuÛ á"§å|ñÆDà–%oËY 1Þb¨UöÝMSB'ø_ÚsÉæëqUã3Qn![„ …£ˆ°ŒmÑHyà1ÛQANÊ ;źRÁsºÁÉ…XÛñ¬þä´-Ú;µ¿n»^MÑé•§Xøõ³÷ÿy©ÁÑ¢ÖÒþa(v¯ø}‹:îõ ¸¶Ý€Ù`D$Ú?´¯]|Þõª^D<¥è[tÃ¥Žs½*¯ÒìrË=À"ƒ±kjœ‚¢aJ~0Òô)üéÒô)üꮕzJ'¦z?Ê<<#3&wc¢cÌC9Áãx½0T)îŠÿðè$ÏÜ£´>a°š ÌÖ¸ óØèOÓÝÞ]}ªSZÏ_J zp9S”•Ï•‰î `;‚òfÐk­¿bÖlµ9Õ/ß³0:²Ÿ¶ÐFñwÇQëñ‡ ""¸Õ·«çÔ¬ cH#ìU‰À”ò]®oy¬ÃF^žWÿ¤ö+í«Ú¯ž¬Ÿ¾|áXÜÚžo¹ºö@  5²ŸÜòø½3B+·ÇÏàÂ.óY›îéÓŸÖŽÝ»é,s×ÂàRܶ24ãÞÇ?5/îÙó¢ög½_AžFçdû‹ØÅƒÆÃãY­j_ÓŽ±}X‚A\ªÂðqºÂaC(š9¨¯A /A!ÏÊš>yiœ>‘”™Í§Zyv¥à´ÈŸ¾@±ùâ5áp ýÅ}6eÁ$kÎÃ{ýb¬Î]ï®m×Z‹ïº˜XÇ@÷»oáœ-díYÌmû8mÂ#µÊîiÞfÄÛÄ8¤ðv’ y%ÅGîœþ¹Û,æºx¢ÓµdåÊ%®ÎDÜg±ÜŸÓ~nA0MÈ rkôª[n¹*ÚŠNùó> Yí%­8[Œû®z[æÑo>š±Õ»ý³¿¡¥´ëÖcLÔ+X„Zßz°CØ×ûjÑëb’õœ™ùæé=¤ wV•óâ‰Û€'~žst­º#i!ü£U(ò?t@)i·tÄq„ÜÉ4èIÇ|I/JvÐÏÑç ®˜òy­åÙ»ˆÿEüÑ ž£ÙF®ƒR>Kò §|f $,! ¦?ŽÝ –φ“û‘àZJ}™MùãɈ#©þ7~½ø/ÜØÐðþõô\ÛÐÐËþèíÅ¿kÙ߯ÞÞ£7Òd½½bþØõâuÿÖ΋~¦ß'¾Åöèú*E #B bŒøò@7Çþ­Âå1ÍH¥cÅt<=‚át>N~L4²_Ë¥cš+#?‰åÓ0œHÇ‹™D7½OÞVª+}ªÚD=É@Üi\$ùZym À/c4.ŸùíK±@fÂÊÈâi½Ù<§ Îs%¶yw.·!i+¥»p9gS¸vãÙ–qœ>':0JòʬK¥|²äeÇ<¢á (1w/dâc¤\•_:¿Ñ£ýE½lÎèæÁ;ý5 ðL$f·äfàEÂûyw³² DµM!VdpXTW(ᇸ…|°d™G{'ºðÌÑOÕ™LJÍü§ê3˜&“øñ¿f+²Ô nt‡Ñ±å¤!þUOĨ -Z¡âÔïs?!–ÃwéÀ¦Œ |ê`çµp›×J$Bjm{D[¨-Œ¶é~·WÌ-ácÿ wZ Aø¬¶6Dý¢ýÆò^ž—ô½ÈŒ4Ç5é› ãû„Ê ˜u”XÖ:†)Ï%°¤ø7£ôwŸQü Ó»!¯¢ªeDÇ]F,êŽÓ!ÃCþÃþÆÜi;€£{NgãpQ§=sÚE¦ØÌ0’Ðçí AÊœ8ìj<Áí(Éè4sˆkÂP1 Ä£KÈÀUaW•øÐv~ñ;G޼s„¡(Ó±<}Ž$ÕÍiÂ¥7«Éâùcüd~èMJÚ:ÊÒñø¼yÆÂ…3n>ž‡ŠÃo™ŽŸ‘[޳Ä#IBÙ‘É2\?«Âl@"Í;î¿Û%# Û½ÈfRm$†ˤ: 'ˆÉùÅç?ë¶RðO'Ò%¨IðñŒŽ9RÚÙJèuA<áHã&`Ó/÷èÖ„›T¯~±Gy=Ë3®ÿ¸A½ìW [ý²Ül£“%$êkë 6±Xå…7·„C ¡<óŒFì´\"ou]<¸èñµw·{¸gÝê2ò"bFA2Ä/n¹ÔnqúLR<Í,½rº®uÍŸ3´lYµG¼®NÄR Ø ßésgUßrzÍ`†ØBij‡µ¯kŸ¼§5Q++vÁ LqöƒUð>cÈ7»#…¿Ázvlð"Hu&cûy!ÕW瀦ÕÇIÌ÷ˆÿbWv–ö¦ýŒ/±…ÛW™ñ”sþHsàHØ“MeuHôèÀµÛ¦_°" ‡4nÆ[šxz¦˜ƒó¿Ô´qª9Xñ‰éµå9DBØ tüÅZ™ñõR׫‡ã#}j’¡·ˆcC§Ñb¢CïWª†þØß'pêátþî8Þ »ÅûïÜåË\Ü÷“J‘J4Ç™‹È˜¸qß1擸RK)à³0&ÔrÞ)œÿÚ­‹FòùR»™O,K1ùh·ãcúÇ·»z´«Çúßéÿ'm>µûc¶ùCx‚o©§ðO7ª¾§Z =ÅCÂøcÌ#"’}Œ›*f*÷H¥4¸l2çèß+NaÒÐɳ±;ÿ“ÆTçwÓû””.í ÿMÈ S«Q1!h´ZTý°J¬)ÇÙSÈMt¥áG¨ÈÍî§S÷¬º““ö±tÇ ó¨$ηC´ƒŒiÅ&ŠG>nÉK~Z‚.–‚.Íø1º¨ó3™ìr›?vv–ûT&âë7KæsS»èÈîbn÷‘#»Ia÷¸ÇYg¶$(3ªÙ!ªpÏãå˜#»ƒƒªè¨Ðm²ÎC¶r®•Ž$¥ƒ2H¾¥²ŠuŽǽØç$¿}xûöaaû±<ä† b °~Ht$î©–¶ì4áöbAËXRâà±0Kð8c™ …’l$Òo‰Û8 ©ÉZ¤ ¸P6!»“nH!Hã f‹$>¶Ïˆ*e7#ŠÛÖ¿µ>O.÷(rñ÷2>I@ÎÀðhAߊ=® =ͤãoÅ0Õ¶‚–PÖk·{³¡EÓýýO½¢a¿^¹û㣷þæÌ@88ó7·>j¼N_¯Ò¿°ŸÂØL®‡[„­Òg“‹ã\z² ŽmŠ0Ú¨EõÅ .MŠòlÊeœp&kÇ_±óðÎ!ÂÚ#Ž 6.?²{”A9ŸëÍØxÞ<ÃêôzFòbÆœ­q‚Å!mDذAÙà_æ?ì‡!,¦sˆ*åÿóY½”ÝGje»‹‘$]àdcßµ,ÅN^ÔFŠXño€à?–²¬2þì>¾…[7Q¦wFG‰¥U=£¨°×£ê÷¤Ý Ê’êa«žJö÷Èô&‡I!a—Å|¹‹Ü ‹êìT¤íåÞ9ý>»êþ‹–g«X;zÙîé¼× ØÅ3³9"»#³—^rËS[‡qË𩸓“ˆV,÷SµÔ‰¾°Pîå/UÅRc7á5-ûEsaïíI¯ ìðùCû:g¬Z¶rά„‡m0˜$Uîûu8×mLBÑ1Ù´22उ¥;™ªßû—¦ëneF /ŸSÅ"I®?þ¬¦¯F[QSs1¾A&7áëâ²Q{nüT*¤2•N¥ÂàÌ[_bj´0+-¤¤Ãs—(ÎçÜÒ9ÃXZ”MUT`,,J©”OYº93ÞWY€ÉBQê=¸ÜOpj qTD…˜“>1B¤•9UàØ…¦NúÀh ÑÀ‘û?^zu|mU|ªƒ”aü.*4A¦ËíÈdùŸ«>ŸZœeª¤òâeªÙw,ç3«ä£R\Wƹã^g2T馴L,ŠjX¢LVªÔvräÕwRͳ¦h&6ûгX[ö”øjµ“´%U©y¥^³A¤OU¥µ‰†IkÃÑ+žõõjñ:qÕÎ0"ÁJ»ÅÖè–c/{C!¯Øî%g—O,ø\tE¹q²‹¶Ò ?îP'ªq% ¦m0š¯ö‰¹1ª§šŠ—a·\ÏIµHã(¦qåŒå•ô±óVºæ-õPf½•äétèÈ tèp`èðáÐÑ‘Ã›é˜šÈ Ì#‡³¼ +å—áä¤ò½ŽñW³´ªÉj4ˆUj!†©kGÇãcDWŒÕ·&Æ·eb#*µW×;¾Æ ±ùÆ$%„“:ŽSõMÍTÍ­Ç:cöΦ…L/±¯ø¦.s÷À§>žéßLâcâÄÒ>TGgÜ”0y³¸3#·Å¶Åúc1ÍoÆÐ±-FnÕÌ£ù´ºøVtêeî:ñ”ÐezËrYmLëÊHŸ½z7„þ–»56‹Á›š/ˆ^ÅZH!•(îÂRéõ¼ oÐw<Žaã×½¯ä¨6Q$äÐU†ÜŽ®7” 9tå¡´OŠqÒBÚu6þ'Ø8€îa‚h¹‰âB¹RÌÉy ådÙ©*ù¥R»Ê­9¹ UºG“ÖZ¢}[&‘C,×ÓÂîjSm@ÞmT’ÇŒ¯OñÜÙÓ“ìè/{ŽoÃ5êSO©ê:µÎG¾:tž{'´ ü°ä¥xiʱñ2¹2ÚZDǽdmõÒVR9ɪö‰‚s=´¶å÷ø\ïtÂVZipŽN”ã ûX³vÁï±j§ šC*M·dBªåËfq sž¨§–jI[£ ¢Ð+”Sà9œí¢DN+2êÜ%¯ù{ÞæÝ'›íÆt(œjïolï½€E¶„‚áY µŸÐúáŠ0;ùÒÚC+~Qã,CøâEt!]|øB¶œ.|881.‹ûõ0ó>üVìN?!€pSå®À´©³—ÆËF˜Ô<§+ɺFP/.ˆ”~gP¢Rl¸r]™©äT|Êø8¨Ð·2Áÿâ‡IBÂ'ÏTñ×~¸8ôÉ²ÚÆ*,¼J‡È”B›÷hëèòþ­ªž‡ï{@ÅçyêžÞü9æQ!ÎR²,4ï±ØÎ9#çdºÉ©0ù2‘Éq"PaÃtœ’†\S´¾ä‡WÖ’¿ßô’Óbpϋڴh BV¸%tBý‹dïÃû‹¶3Öüñ·É{ëŽÞûôj/h¿g˜õÔEÏÃ܉4¶¡Gª¤«¤Ÿ­Œ¿–Ölì̆`ÍIs‹Çû©Ø= É­˜*އ ö*sH>Ÿî×~‹çå;˜©pÄ-ñø’øVLЯã%i±PªOçq13¨ðªô‰b”¡Xˆw%RÉ–±b.5gNŠ´W±þx*Çžä21v$cëOcíÇÚÙ©Ñqˆ¼Ô/æ¨Ö>”;VÁ~*Ç©B©K¢ZV¿eE±Ž`QäÖH6C ’þ¶Ð¶–ñ–§°Î|Yß¼<˜¥nUp°ÒØb¥´—i|âH1Ü">ʧ NǯñŽtLGt(¯‡b:¿Å± s§èÈîY ð^¢é¨ü¶UÄCÌUÒwo% ‚}u†’Ž2ݦQJjdÓ§>¹cSOD6»Y6ÛøëÒŽ µE8©3’_À™2§ïÞœ'EŒ6—ÃèÓ²þñïï‡{(&‚©¸qçi›Þ¯g K//?&×e…Cº½èÌ·¿+Ú×ïRuá]¬þ.ÀE)ï¥níëÔ­(0pWIbþâcé+BÀ4=&_ÌdiLïcRñ’Ì åÄíâßÅËôöMÕŽ©ÚÍäì&iÈí&¹IBî™´Ùû¢®YZ`­¬ QÊŠêê1}QªçBÕX˜†;ùàd¡,}©.‚uñ:-5_LË=ÎÄj…B©¤²RªNƒR:Šj À†_bLÏN”tU¡éxöâÙç+íe¨ö™$‹âZë|9ßù­Úû Òµ÷[ÏG]+(èÔ£@ÑRŠÒÞ‡71øbŒþ¤ö"SÝN~Ã/Æøûï/Ç@’iƒ¿X‰©>(­2I:Ë;þD^M%h⨪BÚô-»¼å“¯:-‹Ë…'q*Šõ5«¢8\ÖoYUq"rü¯ÏZU—åY‹K…óÈEfÉ`ÌÅ{›­|Ç…íÊq΃TóŠ%9Ò!·Ã]Âû’ìÙ剦òœìÐõÔªuÐtJ‹Y[a§sR7¿ÒáᇵBÔ_ðGµÎï^ëkÁ™#¿îllñ]ó\#<‰x”®¥cSßñìEÙ¯í«÷%”„+œ¨Iôí|<?çÈMî\s5Õ«·€tÖ¦í¹‹.l—är_½`•,rd03Kæ)Éôe?{hwÔi㉘’pxöíÔmÁˆ”j£ÚâÄ›Ût^Æèq÷&½S»/;ÁÍ8mhè´sXwÛuYÝ×Çë¾áŠ´¼ .ÝwæÊE‹Ö'‡òM«v^ÿ…Må7”BJ¸wÊ´‡˜ažxw}/.Év6:ÜI st‚nyÙ ¾½Rþðݺ W÷‡o„»á¸»ø”ßuÍ×üþ=«]ü…®Û´Dñ=-q›Ëuü†Xá7·‘ÜÛ»¶\ù-ª¢ü­+·ìzûÅ¿ÿÌlôí—ßïZ½GûټțÚ[ày#2/òx´ÿzƒéñËTÜÈÕrÝÜ\ît„ül+°¦:'¶3FÛYâ²b *õL[ê`Z¬”ׯ"-„T;cC xRG³ñDmÒ²tÍ&ìËäÀX/à&¸@Û°u†â4ï±O»óok]®O `9c}Fqоh ÄÛcÞ5(¸ i»~·ä¸àÊËžè=ëË3x{oaí§¦‘‹ÇºùW™|»h>z†}Û?ç×Þ»ãl»YuªDÑÚo}£ÞŸ¾oA8·ü Ïîs¾óí¯]¶=÷Õ³ô¹³ãþô.ƒ§…¨Ø)÷$ç°‰iô¨l·˜Ï6BxðìvSÞ">4<£'Ù ý®âdÙë²ÔõiÜeç-IŽÇ&¼¡Ú_aÞ”öãªtÙ q± Z*%r/Te»À“‡ 6CÓ ÚðD7aî<}ò ÑÝÚð˜ê¦©„+-8 ùÔ1¦1ŸßØ—ëÛú Côzƒ9–-—ƒà(–ý¡$A&eKŒ~‘%¡ UÁƒÇ™ŸCôšaH”è\ÏâR1YnÕ!”[…*q…ò]v7 QÓ*%2Ù€ éª à¬D†ð(À%l­–tÀƒ “í‘*©Éý]ƒž@2Ù?m„©Ó%£V ÷ÚÁ­ëR}©Yu³KI¨ÖuY½&9Áµ/íj® ¶Ö7Íí^sæóô2&–s žšž]ÔTÏX £V?-×/[½áÖîÄ™_gñTïQû¿»œ ÐÕÛÚsQߺ+–­N†Xæq!zò±{Ü)jŠ ®(IÄ=,žHÇ3qzŠYjŽ¡¨âžÌ½«ûý/hÇfÌqÔ ¼ 1¹ÝÝT0=ðÔïÂÀ7þŸæ[µÏh¿ù¼áËs­âq‚`l¼•ÒÞÎÖEg€t膿|aóçÇÓüI¦9ìv1¬¨|’áþà;zøÊÉvJ®þóÚÃÚ"íáçuM‘¶®­Í­+ºÚt/5x¤é–ßJÆÆ|¤ÿ¡öìSOAßucj î%„(Ÿø¼±¤ÕÙÊ|bn—Ô/ô³ígœ@ÚU(;Ÿæ+u:u »¶R&.¼¡Õ!-G),–c妱\»k9‰Ž3V“o´è1òíMxS§ÜøœNÕ‘[Ë”›Î£NTîUå<žÙ1ªå¥ÛÝjå» ÆiÞêvBL |A—Öoèô.2Ã"¼Iz‡'rTÏ`’‚´… rj— 9[ÜW 9»qX&y›VpÍriV,Ð0ªóWÎk^P$‡ä‚aF”ËyGË;j¨š©`‚C5-ïõ ‚¼¹`4eцªøIyQ×ážMmÌèRBéMµe1@Ü.¹b1Bz–è¦;‚—‰c0í þÇìõã`íñ·AkøÃÌŽ ’žùÊOtÖ·½Öfd¾â‹§™î†þÇ4®&Æï‘ëk{;§÷Y“ó>‹N‹³Á“ãUúv®“ï•êï#¿oÃñ܆}û6>Éð†}üp‘ùù}÷Íé&÷ݬC~N—Õí©´ÕÔ95¾|yMƒöd˧úŽÂéX†.!NkGF _êÖ¾,B©â þ5D´É…¾@CnÃ7Ì>k‘¶SBUc(o‡#‰ÉÜp ˆ&^ñŽ]è’.º ×·UׯÂoStýVÝÎ \å2VßË 's|A¯«|§<ñyü}ñ¤ŽÝO¸ý-Ýö–l|ÈÕzb :-rŒÿ}¸ž,ÁÝ—äÇ~T lLÔgœ]5ªêyÂQ ŽL× ò£H¿té)†«¿X-ð3ŽÒÎ娩˜AüVGn½ˆÆÒW<ˆ®—n,ˆ¯Òåÿ!%”Èœ´ÙrsæÕΙ߽aÍUâõ¿?­~}[úÜÅõ‹Ï½mÞλ}5÷~iû÷nÛ<iîæ#»G™Ü_Ø}„°ÖØ8·ô]µ¦^•wžÝÑyi7Ô’þ]VƒÐ»ÖñîþÔ‘UNãt c¹ŽŒk¿Ê¸ÙÛuÒŒmq'ÙOhd÷…†Î‹¿|xßÞ½ Á½Õ ![_½hFâÕ»nßûjñFr¼_]›\eÛ‡âkTS®•ë)QUÔ@¦,ÍJ‡8{<(Ù=AêæCnrµ)Ý€Ò¨TˆÄÙÙh}É4¢¶L…|ÉNáèëT³»9ú½\ñj)ߟ>Æ¥ûûÓ>ÉWýÎ}Ghì40±§ÑçòÐ=¯ÓÌÉk…ËoÉç³ "}²1[$ÝÃèß…%ù*:ÍŒŒgÊJÿ2åâ„ì-1ætI¿”3Z²t™-«…>·}x»ÚÔ¼l{éÍw“Ø·ðC¯ù—67ú‹g?yôÑŸ…ŽáG_Ü ç ñ­áà&‡E‘–­:c&ÿäðöíËš›Ôí¥·Æ96ñÁÌÍKý䡽/>: ϾøèÑ'µ†ø<9›ipź¾òZ³áZ{gÈórw”;^%7¦÷{樸ªì¹?Ä`ÑÇ7WTe¬˜ÌQI?žÀM†Öòǩ¸?beQUm/î9X¾^¶ÊñßÉÌçñ‡¶ àÙ¢?µ¼Õø)“;Ü)ËÞݪI¹,Öh2ËÞ§MNð†›.—-&å.Yé±{͇k%©ç š4ÜRÔ`¦IÍ]6¯ “’ü}fgRØC V—Ëe0=BÒi¾ï>‹#)=¥ˆd“$\!$–û>nú’I¦ ¹GÒ%‡v×·j"MísÅ,vËëTó…m56哊û ÙpcQ±.õL‹×€ÃTIj2š ËåuNë…­ã’Ú<ía/qGn³ÛêjwÔ üÂnBÜòzëlvŒ¨÷Ò ž‰Q ›Èç­·ñïþ;¹*{0ñcŒe—–ÍÌád§ÃîëÞ¯à”íHÒ˜‡ ÒE¨ÄµKÍ}[{æ?V¯»úþhoR Dä%£öz·rõ߆ùp-Ì']w\­¸ëíQ$ªw‰É\æŽèýW¯[­ý퇳CãÎkö{¯?Äߪý×ÛìkHÑò²$ 2OÅBܱƚE?Û}ëÛ\ñÓE51w\Œ$Iæ­vkíû„u«6¼»°á/+ø<Óìâ.³šô Leè½…ÂBT{JIWìWàCYo¸"]le°ŸÔBè í§tQRŠŒÚáÃTôZ7­#<¾´CÊ ùjbMž¬¯mJ؃AK¬¾ÍÛ.þ|ï•1q¦]¶`K~†1ŽXînž9ôÌU;=ÚÝ?ÁÝ<½¥±?Mòéþš½ýéÚ+÷5ÇæÜ²ëÌsse;}º¬j7µ]µ£%Ø\SEªŒÎˆ‘­¢>`"U¾#^ºÇÓT'—"Uå]ŽI¥RSa•Ó†î`²»2¢•áj Ú\ig$ nZ“ßûs±ÝÛV³ƒöDStZm\Èzšb5>OêXš?¼åÉöHdÿªd"ÜhªQÛgoŽjï°1 zv柽`ûm_„.>nœ!躢ÙŽîÚùÁ%‹íßé+‡Á&Kż3I;ÄyÞ–/÷mÜ4ûdz;Î?|Ι—Ï›?'Ú´rµ»cpo>jÓ¦=x@ÜìqÏŒ†–èv”ù£óÛ:Éj1Ÿ›h•X9öüÉf‡«×%ÕáIo>[yFýÓQ [y<2‘¼Ê–pÕ ¶ ¥|ËÜ¡ [®Ø¼¨ÆÙã¬Y´ùŠ-†æ¶ÒHq¹˜Ëc%Õx†»PÂ6g‘_¹…«$ÓoÝHž€h³hL”ZÇ*y)îÔ¢¸CR~X-ŽP!Gf/ ô*.8$Ý>‹.ô®Z˜gc_‘â§‚l")šKv‚ˆ0Gʺ̺=jO+ÍqÙ¤#‚»;±õ›Š,‡åd:âáÑÍa÷Dìî/úÓû°`çâ…3aö"²øO¯¸y1ùÏÿI¶uMÛ ¯Tc{{È7~™š7/•œ?ô ¸ãþwmî+ÞûâÎÈŒÈeÕ¸ã§3»/&*¯:*á`¸mo¥æÌ<£¢2IG‰>¤D£w Äîð ­ª­L‡êÄíB_üs*æ#Sç<@†‰‹0ÛjÚ—˜­7;êà¸ïªç §Û§Ô¸ç+4ÝG“Ù0®×mW+T,INv—8&îKXйŠl33ñF…ZŸ,îÔ¯ÉmOªº$Ò e!_–°,àËŒJò+¨À$ËÇni>zÅ9f'ËKát 4Vú”Š#–SÒ„ˆ„š E<õ 0ÂeGvSe|³Àz¢”`¶†˜þý“ƒ"$‹¹*È%r :XWxT®ÞÂ-¦wi¤ =±tÈ%ãÉäv駰ˤò<è÷.lK¦6QJ̪te ?Zr‚;*|÷·äö£ù•·¿¸£9¯ïžÛ¿ËiÅ)ÙÕ?·»>žnÞñâí+;!ˆ-£ìÓ`c'¹ý‘Ÿ-ýìûC?}¤þ³/åÞµó41ÓLf–¬Ÿ¯[É™¿~I&9nʈ§í¼ka¾±Sç‹vêú`}+çá¸ö¦sIîvÜ?¤DœÊÇ'<²„ŽDÉë­zÓ—+"¥SIj¯ ƒKR ­”=—è蕈#¦Ë­8.^\ËÞKÇ'LmôáêE–ñÇ÷` iÌPÔ-³Åh0›;ŒFƒÓhL‹…çÅ/)F{žö.»Ãî˜E‚‚Ýοpd÷ˆÝáQR3ן=§é´ØtÿÖDüÌδ§/­Ÿ;­)wöú™Fw{߯:ÛårÛ%3â¹-ŠbéY4—ãðxFÊ ÿ›F³É€¿´Y–|¢Ü&‹¢,òb“¬˜DÉhÚe– ÚMÄb"¼b¨á ÿºmƒÛñ׳§K5™Óöž~åéëwkj|>SpºqÇz ¸aY¦FŠ"ÖÚÒlx£Õ*ŠJ§×o³€ Äoà=^¾Bä–í<ŸŠ­÷·5ÈŒ‚f²tøô»y=ŠZ_+±ë¦²6øy÷9ÝÀuŸã†Ï3ƒƒÍTp“Jmžà¨!6(83N(4ħ08عhQg'j,/ÇFÄF ªªå•óT<°6ƒ;—ž§Ty9*Y '5(¢«‡¸™8]Bô²˜ÝÊ:³.¢%ÒÓ²Œú5¬.m¨§Ål Æ ¾Îj2+EŒêRWןg·œ?·óÀœ¡ëfÔzj<5g×Î|}æSç_ÿ‹ÝùÛF?uõfþ¾ÃoöÔFçW/½ÿ¹=]𥏖/Qˆ ‰ÝI^˜vk]À?Ýç]ï‰9ÁØî­ñdf,þ?½¾q¸É»fZ½§!:ýWàºõ1í™ãÙiõõ—,®Yëm|¸é’_¼ô9³»—¶+›Wy×y‡CñHŒ—¥ :ƒÌb¥»–ÆÑ½B([b¶p» ÜH:>Ô Þ¯›ú¢NB5=ļËäÞ¼qC]2װ̸i0¯ýõ´ö09ådgGíš:«ìŒ˜âA_o9w¦"»aà{HØZgtvvt¹¬õÍBíÌꉇƺ5µIÙi ð‘öÓÀ‘Üd\ÖKÖmظÙmrñ¦›Y+4×[]]Nc5L|oܲ‚e[ëy[0nŠ8åòyU±yËJÁMÓ3Ùø»1U”ÝGD®|ÆÐ¸á±ýŒÖm®HºýEd½ ~¨ñ•ü(OœqY²ìçð³*[š¢-6Åã¥é­3‚‚Ç}ÏL{´¶ÉÙ.}Fû~qÊQœ×šægŽç¿•å’V‰ú‡vÇ‚XAÐ)Rz®¿ºÁãewçÁ[²FùŒiÕܳ`àS¿Ô~öío¯GZ^ò‚ÇBþ–æí÷Ì[Ú·tÚ•°þÃÑý· ]4»àLa˦ùVÿ ZñÿuÑÝÂ-äš³E“÷+»„8?펕kûïûšî?zž{æe½JY¿€çÄw97…>B-ã8tósT쌸á@àÚç?÷¹/<ÿ‡;mn‰EûãèøNðþÏh´„#AVÎl^WÐ5ÊÉÔD¿{¼á©ÉQ.ZPpJœöêŸëjÿÝsÊW÷gíÕ²°&É×.kœ?ÿ÷Oóxg8wÿ©.Jijñg¡:Nù«Éñv{(FÃ1u܇ûÿ¡W }l1[f{‡/”u¨mžBgc•õÃoQÓ<êú^Q[²GÆM°”tÓP니_S=º™dÓÔžZÈ-ÐýAg[T)¯Z,êø.XîÆ¾1.8ŒXÔãœj!CÅa‹JͦåuY±ü-¥î* ]Î$¤Û3ã R BµÇ)Vb‹33{ôºÂ.ÙNTgýMµQâÑžy£6ävøÄaˆî¸ô&b!.§ÿN_ Ì_Õþ ]ó«ÚˆËéãA‚ÿýô3¿]‹W{Þïr‡j߀ù­½©Þé°ÜtéíµGê\®Hí¯`/ÔÕ±Ú;‘ذüò™§µPI”+Ýq5pM{à&Üsy'~c&T6à “Z­ìm}mm}ÐÆ^U+ï>ý€Pc}ÏZ#_ÑGÚþ¼cC–·f78ž·ÃÙ}z6ú÷.ŒY΂¿Àï-‡¥xm‰ÌÌÕvé¾¾tñ‘¶·îe´x+—bÐ@)+üq1+î-VÀãÏ ô¼Ìd+Vσhx':)Oy)í,m׬>!î’œ3Úâõ}¡Už®ÖñŠc«s¾/¦òÚåÚ-p%Ÿg|ÓÔ¬©·%Bs’³›³:êš½×w]¾jGfcµ5šHFù§µŸ5iï5Wø6T¾Ã„;O¸ÃW”&€Pœ¤íA‡‹ˆthÙg ègœôDÒm{º]2Yv'¿Ì>ûôàù‹‹»Eçg+æc؃®,-—xö¹Âg Ë: Ÿ-<÷dðôÙvûâó¡ý)xQ¬_Ó’O¥è"H= ¿é±ç\N•®5Õézî1í˜V’&ˆ“iâ5Œ–ñ"ÛØ“ŠÕËLîYffÕé³—±é“Ú<¢Ïf Š>½ýicvgdqè–€bnüvÚÜPßôt»Òd–\7ÝäonRÚŸnªo0§¿ÝhV·LHÕTÓMõMãÓü„lÄC³™šÆ²5ûÇݤ˜n½5`RÆ¥©|›Œ®é4·e"ï‘ öQ¹t@¹i¸ÇUóËü4©Ä|,iÕ–Oë‘âj‚~CªpÅÇ—v hàŒ…5}5–ÆE 󃋞}~ùÑÇúê>qÁ!ĸŽ7ýÌìË1¨xkÜuÖ2'bi ·õƯyÔ—W3]3Ó+[ævß:Í[¾¼vf1ŸËU3ÒsçÏÖÙfFÕá·ùù%Y÷Šž\dÿžy]‡¹ªñÉâ.qR€I‡Ž·èv¾ZvË…(ûjn{ñJôr‘»=0aèz/è·:Ξ éç?•‰ó$†â ƒ‘9]õ„É¢Fk ¨NÛ°ð ±b¡cé@ úuN¤pöšU/< [tª¥?­Ìþ̳×Ýñ$@7Ž\ð‰Ã[àrÏ£×Ä{Û–ÈRc­s×x¦ûó$_ÓÑây‰,ÏyÃŽÝs[V¦gº“© +²&xúŠ\.^Ý"ÖìùŸºM8ÏãžÙsø‚ówÍÛ³?’ëYáÎ.áqª±¿Â«e|m[By$¨é„V³;UÙ¡ß:˜Ù^ÝfûÏŽ2ÆØ–ªN¹V±Ddôï2†‚Ç˰§2 ÷ø¸©N ¡!O§½8ù´Ÿ?ëôÏvUÃäT€»ú#@hœ¹êñIq]”—MõÅØå;ã—ÐÿVA"’t0Â/í ¸£Wõ‘ŠÜ›$v?Âî%©zhÉP-“ªÒê¤ÔP.WdÓn'›ôÖ™csž/QÄ¥¹ÎÌ¡sYråMæš­ŸÙóI!qQ p Â`Ñ¡–ïfC[œ§ƒj@þpP-öxš¢aAL/’d*€²äj _ !ô$©L²¦UÕõô›‡Ö;ëa+:ÑqÞ§š™“iNÕizLªÂ\è8úẔ¬m\Š~ô‰ê$b£*º›¬‰žŽ,d«5ÿD‘•­7FSFø~©©°u=X&6ms†µ¾ÔßXC1µ6¥¾d©] ]Wô¦ôBE»•}”!:^oò¤žW*ëÄVÇZ뜢/ÅSµ« WEHSWèty5kœMªÏ9IÏlTNn×¹c³< p'>LÍ¿&5ô¬ wô”ì>zã”Ñ R<á’Sq)Q&l)•gÆäd¤ŠäL<‘dQ2Ò@n=!h”„»•P3sôL‘é¦î4½¶‹x˜ü=»žLÂÃRHÞ¸h aZd†Ȱ=5\'ydJcRB+ÎØ’TdEÖ ñf=Þ8½DGÚ,f¦($ë‘3 C¡íòdqK‘½ø–JÌ@–"ŒŸ’Íè¬;X‹PÃ-”ëBŸtg¢Ÿ Êêq” õd3i)‘j%”ÙËòÒQ’ÜaziÙÃÇo‹ÊNRZ¸X(x˜PAăíʦâYO–UŽ»mg ò•JcýV3Ñ‘ #nž¡Y±6öʤ؄d"4€Ž}Çù COdè$ðRD¶ò^Êp£Ô©ÇVº°%†ñá@»ðo¢& ?Ø$"Š Ù­ñ°ƒxy¾†'fHF+Q ˆÏ‹’A^ÂÕ7ñ6»"yY›‹7¤ð-ƒÅ/ð>^”e’(ð&U^IŒÖ†$I6ó„7‚Yæ#6Ñ"U´òF³QäÍ6ƒ»Œ¢ÁÀûµN®“D0)b•ˆEÁEÑÀËAE¨qˆ‚¼`å[Û%I´“°A´J2vH&‚Íj°KÏEðŠQ‚•ð°/ËØ:Â;,–¶Üi³xxàky ‚D|6Š•æâ«‹HvƒÑ#‰!³‹ë ŠÙ!ÚürT%¢I&¢OÄ„.ƒµÁ)ò„F" ®ïy Ž£DLfUzE–-*½|7 „6‡äÉ&‹D¬ákE{&*Äd @ÿÙdE«CpK²8ÜFYE£Y–Ä^&¼à!žwZ;o6òbó8޾t7¯òN d£'Š`’d:UÜ6Ñl4I"ÁÅ$ò6£U°œ;¢—Õ:"Øíp’¢ö<8@1ƒl$ƒJ<€`á»AŠàÐkxÑ$"x‹ŠBp\ ˆ’‚]Œ"ɨò’U”ƒ]0¸%"Ð1=¶ZÑ`´XŒ"Xm¼ä¥k3 6±ÇR¡JN¬Àˆ#äE¸«›Á fŽ™l”1PçUp b­`äA ²‡ÛæÃ&Á*‹v£ÀK’Yâ­8’ËîìØøΙ§‚ ÌÓy¾ÑÄd”Ĉ$ù¸™Ñ<ÄÕ\+ˆnÇÚd·ÝC¤:—bˆJ²ERº€} ª,N/9%A4Ô¾Þ#Âì 5¼‘ # ®`·˜±*o3ð< Ív%ä°Oíi ¼Q2YÀ!Ö9yGðåE«Òˆ.‡I6Þ©A4ªÝˆ5™x;1+ƒ,KGU4€I ì®4 Š$ŽÞý$փȂ™¶Ö€ÓL!Ç pYID(®•p嚈‘ìØ^é°48jmA®30-÷ ·t-£›ÜTбŒåKš±Tn4€`ÎD 8;ǾAá’E·Wÿ …ŽZ‘ÏWS‰ã­ñ89’øyÍÛöÖíºRάë¦ÙíÚ«ßï½Êhs”îþˆÉc2Yç#›>·%æîBg"…¦°éÈÈ6~ýBWý-N]Ö²O×ÙH½„Ò!(ÿNñ ׉~£¨¿–¸QúQ.jBÿ#Yfdæùño4WfsQ3oNå¡ô·ˆô÷U"Çx”¹bvŽ*þ²oDuˆW94•Y›;Á©šJ?þ$rÍÂošê5µèc†æ8ÕoÂ_ê›cv)ÙÌQ9Å6ö’ª1¹Ë6 "îûþÀÄëHB¿À ¥oªÐkkúQô¡XöÜÆ>ÒYv0Ô·QäòEN êì“aÚµaª’ïÛ¨ßXmg!Ç e¸Ç¾dd'P™Ñ”¡Ê9rùãEº +%ôñ Y¬ÈkÀ9wa§Ë ºK{@{à.:@¥Ýç`€ê3›é}Kç`&ö­‚/Î,rñ˜<_ 1!5ÈEsѬn–‚Ö-ªº"Çì›;¸ÜLn·’[ϸâ”@±ëÜ„,"B0ù׫KܸòW¬™ &ßÃn1/)¥ Ë9ÿŠíWKýWÌžÛ' ã?w­ô-Ûóþe}Jés×£º<~uIš”m_±ÿüG–‹}sg_Ñ/]­ „ÂåKáìæo¬þÖ¢uŠOc‹LOk(}!»øÐÒåW‰»n­y[ša+‹,ë‹Ý%mßåBÜ\î‚’Õ$…#Û3°’²–rX¶,NÃ{3%m }ŸI”tñKJ\”Ïâe.ñIÿËþÆæ4©rg£­ÖgnàCþ—êšý÷ø‹sü/ùõ÷øý/×5MLÅ_wú=+¯¸råK+×®]½çŠU/¯šà‡\#–ä̾Z[c§¬šÐÝÜèÿÏ:ßA?ù3:üuý LT×0>QñõwW\yú®¼âªÕk×bÉã½%›“”Ï\Çîÿ.¸´Îä臰ô+M9rþõ‡Žp»¼c'i¯<0{ÁÐÖCM×ò½Ž{è¾W3~Û+0íÉ;zmíï üéËpÍY˜ž{ˆZwgP—Õ%êKÒ)-ôA"íˆ8Üâ?;ço=žß:¿þ™+›ºŠûrÚÛÚ»äÚ»®üÚ3®»î ¾î, q혧­„/6ÄàNmGLßv $Ë(sK¹ ÜVî n?wÛ˜­‘íq 9·––:ÃÙ“L•É4†Ù·cØÕ.ö©PmiÒ)ƒ1£›¨—Ø×ŽøfºË¢>jõ ¡%ãæJ€[f&éÑ¥µò:ó .„ã~^ ä-v‡µ¸ôbƒ€8ñ¦•ûî¾yõ:“¼ižƒ+ç-{öXŒóVÜ·b“,6µœ~àî}+7ɘÒp1ùªÕa·ä"ï?¾¡µcù†s—$ôWëòŽÖÄ’s7è/°…¬§ùx«ˆxÒ¯†Èî˜ÃÔ¼½UðñCù⿾BLD?$}Ú¥®hÄžC”ooŸ3ÚoO¯Zºêª;Ò«,ÆÅ‹–†Ué;f_˜8mUêŽÁö ôÁ^ƒœ³G¢®[š÷%gGé£8;¹¯9Êdx–)ê2´ùx;¢Eð‚$—Ó–ï2A° >­ƒÃ·ð‚~ \˜‹qIúE‰q÷.¥²¬%âvd’2„ŒRé!Rú4g*SñHÃå âýÐOAPýþy]yíÐRdÏïA—Æ,®‘ÿµî*ªý,}s3cÚ7¡ý‚|^û…ö袺<ôkÀ5þKÈë>Æ×N쯯fV™]e­(Ý‚FI ½¤å¥/ŒùÝÒ‹W?¸ë¦sGÿ¹ãµ‡¼Œœ©tÛ-Jñ‘ÓÎÛzp€7ô®È­ê->ã ×Çká~¥ÇnV´óz/]±¶›Ì?÷»<—7\öé‡~·£øˆb¶w+ä¬ÁC[/ýgïªÜŠ^2¿&^¬ÓÎøîï^»âR,lÓ8y8ª+=_ÿ¶“cßÓ¯w$Ë,¯‰º â$ßú! ŽÏç]Šö'¥Ý¦ßÀåq¸yn-_¥¡‹aÇØíφ¿ÆßÈ>J”·ÎP Vq•ìsºÅÂUÝæ¨£CU_'ý³èÔvñª¯©…¡ñ×Ìú}»Ç*iÒ|\KÛãíîM宲¦ýædNvÆdjA¿OKpYŠÑ”5Çœå[ µsS„Ÿlå™Ýêâ»Õ7kÇò“V»_fÙà.ÝàïpŰ1ÿ‡‰!ð÷*«ÇtXmLÇóŸ\WüîÚrWãvÀVAV_r¢‡dÓa)Â>`…ç‘ê1¦«~g’èa—Á”™›LŸl<”N¦(¶)ɉlÒqÊA¸æ¢å[ûfΘYßrÏ0#ªÚçØ·ÂàYÉ.¢’ÚúúÚêk[#§×œ5kñ¹óV̇ëÄÿÒÇÁiÕJûÊ6 †æ…7o߮ީ­UË6ô­^ïÏ:•¹MN éÃk/3/!¹‡¢ÎäªTË4omݬÙÉ™+v¬lÍÖvißÑÇÌêTùËÏ9§é‘F³#6pv¡vM%b¸òUúBiú}˜ Ó•I2º‘Vª½aª|¶Êåªú‚ Ã~˜Å›ÓY]êÇ[²¹F%©$¦0ü“üƒïú½íûo¡cWß%ŠÉ*šWY;Òk÷\:on_ß/æo™{>%7yÛc‹–-^vÕ¥Ëo›i3Pºñ<[À&F¦·ôÌ^œëœÞ¶d6@Ðòz²cŸ›¡é=ªgläôO^à†“ˆO´/Ø3𖺮Ծðê; ]¹.Õ`•¦Gº×ž±eMgK»#êpË6¤¹Õp˹V²êÅ+ÖŸžX,ÙxƒUrÛ|ñ%ýÛ.:øÄ®Ý]Ý»£V\í´Ž}>] ²™Gßš3k­—[ÒÚŸ¯^:;Ôæw†¢þÎY‹?}Ú¦{VÏžëŽáW+¼…Ä-rL’Í'7šTí¦ï^4Ð:gÖÌ`¨µ­`÷²`ðÛµÑc7”çÆÉqJEfc¢ÿ;¹‡tË Õ}wLðÃÿ°b}'ë<–íè1)â*÷ø›:¬vSr—ɈT´¬b[î¨8µ1'o,ô” ª ƒ¥Õ_¥ûp݉O•ìB¨L¿°…ZÚ@¢e+ %û¢1/Ý)z¦x Cl›ö*iv;æÌ8_p:E‰¾ýxÓ¦@põ÷¾×Õ…?þw¥âƒ%ÿ Ëû‹ Í‹Y34¯ó…»Yd`“6Êòu}¯¸±B%ã=ä*ø¿«á«nÙ©mfJâUÔX.«è"쉘Nø!"ÓÃSÃLÌL…nòž‰¿k?üƒãw¢.Õ2'xJ”RJ6Ç'Ì!«j@£jyAí­Žû¤dtùy‚È"淿ļBa”³y­F€ KÐo´zmÔ¶¨!ãi« c!¾‚®È?†Ã,©ØC %í©J3ÄS}Yý¢ôõK¥¤ßÎK—tE½¼ä¥vèíŽDoâ~zɶ٪qºkk÷•ÏnÙýû;.øÆuë[–  ÄL$Gò§G>qäÀ¶î%VCÌ›éèY]{ŽCxI+[ñ\Îø´Á3†¿’˜yàÝC—þàÚYC×쟻åÁ 9(·K^W÷ºOüæ3{?÷ΚîÈ®3þosïGq÷ïÌ–ë{w»×Oº^TO–Nw§®Sqor·±±…ml¹€;¶16‡m06Í0¦Ñ«LIœä’@BB¨§zæt™Q³œ±áÕnzàÀÚ6\9K¬¾}žm¹Íø ¾Ä% Iðâ…¾ÇÃM ü_sïØ9¹¹×åÝ«n÷°j¾Æh;}ëîËühA›oÛw}צ9“ê¥å+o•¥Gª8¶ÄŠÖ5Qj5–m—‡êX]*À)ˆ­•\”•ÙX9RO2 aBžûáh{Få•e„ÅÓ‹„px¿ZÚ¿ì§íœ #Wo¬F»Z%ýáÛW?ì ™?Zp^tIrœuõ¹Ýk;<ôm—jÂ5=Ó{j"Ý]‘X=gPiؤ©‡Þšv÷\úÓú¦RÍ!ï¦Åµ]>2>­Ïè³¼a0E1|÷"¶ÝÞޝÜÙ8g` ®ÿÐì=W¸2žíMžÛ½iòø†VW Ù:aë‘ë#œšåÕÉéËö<öÐö~ì!DrÎñQÈóEU‹æà hŽhŒ(±‚ß6&êî€tcÖe»Çi¯ÂÃëÈuϸ^Òj˜Þ@öÉκh'âHúÃMÃMD¯W©íÆ€Xk¬çŒÞøÀƹñ‰[ö$[×-Mìð¶¯ë>w•u\rIô¼ûÀ‰&ΈtwG¼±d2æ½ú˜ô?­;w-m zRwß8-ÉpjXáºé­]µ”YŸk¥‰æ°7`GÍ£õ14-më]×î²Ç–Òw´Ø3ûPÝÀÀœÆ°sfíÝ §l™ÐÚp4DÚ'®h¼xŸ©nü=íS’çÖ[áœn­ÕàT²Ïßxõ;nS.Üjyý*ÆÐ¢«!«dB窰ÆqÐR‰Á¾D­Ð€Ö[FytÃa@foã¨Ùs¼¦6ß8½J‡u®UÓ÷Ý3½JÞÀªµGO¥ð˜Ä¤Ž~rü‹hcô8ÕÒû‚&ièý«_2cÆ%‡åT)|ƒD~édÛ(”Å?`ÐÚÒå¢nÆ*C‰R#Š&EBìDŸCöEs¸ähIMÑIù^™ÿ\&À )Ã!qÒ€ø‘ˆ²ã¿|o’JAŒ!¢Ë³ gá°Ö£ð &Yœ˜ã˜NÁ$STfìÐ `’”Y~çÉ>”}îÈXrŠŠz‰ŸhsdŽ–;a&%²dR°_¦ÏËLêÔ Nô0ý§R% XvM32>ž³´U…’vÙÆ¡’fû}QKŒÑ†ä9èqg}w#2ÊÞKSÙ2žåÝÑ¥Ï-Ä&;0BraÕn±2Vl…Ä/ Çðf”‚gª! ^ w–ÒJ6"Ô4ƒµš'^ zÑÄæ‡Á¤æêå“¥+—¨»ªÛãV$ºÄÛ«»Ô‹¥‡}ΙΦ»–Ñ-ÃïOG}èŸ+ªêêëëªvý1 æÏ<•N%uåA”×)’Ûªnèœ1°”¼ó'ÐX¿žÄVg7,²Û2ö­$Öâ‹…ñÎCðšý$dœ+= –Õsáì•«ï_É\'=3k~ç<³Fz-‰Àdhªš¸ºó‘×è놽ôŸ@ÃäåË'O9ï¼áw2/BaÍöñQW4ópølܸÞqMî?äh$òí„CJ Š5’xh•GVE‰ ƒ2Žºøé½Û•~u(ªõÅä7·üðЬY‡~8°ü© ‹¬{×ñ†ÛAÙ+t™ô¢ôÞ+_¿_mWRAõ²”ü5t×ÄîCEVËÎßpñ+¨Œ§ÍÜߨßb\,ï`]ìêâpð0›=×ÁÐjÖšíBúlV¯Æ3$|… c|Þ^íß‚ëñM¡êÓÔ¾‚‡fÆÀ(é2Ú©q]E™4P¦RY4.ÚRŒj#g‚<–Œ•#é@Uc Þº`,¸>ØjX гxhâP"ƒ:„nÐXT*¢EÔ¡¬4N”©en†è1èY£“¢R‘tÏiªÕ%Låñ@dßjÌh‹­>S qÚyAWŒGŽˆ'¾0˜_9P’"ç- Ù&fl”Á” c ¼ùò|ÎÙQœß¶Ô`ê»õÉP—“+|fÓ]uèÿú ¿x FÜëÁ´Ï®2ÒÌRFß ö8jy§CÚËNo›~¨¢ozÛf^NñÙl“Ó¥¥S,/pOãL®ýLz*7.ȸ`<ÿQHˆEr!†ÍWÄeÄü@Üb €aÊ`$b²ž& Hw¼~ÝÞN[䨮êæñí¿+^Ì*Âcõ¶Q@bŸÛÁàv&uÕG7¾4¥¡ñ¬Îõ!NyÕG@øèp1³q l±Ç@ø‘G ú4&¢±meq-òuh á·ð ˆ à›±hL/’^‘þyÇÚþóü¾²šØŒ©·õwd¾ƒ1Nž©mùV ×2©ï­š}¬©i®It«ùïýú{üè,° §¾:;bñœ¦èKÐæ•mÔ²q&!²²á&€F :ˆ>yz› É¼§+gÔF#ó‚´–Q :ý%c3€I¢ƒ}\­dDúW&Û©]vÈ–èŠóFo£›yÁnTª¥úå°˜§dÞH]1ZzÍÑRÂè1Ï£¶$ÑG5G³H‡ ª©B]ì—(¢­óhZ#”I€cE?Äè‚MÅaét.õGÓÒ¹{ñÙô´ÆtSA6I£•þLjQV.ʹÿcc¼AÖÀâ%%—3“aO;PrH&3ç NáY›Ë€9äSìèԱ׎…C3VÎðvÐ^Q§ÑÖ/léÝQ­03£ aÌŠêíWn'‡‚‘îèmYX¯ÕèDPKó~t5Ð Ý窪¦ »E?Ÿ99pìØafÌh€½šNTG"SÚÔÎhäê¶)Åû‘ˆZÔ±ð`¼²ï†?‚ðå.ÇB)“·9))’ hmæ•íLÞQŠ$o>&¼}$ ±lÐH²Å6 )…™$3DÓ Ó¨¨–Þ‚TÁÕTÁ =³Ï)læƒÀ“dzÍ\€ÒÏËw>(›0°ÙI§GóA^®$¼5ª‚ZBì¶$L]¶«â`L4¥ãø2±\Z£q¢ H˜°÷_g¯šI ys¹?ìk#K‡D‡¸¾—«aÅÔÁñ:°a|¯ºRÒ½§K¡mojumkSg·×ê:#¦¾¥}¦H§®Ön¯cÚZkW/ºþ™=sý"šh¥# (7Ï´ÆI—̬­yɤógjj4·\ý-h3óüÛ6ÕOÛÒP:ÁÆr«-ÒPÓØXÓ±YËñ¹xYÖiõ›n[ñȦ®®Mñ_ÆÇux¢Â/ØÍdÎKâJb(ÂÎ ßeˆ5Ý׃¢N«•~¢R$¡µìÇÄ óëA‚DÜ/#]‚~T ôOÒavÈ$F±¡7hIÔîyØÊn!áQŠ‘Xãê‚u,gçÃd†ì7ØÜYJà‡HÆC˜8³g.SÜ%þê‹°%þv@·N\¶öhåÞ`?/€~b$lƒ¨ZË´oûüÞ·.Ý› æ±ÃG×Nëp¿>ºŒaâÔ-ã]ä}”£Y„‰3–?µÂ]ê¢Â~Cy\”^«å‰´1è¥ÏPÈ<ñ j!Õ_°v±y?:¾Rz ƒàHOOuüõæÑÂd8q 7bÿT“wz‘ÍàLHviÑ[z--6nœØlß&ì·¯=êiêkòL˜F¶ã[`ÔÊÞ– FJg]\~OÌû»w:´³wÏÑ-‹õ½/™V¶÷mܨ׾ÒôR‡{`ÀÝ‘<ºvQyþ¸«ÊaüÂQïvŸºËÝX%êo9º‡þmÖÙ%Ã.·Åô‚¤—@Ë£‰ñ0©J–•˜ÄÈÞ¥'.Ç)}™üöpH²Y¾B$‰†|HÇÄfâúÞ·CNmlóã¯û$Pžt{ñ¾¿Í¨æ¡·ïŧš'¢Ö¡e‡ŒdÇr³´íÎwß½s¿é·‡ D‡+€¤8Aºh6è à‚˜ËìðoMûÉÉ«ÌË;PÓdyIe›3^Íe¿1¶áŽÖNѼ›Xç=šó“ Ò$38L¥d÷0Hí_’D'™ Û¿„Fû§¼%{… §—ìg©ý²,™—«-‰–ûÖ1rtò¬¡qg ‡“åÁdVž÷‘·K*¼rGG´º„–KmìK&ûþõ™‚:ºöµö¨"ùîÉýK0ê&V¼ÜIÜ(¥2iôhF…ú‘·ÂìaŒöZª[–ù[¹‘nb‘ñ[Fî³ù”þb^؉̈́ yb1DŽê¡Èy44œÜ]Ùt&…>…á/pǧ5èã€2lm?qN,Ýÿáò€hĦ=ûO†ä]6Ç¥WAÕe±yGrc¥†ÝüÌîÅ,®a0†&RÊ»L‰&·?Œæ\2¿cÌj­ô m9žÕOÉÄ)ì£ÐTVúó¦°Ïó{™#4…§y)U®ÍZµ40œ*òOx‘Lì¦"ÌüÑv³Ç©S¯R¢>E‘¸A-hÍ™+9fKŽƒcpdÓõàÿÏî?[úÒúâ7nÌy–ŽÂjÂh¦y±«€NöOíÓg8úÿbzx†ó#Ë R§R¸n8‹*fžÊ×ô£+^t.ó1Nþãÿ`BéßX²¯oÀ€¤C²@VäúŒ5ŠßðÍâ(ä â³(QÎ\˜ck-öµL˶(L»‘³IáAÂô÷Kü0KÊõmøcÇ*—”–Ë––Ÿ%—*YZ2™@6)—nä °¥¼–šNÿƒ‰")®¯Põ `fÌòñ2c„[Ô j‡ZЍÕà ´Ó VKÛÁAphÌÓÇÉ9ƒ~ä$Û¥íê±OË\n¨\ÿ‘+UðãQå(×Q¹Æ8 çà‡ËùDO ™‚7P¹Æ: §Ëe%GÁÁl‰#ê±OãrM§®a¢Ìœí¥*r2¬'cœd¢g«õˆÓ**~>¸hÌÓ”\®ã¨\[ŠÛ«„^ë$*׫;Æix|ôËE)pÁÆ8Ç"Ô¿àòq©rìÎ…Þ„:R6õˆ~C+¦;Mmì“R}±ÉžÌfɵG›*†ú6Ò©3\€I|zcLcS?™úŽ®E¯œ|ŒóÔ˜åæaÑ2Ézdž.¦Fúæ45ª`û@ —û ˜t&YZb@J|†óTmšRQFÊž·¤wMsVišŽ<Ã6Prœ‹•ŸÍkm›;·­²²üoK÷/]ºŸ¹°{Iw÷’ \}dõê#0!ó& ’ÇHÞ1wÒ_d ½ß´4ó ¾©›^„oZÝOâ*†? ¬“à2Ò{Gú|hdÑ\¯äJW²~›EoG.0Ñ." vŠÝÙê‘þSfcÔ#¢ÑðRZ¯Uh z#Ëú;–oºå¶å˜W¢D¼fD8üåÝ10x¿ôg…Ï¡2šô*?×›8pÛ¼¸[‹ã‘I2üƒÑ^¥ ®ÈcÎRä;«§⑟¾h$L~EûV5Ëeý1%-š<ã÷E˜pβ%ë½±Zœ˜¿`ÊÞ>·ÝŽà-ùÝç]TyëÄG'«¾èPrù‘+f?8ûŠ#Ë“Cí¡7üäè’©]¹ÖÛq¥3ºþÞu7Ü}ãþ5÷®‹:¯}s{{çŽü¹dǃfÆüàŽ…û¦Öò|íÔ}@ùê%Ó7¶ùUœXÙ±²k×kß9{áÖU3çú=³g¬Úº`ÖàÈïÈŠßBvœÃ_É7޶2Zzg’ó1&¦EÊ4ɵd~PÊÒ$senc1WfGFµ0÷ñ 7VZ0´Pe lNÅå"o«…û"v*]±Ø!ý^ˆ1ÉŠ%vN]…u£2#.4 j³ÍéwÕGzN¥óåF+¹tü‹žë¯vK7Ù þ7Xgyj°P•G@klü=-ÒM±ñ…Ê,¬ÏcL1—›¬P—¡<}1¡x–9ŽN‘ue²°Ä”wS¹÷€Û:µïÜL³ÆÂô¹û`ÿ¨ÄxWÚ,¿•Lñ Áé÷,{ŽÉ©²T‚¡Kb}èAü|¹´§¸pƒDÍM”Ý )hîÁÃýE%/âor™Yþø2*@5ö&bNXÏÀJÁ)CzxhpA4k Ťò%ý*¨´9ìuJûØ•ãl’Zö+’yÊÀÔU|, üÈ*´ÌǼ_2¢—wÞxãN”ʦoÅŠ>‡ÍP^-":ËHø¶U…lÐTW2Ž]7 w”}°÷ zþº)m ã²µª³;lJ\W)ñïÕ-j¯3䪥DÙ ªBÕÿ¶nãQ=r>™¸Cû*¥BŽ Y«Ã”C ý{5‘ ¤àÄ¿Uø¬ŒŒ6rdJÏ·Ó.1%¾nå÷…ýœ âm “Ÿø/È‘1¹]˜ÌV&»‘Þ|3}äí#é7¥7AÍ›têMuÞ]Mª“õv{SZ(½™Jð À¬óú¼N Ïk~ô%·R©9Ô2jµÚG´Ö÷PO’± Õ }º¨‰¢ýpÑ>JƒÞÚGµž9ÍYÏŸiŸ-Þ7æ÷cøXƒÙ ¤ ýô/e2 Ù#†2 #a›î7dò×ÉŒ}˜ÛJTö¸°EÙnÄ7ü ‰(Scÿ"˜ªYl$)>+úÍ|6ê”4ÆAväMöŸ4HÒpœ2¦³&¢ñ¯<'³zN UEÍÇ’nÎOJa$qC”˜S³VÓ\%vºeò(# ✋,Deꡃ³;W>¸ôøû_œLœ·"‘(«iÞqê9Ëý¨o±i¿Zñû›N(KNØØ²Júb™^0\{úÅæcòüf$e*F©&-&šÑ1ù\À0âc®KS©†ã¥««¶C§cÀ6y^3fŽ­Åc¼_n62V«‘w2cW®'ðÊŒ±—€9c„+„ñM °¡‰§³`v],IdFO@Ë‚t$/]Ê+ÃÒQät¾êX§4q´Vß”|뮑iÀm'?Ÿ€Qx²ëì?^ÚŒ£"¦·Ü¼{w“Ö”pø¾‰3u§JÒI_—ýä¸,÷ÃÓǹ=쥦*PjQÛÓF+K‡U@$X¿AÂ'…Ù¤â˜L ­fDÖ˜»nïvÝÙ :Ú´à é¦ù¬Åj´JR'ÚXØùÒ¡üó]Sy™ù]ðÏZö|ݨnÝÃíîÁŠn“¾#i½AíGiƒ^ÌCåI(0 U¥Ô$³±ì à!+~ù|V^þ7ÖÀ¡Þ©95hyßX“ÝÄW&º¦U…äÐFnaž%›Iä·¿q‘]z÷ÒPuEÇx‡}Q#Vz Stca_Ò;ÜF{°ªmfödN÷õ <å BTu.µ†Ú†$‘ì[Ϊm-&«ì LœBE#›Øc64*àHˆ&²Š<ª’(BE¼Ð­à Y€¢¬Y¬ÝÍ?q† ˆŸšÑ£æÃ¥²ÐLÒ'Ÿ™Q‘æ_Dn¾k^#SrQEYÞ7âaò˜Ð>”a¢nË­X‹<ðˆÆŸ%ŽÿYq2Ÿ‚„àÅμi’hlä´¹Ãk• t¤7ïÑÚpn{Wµ’NÐêîÕ±ÊA0®ïî½³-wƒ NŠ/mmsZ¬síFwP¬sƒßÙRW•,·ŸcPîV»t@Ý1psNwñ7íÂedð|\:¡r—çÎ:à ûbFŒŸ‚…[Üž f'ôS˜ŒÇŒµ¯‡E&YB‹‚E3+'×ö/ô‹Æwé@¤}®¿Ò¿~ÖÜ‹\AW$Ø·ì¨*¨Ò¡;H]ÖŒ óÍë[RÍmO~PXØü5µ–æú¾êÙ‹ÁÓ³ð¥KÂÇÂ,5Ô±æ`oíäÊ™‹Ï®î«o¶ÔÖøm†*¹5[’昫äiYYŒInÀ(ùþ(…9O`OðCþ‰ŠŠòd÷ñ,à!³€Ç¤¤ßýŽ@Tfu €úô;¬2 œhç4uRúê$ö?¦“©w¤çlûeGÓý60ñyˆ±= ‚Ò*‰Úòä~ˆ±w1’e¶ß<›£ óÅQ9Š@½¨£*VãaXLA äýù8D…Y#Ý|r"ÞÞúçHyGÕg÷…ég«ÕÌd+]œ¹áäþU÷Á™ç¯Þ W ]ÒÍ©ý'Åþh¶"ŽUÕõJjt§g·(\Ëò~â2•Oö Aߥh4ñ,!ÍãÔ3I)¹}éßÒ=+nÝ3hË´†Øž­WôÈB0 S§®ëœò,ýD†šÿоKfOv(8Ná˜<û’}Í—ÂB ;ÄÇC«×è –x‹Œ>.‰˜Ê wÅX4™|>¿‚ú‘¦ û˜üäkÂ^™ZÚ å :ƒ$¶4ð€"ÓíÏ8ELz,ޤ"L„òï4Òn4‘s‡¬7­à_ÌB~#8ÔKæóÊ•;Ã8ÙÏÇååĽèb@ AÙ³ž¾_«Ô2´”Ôð§© ×Ë“ÝîžÖÛMŒ±Â ³µ¬ØÔµ¦É¾tÿRDx HÓ º‹•ßy¿”6¨  šUÖ'¶ “©‰ö¬}ȵ¡®uŠWéWhljÏÔ®ñBU ®•×­`?P¨pݧ=œlÇ­-bó5a‰•欌ù=\þD<€uƒD8¶›¿7k³ òRJ¡Òj’:vžôßÒG4Ç«’FíÚvõ÷sË›YJ©I7=Ù×/]nP1*üÒLÀ>¨’¢ ¤xhÚ<ë׊y®§G /'h¯—“ªñ¾—öŒSÅ=zRzíƒÚD-–^–^ÿµôÚËÒkÀÀl¹cá|zÓðuô,3ZðjÕp’N'YꢭEv[<àPÁD¼­ªr<‰Æ)6•Ñ׬EéeÅUx×"Šàçb#¼¢D“y ¾ ¢(]£ˆïh‘Ãßœ{_~>zt8KV`Ue‰ŠŸ_F“³CÙ‚¨ô2)=¡ôù¸T¸hr1_Féðg{>HÄsÑ>2=‚ªäùÌ5Eµ •¥ ä(-,øÍhŒÿ1Ú€4¿*×¥ï vT½ä—PªNþ˜4Bé ƒÛÇhƒ$‰1âU,H ^…áŠü"Eoxi6Ȭ5 _]WZ^x^÷¸¬eÀê†Ì%z©‰M¥2?Êü”~äñ̇ïÅbWK®+ çxëëåwßMú¯öt’ûŸ,¾ WE¯‚EùŠÞ„ˆì»Ò?‡ßÎL˜*ËÁýàýÞS“Z˜çB§&¡áí%é  +o¸ë.0Tþ8ÛV…Ìç2¯è[•Ç¡:À¡V Â(vkÑR¹hÑiŽæ¼Œ ‘2¦Óò¨´Ê¤dtês·K›¤FiÓösU<£4¡³ß¢TêWö|q“,\·L8úæÑ -òÁM_ô¬Ô+•ÐÏ Ìûdl”-J¨:÷º¸î\”/šDÃÊÅ»Mð‘Öïñm›€½G'lóÝCNdvšv/^iM‚üý¹Á?ʾ…ý_Q§É.tb2û1ã)¼y²’A–ú­`R$8ÑIÂíö.9~º”¦Fذäu=‘V (È!g°x(•ý;“D†7–lì8O"J¢òô,†ø¡ g|èxßçæo­Éì9«5‡èPÈž¢ vÁ³øšò=O5UvJcÙ±÷iOnoÌŸ¼}0ßF•Ãx–ãârŒõ e¿k·gIA9©¶Zç}…0™)± þ @dŒ€fø ç-Œ _Í ekð°NzöÞdÔÝú¶º”Î.eW}÷¯Ò»·ò*µ û5Xòº‚\Pk€»Ø“TF4ð}&é€ ]€æí[uF“îVàþëwW±@­&g¯K÷þZ'¨UôË¥þ¥»³„… 儸‰¬%F1h|»¤¹½Á`ÔbUÈÜ,L@RÄ`&•*ô.ã§cÜK쯈,‡Þ¥Š-Ìx–ÕÀqÔ¶ŠpN&Ú/«Å„ m™ç¥çÁ¸ Ș—&sÛk…8}Õð¶àºàž¦ƒM»ƒAú*t°ì 2mÒóŒÃ‹ïjÄ©ñ]ø~xÝðÖ ºip#J·.H ¢›ÐÁîàºí"¯õKC¶Çðý•ŒéÔ˜Þ¾²Ja¤w/=‚;¶n ÂY|°Rr˜hyhå¯à—*æ–…Cy½Ô@¨gå”ôÞbšY4N¢Ñ_³—ReØ/½€ì±÷¼¿@³L-T¤q ›Y©Ôª ™®Œ:Ð+wÒ÷±–T0éÓ˜ Ý e±H)™ËŒžAáÞ&æô5Y×KL+iô 1îÁîr銀SJ£L¥´Ãˆ)¥yÍ N¥b)‘¾kŠGBù‚”;„)Mš7‰#e@‘,ÂY`Ôgx®ÊÎîµÿ‘°L´ªø-~ Wee”FN|«H_Qü> ã>‡FvsöZ؉ŸÀKöSÑF%ºÒö)¼ñXcS?xƒ7Jïu¼øÒ)葆2CtjIYÙ±²¾²%ppóí£Çû›À÷uø^‡oÉ$¡ oS‚ýKÐÇÊÊ–ôŸé»·c䬗‡‚sçØ¤@V Œéåî!Ô™冀–ÂS« —tû~€áÊrœŽ´J'²FXU\’B9‚8V\•tÊgeE"†˜(Z>*-ý1èG/eЮØþÓ}ç5yÕ¨õ ÎB׬^øÓ#Ü­´«/Õ¨bËK§ýFà÷éDéò2à5 OSôhý°\æ²Ï‹˜Ø ´_YÇÉðÍXßJ‡#JlœËëš0z¶Y¦Q ­D¿ñ‹;þ€NìÞÕ×kgúÕ ƒ^7í gírûãáÚuÝ•»ñù;DÝ^h^×Ó&pFí,¥ž×ÑÖDÇ‚ªe;ŒUÁ©‘ºXSb|ЖÝòŽãqÜ«jj£6ô¬j5p…C9fYƒ¯Òj6~gMesë”ÊC¯¹žÂâOp>o•LGô€VÓ‚¿Ü:¿×QvúEÁd­ ut-̾³½èuädp(,YÖè0Î;['òL('‡G½¹U¢ÅŠ­3{þAë›?¼Ziþ™A%½‚±N6î¿Ë"Í#:µ;šÿãz\4š|­3>‚VƒU«xáðS¦ïI·A 6üZ¥»T'Ο-ðèÂ&Qw9N‹vÛçÔ‰„³žòú³$Y˜–|w“EŽŒ¾–¯"ÙGãj4×ÍÌ…gâàâGQ§ 1À#oÿSú±R©~*ª#Õ•Š+Í?6ªUJé¿!}îÀ'oQUÀ_­ç ü€N„݃A„ØÁ½¢7f~(êxaž¨[Í ÒÓ:Í6îÓÿMôjÓ9ŒÀ+ó˜é‚§C•ð–6u>¢·;<‚7 urjÿÉ!h´Çf7/®«bÊ”ŒE9‘UkÖínñF]a‹UNSÄ …ŒnCDŽ%m)5bxì÷`E8ZõÝÍšR]+P·3}OgciÔêœöŠÚŽ©K6w <ñ ÄèÕÄ ‡‡ËÏsçjï•’Ò"IÊŽFò QýE¿h1YãbÂkõFÃ~|-°äòú“&½‘öÓ2c9N c-{ç^qÄÖ¢ óÊ ¶ýÃYwNlõKo{À]Wù'€;gÜ= Ùà•~CpãߺWa»Ó¦¸ÿõÐVc„ƒ¯áj<î½o.XĪՆƒö°ú<…mM±\p.ë8hP«ÙÅp’ë}O¢–›ªÑÒœÁÌr¦R© Z¦Ko¡têx*åA ”9f³ÁôË«á‘ãe­5X¨×imÒ10`“µ:½ô`6^;7¦˜P;F©I¤Y0áÏ(Ìþ˜/lö}èM Ë ùØáÑÚˆEÍñ(úqÑtc„ñ€×† itpÌuÂMÛ¶êÑ[/}k_Õ­Â$ñE÷†¥Së¦mx3é½uvÅ­3/hÝU3±maÃL¥²%Ô[ß©w‰í¶†ÉÕ] ¶Õ×]Ó têéieG¯š¸~B…9} S§Á3Qpwï½  ¿V¸[ÏËÜh ص”4«58|ð•7굪9¤—ÑÔ£ä­îH‘Â(Ç®ŽˆdGЋ‘µ(›ÙÀ\,å=»45ÐÔðPC£j¥@Fß ½Ø ËylzìYrVÌù3=ûÌp `ìR æ—p€/ì^x†¢¢²úNSÜn£æ¨nj6j‡(¦šò+ÐdìªÜ’K!ÈJŒÅÄiñN€)1°· fÅH`1ã„13^„ýŠ(ÞŠQ‘y໓´˜Z‘É|¦–~‚=*¤4ÖÞ¥ÉÝcz3Ï‚MZ&áÓ ì€ é:N¯áUæ¯Þ†¦Öý£nªôî„÷ï~Ÿø}1Ÿö”+œeMl?®ë׃Âå=•Šô–¿,Ê|¢4Âíôek×>¼v-<šY+Û‹Šë݈ë,Ô›=c½AIÍèol‡oQï;FÔNC«Ñ~FwÎÔhl†Ò£ðk¦¯¹¼>ê4›Ñúró‰žÞÃiPîàËb¥Ð_ïh0 ñE£L&ì‘®¯¨-3„€OúÜõn`Ûp¤Ñ\UßUQe~ZUÎ[„гõ²Vg(TÖ2­+êZ³†nº=½½1CwNM+«×³­5 Ž?1·º ï·UÏ¥[AÅÏ~f]b]øå޽-å¡Py Ù8ÛÀfé/n´ƒô‡ à¬Ê‘z_ôu ñò/h¼,ôÅÔ j7uºzœ¬21²#z×,z‚QŒAlŒzÇx-¹—C½#F^^0æ'¦DG½ØfLò¡Ã¨¬à<¤‹`˜uÔ+<¤‡€(rÇÞQ1×÷ä~†û^pŒJ¿¶Z,Ö0˜}Î9Ã-ë¤׬žE‹\N‹”Úȸ88®2ƪ-ª7ªÀìÅhX‹<é ÷ô†ËÊÃã'¡Ì Ο_uð [žÍ8žmY¤s ýÖgàûdرjçJ¾.X¶v"xº,8¾'TVê,3Ç":åb@ NüGÔZz#‘Þ£K—f~>‘®¨2Ó°^ÚYo ¶/}a²£)þ»Ìšq‰„sŽ.ªŒ_°zf0 Î<Ž61§SEÿôñãߘYðáÖÖ>ÎlæúZ7~‚÷&“í3¼´Iú;ÐO9´z®ô¯ ÏBw‡úïÙ̖t‰Ž - I×{¡¥ì–ý.1ó?)#,N^'Ćpn!Œ5Éæœ"Ä> ç©¿t†>5›ÔîÒjTÖO+ô¯4šÌg O£V[>­²IÇìá¿[èóiJć¹Ð+ÔëkÁJƒyø¹ÅdÔ× =ôµµÞ <6‰„_#Ãb¶:˜iΊõ @Î Gñ0@b¸u”ÁfÙý¬ T(w?¯R) ϹE:¡0þÀ%Jç£e´Éó¬ Pª¤ap‹ò#Û4xǧÑ ¤ûy^ giý™0”¼~´poø†+GãQ2<Áó FšDAÀ$“±Ê}s£€BoÆ ¶.‰rû¼^ƒÞÄC º ^oX;ñÃ{þ8q×Ãì1½7{¼xŠ$M‚ʤB‚R ’GÓëïëš¼Ri·+WNîºoýÈCJÆ ãÒì!bSŬØnôi3f`+bh=þ%Ì*-ZL"=,YØÉ‚ÖÊÖÀ|À‚Ì,0_¤Çؘ-Y¥‡ÀðWé1I Û¥W¥?ƒNé½õÒþ/¸¾”a=é=æ·ÒŸ¥×/}.ý]ú (§÷H?‘>ãoÅî!¾zú|iüØÛ—£Âì cRO¯QA‘E@¡‚ 4=˜i§Ÿ§nôƒôàðoaZ—éœ  gæý ž7=s'8®½DÚ»wÞ´óÀÍàf°8ÓãGåÊ…kvío0]ãw0µö€ôÞ‰Ô¥'N\š¯W”?V^A~Û:ëÔ¡Y[·Îb.šµõBxegïž·.útogf‡ÃïO~õøã_=o¸¯¬²²ì>tÓÇ…ä[‹¾=Á(›<ÍE‹J*¹ƒ[/yâ’Kž€OMŽoJîÙÃâsÙÅß%D³æk½lT¢ ï·)ê—Ò…0¾TŠI±¥P N•¢>‘^‚Of¦‚ú±âƒûØËØ{œŽ£{0[°paçGKGè“AŸˆ$±‹¤OìÄŒ¤0‘Ä"ŽªP¸ 1Â8‘#AtšÁW0G"Àb? ºN¹-./ &'6ð?_Þ9•fnX²øâ÷L“jê¥w¤«#IÁµ$ÑöÞÛ±%ó•z]M`þ«/¬ŽLœ4Ù=œð'˜2s†ŽylMµwXºõ«#z³ŽU@•ßìPÑ徦€k÷I° TÞÖfð¾ÎiãìÙFAÛj\·¹¦lçøÅ)¥ò¼ØéW)ëêjŸ£Ì¯R”—)•þaÁq~ÏdÓ¸:Ú¨4ùbþþç ªoä|Mô³H6Wc™qOȹQ[^élT5¼¸ëÑIŽZ—K¯‰Á‘i¦ÂÓ*¿+%E[ÐZ—°‡et—þ€ÊX½ñ·Òß÷/ UG¥%À%'èÖ< è6·›*FXÖn­ŽXLkk¼½ûBí’žú…&÷lÐbvp¬ÉÄ)ì&Ѧ@;«°ghEØÎ¬]ËiomšåŒ¬»ÖÂ_Ä, o§Sëӛƹz¯þU€m4ù4}¦²Å:SÈ 4 ¡d|ÔŒl ö ‹Ê3S–ŠÓ<‚]Ó#ÜpàŠ? éüF^nèÙ¶eÕ´éýkfÍhk1[^œL†ÃlJºäïÒEW‚V÷´ÏÇE§½!¯ƒÎß¹â‰é3ÆÀ𠯲O‰61¿ÌëiŽM ÔQvÊ çGƒåj ¤OîÕ”—×ë½Ôh¬ªnï˜ÑÓÑ >,nÒKO4Ye® |øÏin®ª°Þ#­ŸQ]šL¼ŽaGµ }: 3ñ—¥,c/¡át·ZÇ«¥;tJ•)‹ˆI)¥Vƒ”Aâ }*ç÷AÁ ›&>¸²oIæ9‘Ý‚™|>fƒ,Źƒ:FO'kf(d(s)eÈòoZAÑ’g©?‡Óo•B.AI¡àÐÈg,çI ²|`4—Îæ)ÛŽG"ücæŸ4.BI©à>Ôßá•ÅU@ ”÷™ß„Ú3D¢³Ý Ç~ cYé¿g²Â’9^AŽW&*´š,ì¦ðÜËRõ‹çwµÏš=vÓ ›6>:iÍ€¯vùª‰Û—66Îôw’Þ-wuÆãÁzê”'fî®Ý»Ÿ÷x¼>tÀ~þÞ‘Ã.—Ï×HöD—nºäçÌÅíS§vÆ wÓúu•´f´Å¼y4šÓ52«uÐH§²[øpf>þãRÃÛ°û2Û–ÂZøß™ `,³}ø“Ýð&úÂá÷á²=cé²{ˆ?d’ü¦£5E5ÄɼÅd·¬<»É\†ç$ŽíxyIóabÇÃØ{›º±«àV/$û4XÀ;«Õc'=‹Ç:|ªª­u~[33Y;µm~Û¡¶êª60%’„­K ¯H­Ÿ¨Ðê“–½¹l’B§U€£øz[UuSnÅùÈÿ^m«’fW·µUƒÇªÚÄ̪HòÏøèÏòo2o7%^ضí…Ä>‚ÓگåºÌM¹»ª[[ÑüªAmñ/ ¢§|@L êÁDð)Á9ñcÚ©+R J-×;è6BsjÄÊ Æj tËd ųê <ø£Ù –ãè4g5ù#¨£«!ó4áQLA‚”¬ Ž•’©—Æs§ ó° Ù#$ÏhZÅ<Ö`ñ ³VœÂ‚“à÷ œ,ñ’›]ÐGšÊÑÍ$¾gFl¬q<5E;|Ëc¶XZjâ1ò nD¢G†>S'hÄKD?–'葜AC¸ . €)4gRðtXnœ?n"ˆÇHQn.ZaÂyâb-Ñ}…ðE¢õBµNȳf”@Ì(²i-ø!4ɵnÔlÆÙvv±ðfŠaEv £WÛ”´tð4­PpŒ‘HÏK0H¼Eb® ¨§ømÞ^MØ­•YÐéï³[Ƥ ë[9%g±ËÔÉF»Å°NªJ; |eÎrTF…šc4 #&›Ñ€E¥ «æ-j§¥.«œV¥ai•Ö4YUã°ÇÕìUÆÏë´è ä8BG—ÍŒ[ÌU¸Êu‚u¦Niö0cX&a+Óƒ*ív)«øH˜Ñq€6©#;.¯±j´=’3ÓVТ€ž™»h §‚´š¦54¸ªŒ«b9HóU‚Jó”ZKó yFÙÄêh½JÅÒ¨!Ã(y%0ð0a²@…Ít„”¡eeÆU!Áªö¹jæ‹ÓL5Ѳò{“b2PmcÕ>Ðð­æç]6sÌõ©tÔ² ðÑ´Ït™ß¶²ËZ]M &õÎq½µ |‚K¡ ZB¦ y-ûÂ]±µæñ,’V$é‘¢Q;qŸàT<´„ƒIT7SÑÚ>96Nöx½4x½ÃàdÎ"àPU€žÖè8i6PYV©†À ¦•øuCéVÁ¦·; åjŸ¢šw¡ÉÔy÷Ö ÈÔ^ ·¹-è˜í XÌ]>%í ¡ÐÝvQ¯`’¬«Â¬¢•{ô*šQ4wÐìÖ׸!­QrÑâUFÏk­€w°J«^ hUFÏ¡’М›$•2ŒÞ €Ö êUŒ ²,ÃÑ À·9´š·ŠVØ;Çõ–s6 «”6³»³¬Ll×ùZc= ÒG*h}k}ÄÖ«4(!«R4ô“BJ.bï±–q«Ç¼f‘Cz4t•Ñ¡ŠzÓO• š¡Õœ@C‚ÂÆ¨€€qÒìGSB=Ðé8FÇr4j6À|ý¢ÖnµXŒ&ÀˆSœ… *· nŒ^R™Ç@›uk­Qc] 1Œ TZF-ø|“½&–Öé«8›Ö¢Ñ÷òFgWržæj»ÂÆ5Nñ©lK9fb_ï5]׸ñççìª6ƒrgս˶oZÓúÚ‚ú‰ú‚¨Ñ•¢¶œ òsvwMd½õ~;ª–]£™2Q뎺œ}¯/Eñ”ÉÖªê æc¯Ÿ`ˆöcÃ9æ@£CaÆ‹gh«LåŒF4LxØpÀ§ˆ³xnGŒ ã»ÈXÒ\Œ5>Âÿj9„†ø{®ôëŸýp»Ù#ýR: ö5ÜphW(È«w\r(íúí7~± rÃÃG:œùÜWÓfîÛ2þâ‰mú÷è#@eꙺ{¼]„*:0}Bo[¬Ú¥¾¸dmÀwræé ®®9 o¨ï8WÁ_òî¢E·-íåu€ýÏ·îëúüæOÛÜŸ¾?õ/ô\¯øð›Žññ6³äûà{@kO6O.‹UqVÔ½h´b`á‹cáKfÛ¯ƒZŠ×$º`†áh!×ͲHC¯êÆ‹³LJœ³Yt@™ ý0‘^BVßct=Æ"`l=æ¦pËÂéõ®²*A¸º·"Pã¨kÞøhojCOhÊü¶#çX<}]ÑYõU å ÑÿyhòºÁºwïÜ;0}òuÒ©n0ôe‹ÀïæÄkl›Ba08ŒÓm^Ÿ-Y›Xqwn˜Ü¾¸-È,¼©"õÔÖzÚj—ì NØvøÎwû ~Øë&OØ+H§ðA¾ "Û·Éø+9ô« Oe9Ø‹D‰“­ Í»ÄLUYü7@?ìK™7Ç2'b185v4TÒæ“UÍ­•;ªªÀQW+k Ãu0¶wÏgBF­´†çwêjøaØ¡¯‚Tþ^ô“¾”6¬ªÜYÑÚR…™)™J¸ŽŽ…í;ôUúLH ÓWñ;ôúaØ©¡ ¾ÌÁQÞ“gÁiÅv_"#ƒ¡¬E44r|ML,úM3Äž:L"yH,¥Ä^Jó…D¡¹¬ .kxÇf…[ÿ¶r›åqÒ¨¦¾¦d÷’nò‡ö7öÁTßFiˆ”†NJ2w_ÿðFR’פqxK–n!ìÛ¸¼\(GNÿåÅ>ñXEcÚƒHŒ²XÅŠ,5Ëë*æ·Ûm­[ûüÊH¹ÑÀ,,T>¿³Léw;„RQQæwÿ˵cŒ 5h=ñ&{õ£ÉX‡JHäÐ ÐЂh(Á±gá ‰{f‰«n0„Ý*±\™¿]6?‰ ÁÒa‰s¬Õ¦ßöÖ‡oݶXÞ€ ŒAz[§ç¥·ŸR{ÔOIoózô¶aUO=¥b ‹ ð”ʧz ÐEÈ^„šB6hÓ³ýÒ+µš[ú•N÷ÕRN­6€†~VoÔ~õ•΀®‚ùªV+_•^AW º¯¾Òf×z?b/¥ÔC© ËðPÆA™p<$L¹„'4 ¢1†ÙÀμDòf>‰7=-ýê©_ž^õÈÇ{£I2´Tº|èvL{»å ÜRc¼óùúÆ‹.¬tóŠ¿¢ÚÄŸNß×&=ö›½?²j×Ï^úÇů€²ÛoÖ_ïæ`e¥{Æ«[nüúHTpó2Æ—ÎÚŒ«I þ„É\ªeo’,B´€«Š¿`tåkr…ÃÜ\÷Ë0|Ô0AÛ VNp ƒ`ixNrýlšê¦&‘è¸j´àÅ#!î<ƒM’¾¬ËÑAv‚,qE+0†ñ÷á&H=Y àÅÁý\È1Ôóš( qáç¬)Ù½|\*zþäV^ÿŒ©Ì&Š´ñW-2äÆq1Ô(§§Câñ!‡4!“úPÿžÓzdûëb£(Š/°†Jƒ´9ÃaÿªÙ ÄLÞ<ˆ+’o”³‘~©ËðôŸ>MÅnf"uñµãäµ›5ê†H€hqÇr!4#ÒH&°š‘‡ŸÃuŒÐhaEl¤‚gFüë¢ At K,ÜWÐ:ÆDPY°¶`½Z‹ ZƒhÝ¢Øm=n«œ¡5ºI,'\Ó€"ʪÐiÊ–4™\}Í]6Zmõ@Á0‚ËÄ£›ÎµÙÕþõ×´q4£¯‚Ö²¥©Qo(WW”é '¨Ô,䜽M'ͱïÏŽ™œH¦Grä²3¬I«5Ï_§¬Í?¾ZoçX‘f*»zl6uŵƒ€»Æ`a9ɘ ­17¬++o[X_Æe e`rE·NëSA‹¨q@ enoK㢦ÃWçVAÆQ½¸c`§ZOÓýƒ¬^%s?Ì}ÉN¥ÔdÔ«£æQk¨KÑ™_c¾b²‹Öö&jÖ`Ðú Œ‰x ˆÖºhdÄ1¯:Ä‹@v Æoôé’%t,xg­)å…dœ#§ÂxA+/Ëá=ؼ:ÃlzgmUªt|¹Âèâ]'jÿkúYuu¯¯Ý° ­ ¥ÓGþ$ýW päO BSÿDÊHïKÿóÖÞ«RESºjŽ×sÜUÿ©­…,¯Ö6/éÝ:×.*«­¨`¦…¶*†uØÚÀ¼Ѱª!îP–::]P6Në.Ûõù°o‚žwx}ã=ÎÛtN–ÕèÜ<«Yºª?à{nÙ¹Kœå'ÚúoœÀ[?>"o®í½nß@GÏögÖoLê¡+¦$¯çµ¨ÀÖöÎ-:^ƒzT˸lé®&ôtT†Î~zº­’ÕÍìÏlq:„çì§zÇÇÎÝTÇ9¦æü|û)æ¥B£eB#f‹ÑÔ oªóM®x?«ö.åÚ™‰¦þ`8†³Pöªô'•kŒèÁ¹-€ù[ïð©ìý¼‹Ç ïÆÖ7{¨qbžÞ¿ß ñí‘c¯œˆ|bž€gøì»AbÃ~ÔýÄ»ƒ<]×oáeGw_u]=§7ªÇ½Žñß~™Þ]¹/H‡ùHt´œ¢Û@2³‰NÐ/1(Ìÿ 8ä4s5…´Z‡Ü{/ÿ`Ç>uŸÕn{hö)˜ÿìÏErŽcúýµ{šŸ?îe‰xÚc``#Ê10Lb¸ÂèÄXÀ¸Ž‰É†Y…¹‰ÅƒåË/VÖe¬ØBØŽ°§±ÿáá˜Äñ€Óƒsç?® ® \·¸u¸gñ¸ðTðœâ5ãá]Âç·‚_Œß‡™@„@—À#A-ÁUB.BÛ„+D,D¦ˆ|õ]"æ'v@ÜHÈRÈJÈZ”õ([(Û"{Sö«Ÿœ 9—rrËrÏäþËsË«È[‘÷,ß&¿!ÿRHA\ÁŠ‚O…U…—ŠBŠÖów•蕬*¹VªSº¢L©lQ¹Rù‚ ¹Š%•UÕÕeÕjjÒj6ÕòÕ:Õ–ÔIÔ¨ûUïU_R?¯þDý§¯†¢†ÍÇZÒZ^´úµÎh}×fÔVÔö¢Ý¢½¦ýYǚκÎG0ŽDxÚc`d``lg’da& fB0ŸýxÚmËNÂ@†ÿ¼£+C\6Ƹpm1a*^B„ A·åR!J!m½àcø.\ø ®tçÒ'ð9ü{:E0d2g¾óÏÌÎ €¼"•ΰ9cVÈ2‹y«8ÑœÂšÓØÂ£æylâEóï¾k^¤û·æ%¬«'ͬ©gÍËØVoš?U_š?aª\àe¨`ˆ¼Àñ!|ÁGñ1|ŸÀ'ñ)|ŸÁgñ9|_Àq?¾„/ã+ø*¾†¯ãø&¾…oã;ø.¾‡ïãø!~„ã'ø)~†Ÿãø%~…_ã7ø-Àïð{üÄŸðgüÅßðwüÿÄ¿ðoüR…@DU2¨FujP“ZÔ¦u©G4IS4MhÚ•v£ÝiÊA´'íE{Ó>´/íGûÓô:¢ƒé:”£Ãé:’Ž¢£é:–Ž£ãi†6Ò,ÍÑ&ÚL[hž¶Ò6:N¤“èd:…N¥Óh;NgЙtMçйtOÐ…t]L—Ð¥t]NWЕt]M×еt]O7ÐtÝL&ÝBÙ•ûÉ!—õi@ù´ƒ†PHIŠi'%”RF9-Tîkä‘?3³}FÉÙ™™±ÜXÊÙRΕrS)7—rK)çK¹µ”ÛJ¹½³gr³–gñ>µA`¥i-ÌSß©§ÂJ¯)¢ÈXÔ<¶3#ͬ¤­S„q62òT$FßÂf晕 e^Cé~š‘ÖÊÑX–24ý¨©¥Ì³ªì÷ë©?ˆ¬ êÈA-K¬Ô3<Š&¯&L+Ȍ̅‘HËíºr1 XQÝͱQÏc%j~dË¥NX#Óñ'¼g,¬¬‘ˆ~"R¯©\Ñ ÒýÀ´ù0nìÉH¤íä¡0ÙŸN©ª Z¥žÇõ‰#]Ѱ--«™50ø/5l)‡MÕ„V2¬Å‰euÇ Eb}eü=pë~f¾ÓÉÄRfzÂxY[닾›ymþ6ˆÌ@ô³n¡:"ÊDÒ)ŒD ïúŽ<ÍüþÈPgéø‘ËãŠy¥®ÇNô-G¨¨™ ¾+d#ö,OD=‘ãíЊMå«Hꖫ䳟Âõ³ZêY‰¨9žà© 륙ˆMÛr†‹Vâöú‡pl5ÇŠ¡‚^‹-CƾLTWz¥Ò¨‰Âɺ¼ÏB"‹“÷Ɔ>B+òÔTÀh‡~TªDZoÈ¡–½¹àð£P$ÅfÒP.L–Ž/ˆ$óyÇéÒödâ/3|­ Åˆ7O-’-úã²¼™‚½¶ºâMÞ<‘Õ¡œÍi³t9íe^Ú)ûª7YZÊ]e·4‘xVÐïhv)8¥¡ÖeŠè~4dp¡lÄyêñ±zœ="aÚ0ÕgM!~TçÍcoÔø¼ƒ]à `µM-`ppU¾w4Ä‹&ÆÉ[˜m= Ø¬LËN|Ñw¬T´r‹<© ™Ç†Še1’»u[XÌU'Ïø*cŽŠküø±‘Z ¢­âcÚ Ô!#N&Œ'Ê’3FâEæñ‚¯•3/%¼¬`ì@Ô¼¾Ã4Ÿ;Ã_#ûÃé;±¢é°O ¤ðiV8 ³¦£Æw(Fm޹ÈôI›…ÊIZ(:‰ UÇŠó†)mñaб5®þÁ¨]Rfj-jZCƒÊn‹¥Xeaq»|q1®–†ìH­Ï©UCá5Ìu±å6™æ4.šê-¡FNhES £ÙmrŒ¹zY¡^ -í &Wø®$ &“¢Xèü5f±–š¢ÊåP‘ £Ò0gç·uÖT–NšsFrúú1Ã:· ‡mëÆùò²Š/ÁT-¨Â8±ªšúáåù"p'Æ…¦ðfZ•(“ÑÄÊýÔãˆ&LvBž%Çe‚*«M:~´lX×SÔÚ.EPkmMP^› 'MçêŒM¦ÌvÁª%ˆ™™¸:îÂx÷ãÔOפ镾qÑ2̹™¹–~ú©õëÜÉþN¬¾t¹.(_w6ÁI¯`X(±ÅwýŒÐ´®SœÛ8Û.J¾®œöœÖª²YE CWž¯Š<©츚§nÕ’êŽxTu¢Qu˜,VíÌQÏdÑZÉÙ)ÍC¶FìY6g¤97»mÃJoÆtjç™Hwÿß.u¬Þ¸[sðô:Ks“97·I5›»#®¦¹]¤4Œ%¾æÖÒøé±2F³á2XøQÍ”Î/½1yñ‹íAb…õ>¿i‡IÕr™:6Îoœ°ýÌÎUèËk`& ’N!t×d y£Õ*Õ[cçñÚ¯ WSkì"Åù™+Ó§i"}·Æ‰‘/±›¾­jK:Å\Ôdž¤;s¾1~0Td½Ï´C5ª€g~\Msuµ[¶4Ô?7þ‚¨Úù€†µEáÛ’ÿqˆø—ÌϪfN5›þ jw©¤Q¸ŒQmongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.ttf0000775000175000017500000023234412651363712030477 0ustar travistravis€`FFTMepaÑìGDEF´ OS/2‹z(`cmapé5°òˆjgaspôglyf2‡÷üÄhead\Ã"À6hhea ‚áø$hmtxÙìlocaqµê4maxpÝ!D name<eš!d¸post2Í¿$¾webfŒRQ¸4ÜÌ=¢ÏËT‚0ÍÞ<цáŒ3†Œ3sZ3pyrs@ õÿ# dHN@  ©®´Æ / _!"""`àððð>ðNð^ðnð~ðŽðžð®ð²ðÎðÞðîðþñññ.ñ>ñNñ^ñnñ~ñŽõÿÿ  ¨®´Æ / _!"""`àððð!ð@ðPð`ðpð€ðð ð°ðÀðÐðàððñññ ñ0ñ@ñPñ`ñpñ€õÿÿÿãÿdÿ]ÿYÿTÿCà ßæß·ÞõÝúݹ ÿþýüûúùø÷ † ÿÿp7!!!àÀþ@p p úpú1]ÿ£€!2#!"&463!&54>3!2£+ýˆ@&&ü€&&@ýˆ+$(€($F#+ýˆý&4&&4&x+#ÿ€+".4>32".4>32467632DhgZghDDhg-iWýDhgZghDDhg-iW&@ (8 û 2N++NdN+'íý;2N++NdN+'Ç3 8ÿ€€!  #"'#"$&6$ €þùþŽþùrL46$þ©³Üþû½oo½½o|W%rþùþŽþùþ€4L&V|o½½oo½þûܳþ©%ÿ€=M%+".'&%&'3!26<.#!";2>767>7#!"&5463!2€ %þôž3@m00m@3žþô%  À  ú@ “ÁÐ:"7..7":ÐÁ6]€^Bú@B^^BÀB^ $΄+0110+„Î$ý (   ¨t˜¥1%%1¥˜+‘`ûÀB^^B@B^^ÿ€€"'.54632>32š4ý #LoP$$Po>àþåý‘€Z$_dŽCÜø+I@$$@I+øÜÝåý¨ÿ­€à"#"'%#"&547&547%62€þ•Vþ?þ?Vþ”8öá<áö8yþžþ   ìì ôb% IÇ))þ9I ÿ­€à + % %#"'%#"&547&547%62q2þZ½½þZ2IzyÇþ•V)þ?þ?Vþ”8öá<áö8)>~þ‚>þ×þ[ÇÇ þžþ  2 ìì ôb% IÇ))þ9I ÿ€€€'%#!"&54>322>32 &6 €’yü–y’ 6Fe= BS…†…SB =eF6 þÀáþÂáá>ƒx‹‹x5eud_C(+5++5+(C_dueçþÂáá>á ÿ€€/?O_oŸ¯54&+";2654&+";2654&+";264&#!"3!2654&+";2654&+";264&#!"3!2654&+";2654&+";2654&+";267#!"&5463!2€&€&&€&&€&&€&&€&&€&&ý&&&ü&€&&€&€&€&&€&þ€&ý&&&€&€&&€&&€&&€&&€&&€&€^BùÀB^^B@B^@€&&€&&š€&&€&&š€&&€&&ý&&þ&&š€&&€&&ûš€&&€&&&&þ&&þš€&&€&&š€&&€&&š€&&€&&ºúÀB^^B@B^^€€/?#!"&5463!2#!"&5463!2#!"&5463!2#!"&5463!2L4þ4LL44LL4þ4LL44L€L4þ4LL44LL4þ4LL44Lþ€4LL4€4LLÌþ€4LL4€4LLüÌþ€4LL4€4LLÌþ€4LL4€4LL €/?O_o#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!28(þÀ(88(@(88(þÀ(88(@(8€8(þÀ(88(@(8ý€8(þÀ(88(@(8€8(þÀ(88(@(8€8(þÀ(88(@(8ý€8(þÀ(88(@(8€8(þÀ(88(@(88(þÀ(88(@(8 À(88(À(88ØÀ(88(À(88ýØÀ(88(À(88ØÀ(88(À(88ýØÀ(88(À(88ýØÀ(88(À(88ØÀ(88(À(88ýØÀ(88(À(88ØÀ(88(À(88€/?O_#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!28(þÀ(88(@(88(þÀ(88(@(88(ü@(88(À(8û8(þÀ(88(@(88(ü@(88(À(88(ü@(88(À(8 À(88(À(88ØÀ(88(À(88ýØÀ(88(À(88ØÀ(88(À(88ýØÀ(88(À(88ØÀ(88(À(88y‡²"/&4?62 62‡ý,ˆPˆþ–ˆP&PˆòPý,ˆˆjPˆþÙ‘ˆnÿî’#$"' "/&47 &4?62 62 ˆPþÚþÚPˆ&þÚˆP&&PˆþÚ&þPˆ&þÚˆP&&PˆþÚ&ˆPþÚþÚÿ€€#+D++"&=#"&=46;546;232  #"'#"$&6$  à @ à  à @ à €þùþŽþùrK56$þ©³Üþû½oo½½o|Wà@ à  à @ à  àærþùþŽþùþµjK&V|o½½oo½þûܳþ©ÿ€€0#!"&=463!2  #"'#"$&6$  ýÀ  @ €þùþŽþùrK56$þ©³Üþû½oo½½o|Wà@  @ ærþùþŽþùþµjK&V|o½½oo½þûܳþ©ÿ€)5 $&54762>54&'.7>"&5462zÎþäþÈþäÎz¡’+i *bkQŠ½Ð½ŠQkb* j*’¡ý€LhLLhL€œþäÎzzΜ¶Bm +*i JÖyh½ŠQQнhyÖJ i*+ mþ¾Jý€4LL4€4LLÿ€€/?O%+"&=46;2%+"&546;2%+"&546;2+"&546;2+"&546;2ÀÀ€ÀÀ€ÀÀ€ÀÀ€ÀÀ`ÀÀrþÀ@òýÀ@rü@Àòú@Àÿ€€n4&"2#"/+"&/&'#"'&'&547>7&/.=46?67&'&547>3267676;27632–Ô––Ô– ¹#H  Š,/ Þ1)  ~'H·  º(C ‘ Š,/ Þ1)Ž  $H· Ô––Ô–mÞ 6%2X  %Ž lˆ2 ¸k r6 [21 Þ ..9Q $ kˆ2 ¸k w3 [20ÿ€€€/;Cg+"&546;2+"&546;2+"&546;2!3!2>!'&'!+#!"&5#"&=463!7>3!2!2@@@@@@€ü€@ý`À0 þà o`^BüÀB^`5FN(@(NF5 ýÀ@ýÀ@ýÀ@ý´üL%%Ju  •@üLSyuS¸@§%44%§f5#!!!"&5465 7#"' '&/&6762546;2€&þ€ÿþ€&??ß>  ýLýL > Ï X ôÀÛ  þ &€þ€&àÚþ&AJ Aý¿ J WÌÃþh¶ÿ€€#3!!"&5!!&'&'#!"&5463!2€þ`(8þ€x þÇ 8(ûÀ(88(€(`8(8( þ€ 9 þhü€(88(@(8(þÈ`ÿ€€ ,#!"&=46;46;2.  6 $$ €þÀà@ ’úþØú’’ú(úrÎþŸþ^þŸÎÎa¢aàþ@@`ýþ(ú’’úþØú’’_þ^þŸÎÎa¢aÎÎ2NC5.+";26#!26'.#!"3!"547>3!";26/.#!2W º  ô ö.ý@  þð  ý@.¡$S  ¦  S$¡@  þÀ þ9I   ÿ I6> ÀÀ ûì>€%=$4&"2$4&"2#!"&5463!2?!2"'&763!463!2!2&4&&4&&4&&4¦8(ú@(88(ч:œ:ˆÐ(8þ»þ@6þ@*&&*¦4&&4&&4&&4& þÀ(88(@(8ˆ88ˆ8)þ@À)'À&&þ@ÿ€€$0"'&76;46;232  >& $$ ` þÁ  þÀÀÀÀÌþØú’’ú(ú’’rÎþŸþ^þŸÎÎa¢a` þÁ @`þ 2’úþØú’’ú(ú½þ^þŸÎÎa¢aÎÎÿ€€$0++"&5#"&54762  >& $$ ^ÀÀÀ ?  @ÒþØú’’ú(ú’’rÎþŸþ^þŸÎÎa¢a”þ ` ? þÀù’úþØú’’ú(ú½þ^þŸÎÎa¢aÎÎ #!.'!!!%#!"&547>3!2ÿ<Ôý<Ô<_@`&ú€&î 5@5 î@ ðþ À¢þ&&â>=(""ýØ=ÿ€€'#"'&5476.  6 $$   ýà !  ’úþØú’’ú(úrÎþŸþ^þŸÎÎa¢a¥JþÀ %€%þÀË(ú’’úþØú’’_þ^þŸÎÎa¢aÎÎÿ€€3#!"'&?&#"3267672#"$&6$3276&þ@*Š”Éh½ŠQQнhwÔI ‰ mþʬœþäÎzzΜ“k‚)'þ@&('ЉQŠ½Ð½ŠQh_  Š „‘zÎ8Îzoeÿ€€$G!"$'"&5463!23267676;2#!"&4?&#"+"&=!2762ç@þhþî’þïk4&&À&‰G´a†èF *À &þ@&ДɆèF *Ç Aš’k‚4&àþôþ³nf&À&&4‰BH‚rdþ@&&4Љ‚rd  Moe&€/?O_o+"&=46;25+"&=46;25+"&=46;2#!"&=463!25#!"&=463!25#!"&=463!24&#!"3!26#!"&5463!2€ @  @  @  @  @  @ € ü@  À  ü@  À  ü@  À € ú@  À €^Bú@B^^BÀB^`@  @ ó@  @ ó@  @ ýó@  @ ó@  @ ó@  @ ý3@  üÀ MûÀB^^B@B^^€€!54&"#!"&546;54 32@–Ô–@8(ü@(88( p (8Àj––jþàýÀ(88(@(8À¸þø¸À8@ÿ€€7+"&5&5462#".#"#"&5476763232>32@@ @ @KjKÀך=}\‹IÀþð&:ì¹kº~&26]S &H&û  ò&H5KKuýt,4,’ &æ x:;*4*&€€K#+"&546;227654$ >3546;2+"&="&/&546$ €<¹‰X@@Gv"D°þ×þ²þ×°D"vG@@X‰¹<†à4L4à†Ц”1!Sk @ G< _b”œœþú”b_ 4.54632&4þ³þú&&M4&€UF &""""& F ûÀ&M&€&M&þ˜ƒ%.D.%ÿ¹€G-Ik"'!"&5463!62#"&54>4.54632#"&54767>4&'&'&54632#"&547>7676'&'.'&54632&4þ³þú&&M4&€UF &""""& FUªŒ &'8JSSJ8'& ŒªþÓ &'.${ŠŠ{$.'& Ó ûÀ&M&€&M&þ˜ƒ%.D.%7þÎþý;&'6£¸£6'&;¶þ4þ[&$ [2[ $&[ €€ #/37#5#5!#5!!!!!!!#5!#5!5##!35!!!€€€€€€ü€€þ€€þ€€þ€ÿý€€€€€€þ€€€€€ý€ý€€ý€€€€€€€€ü€€þ€€ý€ý€€þ€€€€þ€€þ€€€€ý€€ý€€€ #'+/37;?3#3#3#3#3#3#3#3#3#3#3#3#3#3#3#3#3???? ^>>~??????~??~??^??½^^? ^??€úúúúúúúúúúúúúúú€€ÿ•ë€4&"2#"'.5463!2ÀKjKKjv%þ'45%ý5&5L4 5€&Ë% jKKjKþ@5%þ%%Ì%€5 4L5&ý6'ÿ•k€54&"2#"'.5463!2#"&'654'.#32ÀKjKKjv%þ'45%ý5&5L4 5€&Ë%€%þ'4$.Ö%%ý5&€5à5€&Ë% jKKjKþ@5%þ%%Ì%€5 4L5&ý6'45%þ%Ö%54'Ê&55&ý6' ÿ€y€Tdt#!"&'&74676&7>7>76&7>7>76&7>7>76&7>7>63!2#!"3!2676'3!26?6&#!"3!26?6&#!"g(þísAüeM ,*$/ !'& ùJPþî$G]ü› x›6,&ûí `  ý  h `  ý  "9Hüv@WkNC<.  &k& ( "$p" . #u&#  %!' pJüvwEFÛ#  @  þÀ  @  ÿ—€2#"' #"'.546763Œ!''!0#þGþG$/!''!€ 8"ú÷"8  ¨þX! 8" "8 ÿ€€€ <)!!#"&=!4&"27+#!"&=#"&546;463!232€€ü€€ (8ý€€&4&&4¦ à8(ü@(8à qO@8( (`˜(@Oq€€8( ý&4&&4&@þ`  (88(   Oq (8(˜`(ÿqÿ€€!)2"&42#!"&546;7>3!2  Iî©©î©àj––jú€j––jà3e55e3ýgrþùþŽþù`©î©©îI–jü€j––j€j–ˆ1GG1ˆû€rþùþŽÿ€€€ Q37&'&#7676767;"'&#"4?6764/%2"%ÕªI¡M <5ý:YíK5 Íg'9') //8Pp]`O8È:ƒ8 /\þ>KM'Bþå0QÑþ>_’„ûþO 4hÔþ ò7f…:jCR1'  -! räAÑ@   ÿ€€€%e%3267654'&'&#"32654'&#"767676765'&'&'&'&/-72632;2/&+L@ƒª%&):SP§J+B¯²UT«4Nýä-M.   3T|-)JXg+59-,*@?|±Z\2BJI‚RtÅTÖ! RHForB^ ­þòÍ‚ŸpKK ,!zb+üe^  B€ñ”W  S  //rAFt/9)ij‚LU>7H$$ ÿ€€J767676?7>5?5&'&'7327>3"#"'&/&IL( 8  )g ='"B”!F76@% ,=&+ @7$ ~Æ)…J~U%@‹ @,Q5(?‡2&g  9,&ÇkþÉžë-   ÿ€ú€i…;?!6?2&'.'&'&"#"2#"'&#"#&5'56767676'&64&'&'&#"#&'52"/&6;#"&?62+Q6¿‚s×%"* ' Gˆ+"!  1( 8nMH¦X‘0:‹ &n+r  , Ð!~:~!PP!~:~!P5d: +UM6a'˜þ´þ™“.'  -   !& #Àñ¬>q\ 0f!)Vû%¢¢%%¢¢%üÿ†€h„;?!6?2&'.'&'&"#"52#"'&#"#&5'56767676''&'&'&#"#&'5&=!/&4?6!546Q6¿‚s¾>"* ' g®)^!  1( 8nMH¦R—-:‹ &n2í  , á¢%ü%¢¢%%5d: +UM6a'˜4þ™“.'  -    !& #‰(,  0f!)Vúó:~!PP!~:~!PP!€/?%#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2&ù€&&€&þ€&û&&&&ú&&&þ€&û€&&€&À€&&€&&f€&&€&&f€&&€&&f€&&€&&€/?%#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2&ù€&&€&þ€&ü€&&€&&ú€&&€&þ€&ý€&&€&À€&&€&&f€&&€&&f€&&€&&f€&&€&&€/?%#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2&ù€&&€&&û&&&&ú&&&&û€&&€&À€&&€&&f€&&€&&f€&&€&&f€&&€&&€/?%#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2&ù€&&€&&ù€&&€&&ù€&&€&&ù€&&€&À€&&€&&f€&&€&&f€&&€&&f€&&€&&€/?O_o%+"&=46;2+"&=46;2+"&=46;2#!"&=463!2+"&=46;2#!"&=463!2#!"&=463!2#!"&=463!2 À  À  À  À  À  À  úÀ  @ ú À  À  úÀ  @  úÀ  @  úÀ  @ àÀ  À sÀ  À sÀ  À üóÀ  À sÀ  À üóÀ  À sÀ  À sÀ  À €/?O#"'&47632#!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2€  þà   € ù@  À  ûÀ  @  ûÀ  @  ù@  À àýÀ     üóÀ  À sÀ  À sÀ  À sÀ  À €/?O#"&54632 #!"&=463!2#!"&=463!2#!"&=463!2#!"&=463!2` þà     © ù@  À  ûÀ  @  ûÀ  @  ù@  À Î þà  @  þàþ À  À sÀ  À sÀ  À sÀ  À #"'#!"&5463!2632' þm©wý@w©©wÀw©“ ' ûÀ*“¦w©©wÀw©©w¥’ÿ€€€."&462!5 !"3!2654&#!"&5463!2€p pp pú€@ ùÀ  @ “^BùÀB^^B@B^ pp pýÀþ@À@   û@  À  û@B^^BÀB^^ÿ€ëk%!7'34#"3276' !7632k[ë[€v ýâ 6 üÀþ`ë%¦þ`¦$65&ë%[ë[k€ ýâ Êþ`üÀ à5%¦ ¥&&ê'ÿ€€4&"2"&'&54 –Ô––Ô–!þ”?H?þ“!,¨,Ô––Ô–mFüú!&&!FmÔ,þÔÿ€€%" $$ ”ú’’ú”ÎþŸþ^þŸÎÎa¢a`@’úþØú’ñþ^þŸÎÎa¢aÎÎÀ-4'.'&"26% 547>7>2"KjKþÔþXþÔQqYn 243nYqQ€$!+!77!+!$5KKµÔþÔ,Ô‘‚ £‹Ù]""]Ù‹£ ø€9>H7'3&7#!"&5463!2'&#!"3!26=4?6 !762xt˜t` þ¢ ^Q©wüÀw©©w@?6 1üÀB^^B@B^ @(` ý`þà\\þà\P˜`t˜t8`À þ¢ ^ýϾw©©w@w© 1^BüÀB^^B~ @Íþàý` \ \˜P€+Z#!"&5463!12+"3!26=47676#"'&=# #"'.54>;547632€©wüÀw©©wÿ M8 pB^^B@B^íþ€ ' þ½sw- 9*##;Noеj ' €#þýw©©w@w© "^BüÀB^^BÖ  Üþ€*Àƒ‰þ°  "g`„81T`PSA:'À*þ€4€/D#!"&5463!2#"'&#!"3!26=4?632"'&4?62 62€©wüÀw©©w@?6 1 üÀB^^B@B^ @ çüÒBþRnB‡Bn^þÂw©©w@w© 1 ^BüÀB^^Bþ @ ÔüÒ®Bnþù‡nBÿC"&=!32"'&46;!"'&4762!#"&4762+!5462ÿ4&þ€€&ÿ4ÿ&€þ€&4ÿ4&€€&4&€€&4š4ÿ&€þ€&4ÿ4&€€&4&€€&4ÿ4&þ€€&ÿÿ€€6'&'+"&546;267Óý: &€&&€& s ú@ Æ ýZ&&€&&ýZ ÿ€€+6'&''&'+"&546;267667Óý: ý: &€&&€& Æ s ú@ Æ ý: Æ ýZ&&€&&ýZ Æ ý: zÿ€€€6'&''&47667Sý:ý:Æs ú@ Æ ý: Æ4Æ ý: ÿ|„ &546húÐ!!0aý À ý $ÿ€€#!"&5463!2#!"&5463!2&þ&&&ü€&þ&&&@ú€&&€&&ú€&&€&&ÿ€€#!"&5463!2&ú€&&€&@ú€&&€&&ÿ€€&54646&5-ÆÆý:s À ý: Æ ý:4ý: Æ ÿ€€+&5464646;2+"&5&5-ÆÆ&€&&€&ý:s À ý: Æ ý: ¦&&ú€&&¦ ý: Æ ÿ€€&54646;2+"&5-Æ&€&&€&s À ý: ¦&&ú€&&¦  62#!"&!"&5463!2Æ4Æ ú@ Æú€&&€&&-Æý:ýæ&&&ÿ&5ÿ¶ Ë&4762 "æýt%%Œ%k%K%%þæ%%K%k%‹%k%‹%%K%k%þþ&j%K%uÿµKË"/&547 &54?62K%ýt%j%L%%æþ%%L$l$Œ%À4'ýu%%K'45%æå'45%K&&ýu%ÿ€€#/54&#!4&+"!"3!;265!26 $$ À&ÿ&€&ÿ&&&€&&@ÎþŸþ^þŸÎÎa¢a@€&&&ÿ&€&ÿ&&&+þ^þŸÎÎa¢aÎÎÿ€€54&#!"3!26 $$ À&ý&&&@ÎþŸþ^þŸÎÎa¢a@€&&€&&+þ^þŸÎÎa¢aÎÎÿ€€+74/7654/&#"'&#"32?32?6 $$ }µµZµµZµµZµµZƒÎþŸþ^þŸÎÎa¢ažµµZµµZµµZµµZÎþ^þŸÎÎa¢aÎÎÿ€€#4/&"'&"327> $$ [4þhâ4[jüÎþŸþ^þŸÎÎa¢a"ZþiâZþ–Jþ^þŸÎÎa¢aÎÎÿ€€:F%54&+";264.#"32767632;265467>$ $$ €ÀÀo¦Wó€„  5!"40K(0?iÀ+! ":€ÎþŸþ^þŸÎÎa¢a ÀÀ®X–RÕd D4!&.uC$=1/J=þ^þŸÎÎa¢aÎÎÿ€€.:%54&+4&#!";#"3!2654&+";26 $$ `þÀ``À€ÀÀ€ÎþŸþ^þŸÎÎa¢a   þÀ Ž  Áþ^þŸÎÎa¢aÎÎÿ€€/_#"&=46;.'+"&=32+546;2>++"&=.'#"&=46;>7546;232­m&&m ¡l&€&l¡ m&&m ¡l&€&l¡s&%ë¡&€&¡ë%&&%ë¡&€&¡ë%&&€&l¡ m&&m ¡l&€&l¡ m&&m ¡,€&¡ë%&&%ë¡&€&¡ë%&&%ë¡&ÿ€€#/;"/"/&4?'&4?627626.  6 $$ I’  ‰‰  ’ ‰‰ ’  ‰‰  ’ ‰‰ Í’úþØú’’ú(úrÎþŸþ^þŸÎÎa¢aÉ’ ‰‰ ’  ‰‰  ’ ‰‰ ’  ‰‰ (ú’’úþØú’’_þ^þŸÎÎa¢aÎÎÿ€€ , "'&4?6262.  6 $$ “þZ4þÚf4“4fz’úþØú’’ú(úrÎþŸþ^þŸÎÎa¢aÓþZ&4f“f4ú(ú’’úþØú’’_þ^þŸÎÎa¢aÎÎÿ€… "4'32>&#" $&6$  Wý‰ oÉ’Vü󇥔ú’ zÍþãþÈþãÍzzÍ8̓¡†ýYW’˼ò[’ü”¢?þÆþâÎzzÎ:ÎzzÎ@ÿ5K #!#"'&547632!2A4ý@%&&K%54'ýu%%‹&54&K&&þÛÀ4A€€5KþÚ$l$L%%Œ%54'Š&&J&j&þÛKÿ5ÀK #"/&47!"&=463!&4?632À%ýu'43'K&&%ý@4AA4ÀþÛ&&K&45&‹%@6%ýu%%K&j&%K5€5K&$l$K&&ýu#5ÿ€K@!#"'+"&5"/&547632K%K&56$þÚK5€5KþÚ$l$K&&‹#76%‹%53'K&&%ý@4AA4ÀþÛ&&K&45&‹%%ýu'5ÿµK€"#"'&54?63246;2632K%ýu'45%ýu&&J'45%&L4€4L&%54'K%À5%ýt%%Œ$65&K%%þÚÀ4LL4ý@&%%K'ÿ€À,"&5#"#"'.'547!3462þ4&àb›™qb>#  5¢Éà&4š4þ& 6Uu e7D#  "¦Ç†“&þÿ€€/#!"&546262"/"/&47'&463!2ó þ´&þ@&&4L  r&4þ´  r L&À&í þ´4&&À&L rIþ@&þ´ r  L4&& ÿós/"/"/&47'&463!2#!"&546262&4þ´  r L&À&ó þ´&þ@&&4L  r@þ@&þ´ r  L4&&“ þ´4&&À&L r€€##!+"&5!"&=463!46;2!2€8(þ`8(À(8þ`(88( 8(À(8 (8 À(8þ`(88( 8(À(8 (88(þ`8€€#!"&=463!2€8(û@(88(À(8 À(88(À(88zÿ€€5'%+"&5&/&67-.?>46;2%6Ê.@g.þöL4€4Lþö.g@. þö.@g. L4€4L .g@.þöæg.n.™þÍ4LL43™.n.gššg.n.™34LL4þÍ™.n.gšÿ€€ -  $54&+";264'&+";26/¢aÎÎþŸþ^þŸÎβ À  À  Ü ¹€ÎþŸþ^þŸÎÎa¢aûï¾ ¾ fm  ý“ @ J%55!;263'&#"$4&#"32+#!"&5#"&5463!"&46327632#!2 þÀ$À$þ8Ã~+(88Ø8(+}Â(°`8(ûÀ(8`¸]ƒƒ]k=€€=k]ƒƒ]¸´8ÔÀÀþ,8e¡8P88P8¡ þÀþ`(88( @ƒºƒM¥¥Mƒºƒ€O4&#"327>76$32#"'.#"#".'.54>54&'&54>7>7>32&¬þÜãz&^‰¶&.þëÛÖà”Š’/+>*>J> W—¾m7´³²•' '"''? &4&c©‡&^|h_bàþÂml/J@L@ #M6:D 35sÒŸw$ '% ' \„tÿ€3#!"&=463!2'.54>54''€ úÀ  @ ÿ1O``O1CZŒ‰Z71O``O1BZŒ‰Z7 @  @ N„]SHH[3`€)Tt¬bN„]SHH[3^‚)Tt¬€!1&' 547 $4&#"2654632 '&476 €˜å=þùþŽþù=嘅‘Ô‘ýµ}³(zVlŒþ'ýòþ'ŒŒÙÙŒ@ìuhy¹þù¹yhuìÍóó9(³}VzþÒD#æþëå#D#åþêåÿ à =CU%7.5474&#"2654632%#"'&547.'&476!27632#76$7&'7+NWb=嘧‰}³(zV‡iþ\j1  z,ñX™Æ Y[6 $!%ž‚À'F–þuÞJÔiys§?_¯9É?Àkyhuìþþn(³}Vzï¼ý ½YF  KA؉Lëa  þ0‹å2ö-„F"@Q¬¾„î¼³sp@²_ÿ€ð!3%54&+";264'&+";26#!"&'&7>2 À  À  Ü ¹ #%;"ú";%#`,@L¡ € þéþý5 `  ½ü  ` Âþ €™ LÀ`4ÀL¡ýH` üþýÂ`  ü½ a 5 € Ÿ L@ÿ€ #37;?Os!!!!%!!!!%!!!!!!!!%!!4&+";26!!%!!!!74&+";26%#!"&546;546;2!546;232€ þà`@þÀþ  þà`@þÀþ  þàà@þÀþ€@þÀ þàþ€@þÀþ  @  @ à þàþ€@þÀ€ þà  @  @ €L4ú€4LL4€^B@B^€^B@B^€4L€ þà @@þÀ@@ ü À ü @@   þà ü­@@ þà À  þà Mû4LL44L`B^^B``B^^B`Lÿ à7q.+"&=46;2#"&=".'673!54632#"&=!"+"&=46;2>767>3!54632š7>7&54>$32ðþdôFKÆþú1A  0) µŽðL¶ôœ.þ¤þÙ«¯C58.H(Y–‚í¬e«ÿ€€#3C $=463!22>=463!2#!"&5463!2#!"&5463!2Åþ¡þHþ¡Å&€&/7#"&463!2!2€KjKKjËKjKKjË ûì˜&&ü&%±Ì&& ±&5jKKjKKjKKjKÀþ%z 0&4&&3D7&4& %&€€#!"&5463!2!2€„\û@\„„\@\„ \„ ý@\„„\À\„„\ „W€*#!"&547>3!2!"4&5463!2!2Wþ°+›BûÀ"5P+›B@"5þ©üÀ^Î=þ¯„\@\„ \„H#þt3G#Œ3G: _HþtÀ\„„\ „@ÿÀ+32"'&46;#"&4762À&€€&ÿ4ÿ&€€&4Ú4&ü&4ÿ4&&4ÿ@À"&=!"'&4762!5462ÿ4&ü&4ÿ4&&4š4ÿ&€€&4&€€&ÿÿ€€€ /!!!!4&#!"3!26#!"&5463!2ÿ€ÿ€ÿ€ÿ€ ùÀ  @ €^BùÀB^^B@B^þ€€ü€€ÿý€€€üû À  û@ Íû@B^^BÀB^^ÿ€€0@67&#".'&'#"'#"'32>54'6#!"&5463!28ADAE=\W{âO[/5dI kDt‘”®pÄŒe1?*©wü@w©©wÀw©ž (M& B{Wta28r=Ku?RZ^Gw›©T -ü@w©©wÀw©©ÿ€€#7#546;5#"#3!#!"&5463!2Æ8n¯˜„ƒƒ”©wü@w©©wÀw©jÛm1'ÛƒÛý…{öü@w©©wÀw©©ÿ€€#'.>4&#"26546326"&462!5!&  !5!!=!!%#!"&5463!2 B^8(ò–Ô––Ôü–ú€áþÂáá>üá€þ€€üÄ@ý|€K5ú5KK55K²^B(8Ô––Ô–ü€>ááþÂá€þÀvŠ€€û5KK55KKÿH“€G4&"&#"2654'32#".'#"'#"&54$327.54632@p p)*Ppp p)*PÃb '"+`ÜN*(ýa°½£Í¾2 £Íƒc`." b PTY9° ppP*)p ppP*)þb ".`Ü(*NŸƒÍ£ 2¾Í£½°þ`+"' b MRZBÿ€ð½û4&"24&"264&"26#"/+"&/&'#"'&547>7&/.=46?67&'&547>3267676;27632#"&'"'#"'&547&'&=4767&547>32626?2#"&'"'#"'&547&'&=4767&547>32626?2€–Ô––Ô–LhLKjKLhLKjKþ€ › "8w s%( º ")v  > ˜  › "8x s"+ º ")v  < ˜ €• 3zLLz3 •• 3>8L3)x3 •• 3zLLz3 •• 3>8L3)x3 •Ô––Ô–ÿ4LL45KK54LL45KKþ¹ #)0C wZ l/ ™ Y… N,& ¹ #)0C vZl. ™ Y… L0"ýàŒqG^^GqŒq$ ]G)FqðŒqG^^GqŒq$ ]G)Fqÿ€%O#"'#"&'&4>7>7.546$ '&'&'# '32$7>54'€¼þ»¿VZ|š$2 $ |޼E~E<Ž| $ 2$š|ZVþñÉ:¡(t}†–‹þêì‰X(  &%(HÒw‹ì‰‰ý‰xÑH(%& (X„ZT\ð†MKGÖÿ€<m$4&"24&#!4654&#+32;254'>4'654&'>7+"&'&#!"&5463!6767>763232&4&&4¦N2þ `@`%)7&,$)' %/0ÓƒyÀ#5 +€1 &<¬$]`»{tþà5KK5$e:1&+'3T†F0°h˜¦4&&4&€3M:Ë;b^v…+D2 5#$ý€I§IJ 2E=\$YJ!$MCeM‹¡-+(K5€5KƒK5y„*%AŠu]c˜ÿ€=p4&"24&'>54'64&'654&+"+322654&5!267+#"'.'&'&'!"&5463!27>;2&4&&4¦+ 5#bW€ƒÓ0/% ')$,&7)%`@``2N€˜h°0##†T3'"( 0;e$þî5KK5 t€¾ipŒ­<& 1&4&&4&þ#\=E2 JIURIý€$#5 2D+…v^b;Ë:M2g˜c]vDEA%!bSV2MƒK5€5K(,,ž‰MeCM$!Jÿ­@à#"&547&547%6@þ?Vþ”8öáàúÅì ôb% IÇ)ÿ€€94.""'." 67"'.54632>32€+C`\hxeH>Hexh\`C+»ED¼€åý‘4ý #LoP$$Po>àþ¬Q|I.3MCCM3.I|Q¨»ýÐ/¼¨Ýåý¨Z$_dŽCÜø+I@$$@I+ø (@%#!"&5463!2#!"3!:"&5!"&5463!462€ þÀw©©w@  þÀB^^B   ýà4&þ@&&À&4 `  ©wÀw©   ^Bý@B^ 24ýà& &€& &ýàÿ€€%573#7.";2634&#"35#347>32#!"&5463!2íççöFtIG9;HIç’xˆIçç<,tÔ©wü@w©©wÀw©z¶Ö4DD43EEü§ŽšžueBýŒ„&#1sü@w©©wÀw©©ÿ€€ .4&"26#!+"'!"&5463"&463!2#2à &þS3 Lþl&c4LL4€4LL4c Àþ@þ®&þ å&{ÅLhLLhLþÅ'?#!"&5463!2#!"3!26546;2"/"/&47'&463!2€©wüÀw©©wÀý@B^^B@B^@€&4°ýt  r Œ°&&`þÀw©©w@w©@^BüÀB^^B@Rþ&°ýt r  Œ°4&&@"&5!"&5463!462 #!"&54&>3!2654&#!*.54&>3!2 ýà4&þ@&&À&4 s©wþÀ  @B^^Bþà  @w©š4ýà& &€& &ýà3ý@w©   ^BÀB^   ©ÿ€€€ I&5!%5!>732#!"&=4632654&'&'.=463!5463!2!2ÊJÿ½ÃÿJ½€SÍq*5&=CKuüÀuKC=&5*qÍS8( ^B@B^ (8¢Ñ`N¨ö`Ñ¢¨Î€GtO6)"M36J[E@@E[J63M")6OtG€(8`B^^B`8ÿ€€%-3%'&76'&76''&76'&76'&6#5436&76+".=4'>54'6'&&"."&'./"?+"&5463!2Š  2  5    z<: Æ©wà 49[aA)O%-j'&]Æ]5r,%O)@a[9( 0BA; + >HCàw©©wÀw©¸  5 /)  u    ±ü@w©ïa-6OƒUyU[q ( - q[UyU‚P6$C +) (  8&/ &‚©wÀw©©ÿ€€À'?$4&"2$4&"2#!"&5463!3!267!2#!#!"&5!"'&762&4&&4&&4&&4¦8(ú@(88(«c==c«(8þ»*ÿ&ÿ&ÿ*À6À&4&&4&&4&&4& þÀ(88(@(88HH88`(þ@&&À('Àþ@ÿ€ÿ€1d4&'.54654'&#"#"&#"32632327>7#"&#"#"&54654&54>76763232632   N<è;+gC8‰A`1a9á9µgÕw€Œü›|Ê9â8aIe$I€VNšÂz<ç:LQJ  Æ,‹-[% 061Iéï( )W,$-׋¥þ»û7,oIX(¡)oÕζA;=N0 eTZ  (€€O#".'&'&'&'.54767>3232>32€ e^\3@P bM€þïO0# 382W# & 9C9 Lĉ" 82<*9FF(W283 #0O€Mb P@3\^e FF9*<28 "‰ÄL 9C9 & #€€!"3!2654&#!"&5463!2`üÀB^^B@B^^Þ©wüÀw©©w@w©^BüÀB^^B@B^ üÀw©©w@w©©ÿ—€#!72#"' #"'.546763€ü§YY§ !''!0#þGþG$/!''!û&–UUþjZ 8"ú÷"8  ¨þX! 8" "8 ÿ€€EU4'./.#"#".'.'.54>54.'.#"32676#!"&5463!2G55 :8 c7 )1)  05.D <9¤0)$9“©wü@w©©wÀw©W + AB 7c  )$+ -.1 “9$)0¤þÇ< D.59ü@w©©wÀw©©,T1# '327.'327.=.547&54632676TC_L›ÖþÒ¬þñá#+á°i¦!+*p“DNBN,y[ƽ†Œ`m`%i]hbE‚þýî·m‘Š}a ±u&,ŽSXK•³ &$†½f9s? ÿ€ð!#!#3546;#"ÿãþ«ªª¬ÅãŽ'/ÔþäüÈ8«¶»þä "# ÿ§€R&=4'>54'6'&&"."&'./"?'&54$ þÛè49[aA)O%-j'&]Æ]5r,%O)@a[9( 0BA; + >HCèþÛÎa¢a΀ûþoMÓa-6OƒUyU[q ( - q[UyU‚P6$C +) (  8&/ &fM‘ûÑaÎÎþŸ€€%+"&54&"32#!"&5463!54 €&@&–Ô–`(88(ü@(88( rÀÿ&&j––jÀ8(ýÀ(88(@(8À¹þùÿ€€€#'+2#!"&5463"!54&#265!375!35!àB^^BùÀB^^B € ù€ `€€€^Bû@B^^BÀB^€ àà û `ý  €€€€€€€!="&462+"&'&'.=476;+"&'&$'.=476; €p pp p‡$þ»å! $qr‡ % ²þãþ}×#ߺ»Ö pp pþÅ!åE$‡ ‡rqþÜ¢#׃² % Ö»ºþ!)?"&462"&4624&#!"3!26!.#!"#!"&547>3!2/B//B//B//BŸ û@  À û2œüò±^Bû@B^Å\77\ÅaB//B//B//B/ð@  þÀ íâ  ý~þÀB^^B@2^5BB5ý¢2ÿƒ€.42##%&'.67#"&=463! 2€5KK5L4þ_þu:B&1/&¥¬.- zB^^Bà³Í4L€þvþŠy€KjKþ€4L[!^k'!A3;):2*54&#"+323254'>4'654&'!267+#"'&#!"&5463!2>767>32!2&4&&4¦N2ýÀ$YGB (HGEG H¾ÅQ½#5K4L€—i©!<¬…½¤;þà5KK5 A# ("/?&}£vh˜¦4&&4&€3M95S+C=‹,@QQ9ý€@@§IJ 2E=L5i˜>9eM‹¡E;K5€5K J7R>@#†zD<˜ÿ€€7?s%3#".'.'&'&'.#"!"3!32>$4&"2#!"#"&?&547&'#"&5463!&546323!2` #A<(H(GY$ýÀ2NL4K5#aWTƾh&4&&4¦K5þà;¤¾ް=!©i—˜hv£}&?/"( #A  5K€€2*!Q@.'!&=C+S59M34L=E2 JI UR@@&4&&4&€ý€5K;E›ŒLf9>˜ig˜R7J Kÿ5h4&"24#"."&#"4&#"".#"!54>7#!"&54.'&'.5463246326326&4&&4¦§IJ 2E=L43M95S+C=‹,@QQ9€@@€E;K5ý€5K J7R>@#†zD<˜gi˜>9eM‹¡Z4&&4&<½#5K4LN2ýÀ$YGB (HGEG H¾ÅV…½¤;þà5KK5 A# ("/?&}£vh˜—i©!<¬ÿ4<p4.=!32>332653272673264&"2/#"'#"&5#"&54>767>5463!2€@@ý€2*! Q@.'!&=C+S59M34L.9E2 JI UR€&4&&4&›ŒLf6A˜ig˜6Jy‡#@>R7J K5€5K;E@TƾH #A<(H(GY$ýÀ2NL4K#5#a=4&&4&ýDް=©i—˜hv£}&?/"( #A  5KK5þà;¤¾ÿ€€+54&#!764/&"2?64/!26 $$ &þ ½[6þ–[[j6[½ö&ÎþŸþ^þŸÎÎa¢a@€&½4[þ–[6[þ–[6½&+þ^þŸÎÎa¢aÎÎÿ€€+4/&"!"3!277$ $$ [þ–6[½þ &&ö½[6j[ ÎþŸþ^þŸÎÎa¢ae6[j[6½&€&½4[j[þþ^þŸÎÎa¢aÎÎÿ€€+4''&"2?;2652?$ $$ þ–[6[þ–[6½&€&½4[ÎþŸþ^þŸÎÎa¢af6j[[þ–6[½þ &&ö½[ýþ^þŸÎÎa¢aÎÎÿ€€+4/&"4&+"'&"2? $$ [6½&€&½4[j[6[jÎþŸþ^þŸÎÎa¢ad6[½ö&&þ ½[6þ–[[jÿþ^þŸÎÎa¢aÎÎÿ€€ Ø  $2>767676&67>?&'4&'.'.'."#&6'&6&'3.'.&'&'&&'&6'&>567>#7>7636''&'&&'.'"6&'6'..'/"&'&76.'7>767&.'"76.7"7"#76'&'.'2#22676767765'4.6326&'.'&'"'>7>&&'.54>'>7>67&'&#674&7767>&/45'.67>76'27".#6'>776'>7647>?6#76'6&'676'&67.'&'6.'.#&'.&6'&.5/¢aÎÎþŸþ^þŸÎÎD&"      4   $!   #          .0"’Y +  !       $     "  +       ½Î‘      €ÎþŸþ^þŸÎÎa¢aþÅ                        PŽ   ' -( # * $  "  !     * !   (         ü‚$™      2 ÿ~€/$4&"2 #"/&547#"32>32€&4&&4ªýV%54'j&&©'—Ü/ë¹þù¹:,þÛÁ”{ &4&&4&äýV%%l$65&©b—Œ'C†§r! " ©àk[G€ +;%!5!!5!!5!#!"&5463!2#!"&5463!2#!"&5463!2€ý€þ€ü€€þ€&ù€&&€&&ù€&&€&&ù€&&€&€€€€€€ü@ÿ&&&&æÿ&&&&æÿ&&&&ÿ€{#"'&5&763!2{þ' ÿþ**Ù)þý*æí)'/!5!#!"&5!3!26=#!5!463!5463!2!2€þ€^Bú@B^ &@&`ÿù^B`8(@(8`B^€ýþ B^^Bà && €€àþ€€B^ (88( ^ÿ€€G 76#!"'&? #!"&5476 #"'&5463!2 '&763!2#"'þc)'&þ@*þþ*þ@&('cþ (&À*cc*À&' ãþþ*þ@&('cþ'(&À*cc*À&('þc'(&þ@*ÿ€19AS[#"&532327#!"&54>322>32"&462 &6 +&'654'32>32"&462Q¢g†Rp|Kx;CB€’yü–y’ 6Fe= BP†ˆ†PB =eF6 ü–Ô––ÔVáþÂáá>!pR†g¢QBC;xK|€–Ô––Ô€{QNa*+%‹ýx‹‹x5eud_C(+5++5+(C_due2Ô––Ô–þþÂáá>áýŸNQ{u‹%+*jÔ––Ô–ÿpð!Ci4/&#"#".'32?64/&#"327.546326#"/&547'#"/&4?632632°Ð(* 8( !Î)(“ýAÎ('“Ð)* 8( !U“SxySÎSXXVzxTÐTU“SxySÎSXXVzxTÐ@(Ð  (8 *(Ï’è(Ï’'(Ð (8 ýáðS’SUÏSx{VXXTÐTðS’SUÏSx{VXXTЀ€#!"5467&5432632€áŸûÀ¹þùŽt,Ôž;F`j–)¨€Ÿá¹„Û6Ô,°Ž>–jK?Ñsÿ€ €!%#!"&7#"&463!2+!'5#÷8Ejû€jE8÷@&&&&@þìþðÈþð€XYY&4&&4&þqDþS­%þq%ÿ€N\jx†Œ2"&4#"'#"'&7>76326?'&'#"'.'&676326326&'&#"32>'&#"3254?''7¦4&&4&lû€ ýNnbS„‘ˆVZ bR„’SD zz DS’„Rb)+U‰‘„Sbn² €û\.2Q\dJ'.2Q\dJ.Q2.'Jd\Q2.'Jd`!O×`à€ý  `€ýø± €&4&&4þr$#@ƒB10M5TNT{LŽ5T II T5ŽL;l'OT4ŽM01Bƒ@#$Š*„3;$*„3;ý;3„*$;3„*$ : $/é @@þQq`þÀ@˜Šÿ"%3<2#!"&5!"&5467>3!263! !!#!!46!#! (88(ü@(8ýà(8(˜`( (8D<€þÕ+ý€þÕ+Ä<þ€8(þ`(Øþ€8(þ`€8(û@(88( 8( (`˜(8(þ¸(ÕþÕ«þÕþ¤< þ`(8ý€(`üø€þ`(8ý€ÿ„||?%#"'&54632#"'&#"32654'&#"#"'&54632|žu‡dü÷qÜŸžs] = ý¢Ofj’L?R@T?ý»"&š > þf?rRX=Ed—uždsœŸÞqý¢ = _M–jiLü÷?T@R?E& þf > š=XRr?ý»bÿ€€!1E)!34&'.##!"&5#3463!24&+";26#!"&5463!2€ý€€ þç 08(ýÀ(8€€8(@(8þ€ À  À €8(úÀ(88( (`(€þ€€1  þ`(88( û (88(@  þÀ ü`(88(@(8(þè`ÿ€€#!"&5463!2©wü@w©©wÀw©`ü@w©©wÀw©©/%#!"&=463!2#!"&=463!2#!"&=463!2&ú€&&€&&ú€&&€&&ú€&&€&À€&&€&&æ€&&€&&æ€&&€&&ÿÀ@'7G$"&462"&462#!"&=463!2"&462#!"&=463!2#!"&=463!2€p pp pp pp ð û@  À ú€p pp ð û@  À  û@  À Рpp p pp pý À  À ã pp pý À  À óÀ  À ÿ÷<L\l|#"'732654'>75"##5!!&54>54&#"'>3235#!"&=463!2!5346=#'73#!"&=463!2#!"&=463!2}mQjB919+i1$AjM_3<þ–/BB/.#U_:IdDREê û@ À ú€þ±k*Gˆjì û@ À  û@  À TP\BX-@8 C)5˜Xs J@Ÿ$3T4+,:;39SG2S.7<þÁÀ  Àvcc)¢( %Lþlþ}À  ÀóÀ  À ÿ€€5e2#!"&=463%&'&5476!2/&'&#"!#"/&'&=4'&?5732767654'&àù@Ã0†…2uBo  T25XzrDCBBÕEh:%ì›)0%HPIP{rQŒ9f#-+>;I@KM-/Q"€@@@#-a[µ€ $&P{<•8[;:XICC>.ÿ'5oe71#.0(  l0&%,"J&9%$<=DTIÿ€€cs&/6323276727#"327676767654./&'&'737#"'&'&'&54'&54&#!"3!260% <4„"VRt8<@< -#=XYhW8+0$"+dTÍLx-'I&JKkm’§uw<=Vú@À!X@ v 'åþè|N;!/!$8:IœOb“V;C#V  &   ( þ‡ÃmL.A:9 !./KLwPM¼$ú‚@@ €€/?O_oŸ%54&#!"3!2654&#!"3!2654&#!"3!2654&#!"3!2654&#!"3!2654&#!"3!2654&#!"3!2654&#!"3!2654&#!"3!26#!"&5463!2þÀ@þÀ@þÀ@þþÀ@þÀ@þÀ@þþÀ@þÀ@þÀ@€^BúÀB^^B@B^ ÀÀŽÀÀþŽÀÀÀÀþŽÀÀþŽÀÀÀÀþŽÀÀŽÀÀNûÀB^^B@B^^ÿ›€#+3 '$"/&4762%/?/?/?/?¦%kþÛ*úú6Æ6ÆúËbbbb|ÄÄ<<ÄÄ<Þbbbbýžbbbb»%kþÛÕ6úúÆ6Æ‘bbbþü<<ÄÄ<<Äý^bbbbbb@ÿ€M$4&"2!#"4&"2&#"&5!"&5#".54634&>?>;5463!2€LhLLhþÌ€ž à LhLLhL! '–Ô–þ€–Ô–@' !&  Æ? &&LhLLhL€ à ý®hLLhLÀü j––jj––j &@6/" ÆÀ&&ÿ€€J#"'676732>54.#"7>76'&54632#"&7>54&#"&54$ ÎþŸÑok; -j=y¾hwâŽi¶[+PM 3Ñ©—©‰k=J%62>VcÎþÎa¢aQþ^þŸÎ ]G"±'9‰ð–rÈ~:`}†Chž 0=Z—Ù¤ƒªîW=#uY2BrUI1þ^Fk[|éÑaÎÎÿ€€L2#!67673254.#"67676'&54632#"&7>54&#"#"&5463àw©©wý+U ,i<µåF{¶jhµ}Z+OM  2ϧ•§‡jX–Õ¢¨ìW<"uW1AqSH1þbdš©wÀw©ÿ€€"3g!"&'>32 327#".54632%#!654.54>4&'.'37!"463!2!#!!3¦ þ„_™Znh7 1-$ þÛê—gª &ìWa3\@0g]Bj> Ò©wþ,',CMC,.BA.51 ‡‡þKŠÕL¢~Àw©ÿ€ÿ€9&!q[-A"" ""$!'JN±v=C­dy4Shh/`ŠR~†ý w©ITBqIE2;$@;Ft’‘.  @M_~®©w`ÿ€ÿÿ€€-co%4.'&#"32>4.#"326!#!".547>7&54>7#"&54676!#!5!3l $-1!6hpT6Gs~@;k^7x!=kB]f0@\3aWƒµ‡‡GN.BB.!5@@5!Œ˜þô;y{^<% €¢”L@ (•Õ¾‹^lÿ€ÿ€G'!$"" "$8^2"&5!#2!46#!"&5463!2€€r”M* €*M~–~M**M~–~M*j–û€–jj–€–ê&ù&&&€`À‰P%þàæŒ|NN|Œ|NN|þ*–jj–þ–jj–@û€&&€&&@€ "'&463!2þ@4þ@&€Z4þ@À4&@ #!"&4762&ü€&À4ÀZ4&&4Àþ@@€€€ "'&4762€&4þ@À4&@ü€&À4À&€@€ "&5462@þ@4&&4Àš4þ@&€&þ@ÿ€€€ 3!!%!!26#!"&5463!2 `ý€mý€` €^BúÀB^^B@B^€û   `û€Íû@B^^BÀB^^ÿÀ@ "'&463!2#!"&4762þ@4þ@&€&&ü€&À4ÀÚ4þ@À4&Z4&&4Àþ@ÿÀ "'&463!2þ@4þ@&€Ú4þ@À4&@ #!"&4762&ü€&À4ÀZ4&&4Àþ@ÿ€:#!"&5;2>76%6+".'&$'.5463!2^Bú@B^,9j‡9Gv33vG9ªH9+bIþˆ\ A+=66=+A [þª">nSMÀA_:üæB^^B1&öc*/11/*{Þ'VO3þû@/$$/@í*“?Nh^ÿ°l+!+"&5462!4&#"!/!#>32]þ¶_gTRdg¦dþ·QV?U þ·I*Gg?«Ðü!ß2IbbIJaaüÝýÈiwE33ý×ð00 08ãÿ€€4#"$'&6?6332>4.#"#!"&54766$32zÎþ䜬þÊm‰ IÔwh½ŠQQнhb´F‰*þ@&('‚k“œÎþÈþäÎz‘„ Š  _hQŠ½Ð½ŠQGBŠ'(&À*eozÎ(ÿëØq!#"'&547"'#"'&54>7632&4762.547>32#".'632ë%k'45%þ•&+ÿ~(  (þh  &  \(  (˜  &  ~+54'k%5%l%%l$65+~  &  ˜(  (\  &  þh(  (~ÿ+%þ•'ÿ€!)19K4&"24&"26.676&$4&"24&"24&"2#!"'&46$ €KjKKj KjKKj÷e2.e<^PšŠ,bKjKKjýËKjKKj KjKKj‹#ú†#ŽðLlLðŽKjKKjK jKKjKþŸ~-þ‚M7>7&54$ LþhþÑ‚W.˜{+9E=ÌcÑÑQðþdôFKÆþú1A  0) µðœèœ€‹ì‰pËJ2`[Q?l&‹ììÇþ¤þÙ«¯C58.H(Y–®'««ÿ€:d 6?32$64&$ #"'#"&'&4>7>7.546'&'&'# '32$7>54'YþÎþöj`a#",5NK™ ýž~E¼¼þ»¿VZ|š$2 $ |޼: $ 2$š|ZVþñÉ:¡(t}†–Ž€h²fR˜88T h²Ì²è‰ìþêì‰X(  &%(HÒw‹ìûø(%& (X„ZT\ð†MKGÖ{xÑÿ|€!#"'.7#"'&7>3!2%632u ýä  Åþj ÉH«ŒÊû{(e 9 þ1bÿ€€U#!"&546;5!32#!"&546;5!32#!"&546;5463!5#"&5463!2+!2328(þÀ(88(`þ`(88(þÀ(88(`þ`(88(þÀ(88(`L4`(88(@(88(`4L`(8 þÀ(88(@(8ÀÀ8(þÀ(88(@(8ÀÀ8(þÀ(88(@(8À4LÀ8(@(88(þÀ(8ÀL4À8ÿ€€ÀOY"&546226562#"'.#"#"'.'."#"'.'.#"#"&5476$32&"5462€˜Ð˜&4&NdN!>! 1X:Dx+  +wˆw+  +xD:X1 -ÿU¾Œ à¥!ý*,*&4&Äý¼h˜˜h&&2NN2D &  ..J< $$ 767#"&'"&547&547&547.'&54>2àl4  2cK Eo‡Š‡oED ) € ä € ) D€g-;</- ?.P^P.? -/<;-gY‘·¾·‘YÀ  .2 L4H|O--O|HeO , ™‘‘™ , Oe›q1Ls26%%4.2,44,2.4%%62sL1q›c«qAAq«ÿ à4#!#"'&547632!2#"&=!"&=463!54632 ú  þÁ @  `  þÀ  ú   ` ?`À À  @  @  À! þÀ  À À À þÁ€€54&+4&+"#"276#!"5467&5432632à À à  `  _ €áŸûÀ¹þùŒv,Ôœ;G_j–)‚§``  þ   þ  _ ԟṂÜ7 Ô,®>–jL>Ñ€€54'&";;265326#!"5467&5432632 þ   þ¡ à À à €áŸûÀ¹þùŒv,Ôœ;G_j–)‚§  ` þ¡ þ   `þíŸá¹‚Ü7 Ô,®>–jL>Ñÿ€€€X`$"&462#!"&54>72654&'547 7"2654'54622654'54&'46.' &6 €&4&&4&’yü–y’ %:hD:Fp pG9„F„j– 8P8 LhL 8P8 E; Dh:% þÀáþÂáá>Ú4&&4&}yŠŠyD~–s[4DËd=PppP=dË>hh>@–jY*(88(*Y4LL4Y*(88(*YDw" A4*[s–~ØþÂáá>áÿ€€€M4&"27 $=.54632>32#"' 65#"&4632632 65.5462&4&&4¦G9þùþŽþù¤Ü& <#5KK5!¼¼!5KK5#< &ܤ¼¼9Gp p&4&&4&@>bþuŸáោØ&$KjKþnj––j’KjK$&þØ„j––j‹b>Pppÿ€€ %!5!#"&5463!!35463!2+32€þþ @\„„\ ü€8(@(8„\@@\„€€€û„\@\„û (88( àüÀ\„„ÿ€ -4#"&54"3#!"&5!"&56467&5462P;U gI@L4þ@–Ô–þ@4L¾ÂÀ¨8P8¨À° U;Ig04Lj––jL4¡Ù¥Â(88(Â¥þúþ'ÿ€@"4&+32!#!"&+#!"&5463!2€pP@@Pùð–jûj–@áŸ@„\ý@\„&€Ÿ0 pþ€ýÀj–– þÂá \„„\à&ÿ€-B+"&5.5462265462265462+"&5#"&5463!2€G9L4€4L9G&4&&4&&4&&4&&4&L4€4Là ¼„&Àý€=düõ4LL4 d=€&&þ`&& &&þ`&& &&ùÀ4LL4  „¼&ÿ€€(/C#!"&=463!25#!"&=463!2!!"&5!!&'&'#!"&5463!2ý@Àý@Àü€þ`(8þ€x þÇ 8(ûÀ(88(€(`8(`@@ò@@ý’8( þ€ 9 þhü€(88(@(8(þÈ`ÿ€/?O_oŸ¯¿Ïßïÿ-=%+"&=46;25+"&=46;2+"&=46;2%+"&=46;2+"&=46;2%+"&=46;2%+"&=46;2%+"&=46;2+"&=46;2%+"&=46;2%+"&=46;2%+"&=46;2+"&=46;2%+"&=46;2%+"&=46;2+"&=46;2%+"&=46;2+"&=46;2!!!5463!2#!"&5463!2€ @  @  @  @  @  @ ÿ @  @  @  @ ÿ @  @ ÿ @  @ ÿ @  @  @  @ ÿ @  @ ÿ @  @ ÿ @  @  @  @ ÿ @  @ ÿ @  @  @  @ ÿ @  @  @  @ ÿ€û€€ @ &û&&&à@  @ ó@  @  @  @ ó@  @ ýó@  @ ó@  @ ó@  @ ó@  @ ýó@  @ ó@  @ ó@  @ ó@  @ ýó@  @ ó@  @ ó@  @ þó@  @ ó@  @  @  @ ú“úà  `ù€&&€&& ÿ€/?O_oŸ·Ûõ%+"&=46;25+"&=46;2+"&=46;2%+"&=46;2+"&=46;2%+"&=46;2%+"&=46;2+"&=46;2%+"&=46;2+"&=46;2!!#!"&=!!5463!24&+"#54&+";26=3;26%#!"&5463!463!2!2€ @  @  @  @  @  @ ÿ @  @  @  @ ÿ @  @ ÿ @  @  @  @ ÿ @  @  @  @ ÿ€ÿ8(þ@(8ÿ€ @  @ € @  @ € @ &û&&@8(À(8@&à@  @ ó@  @  @  @ ó@  @ ýó@  @ ó@  @ ó@  @ þó@  @ ó@  @  @  @ ü“€ (88( û€à  À@  ``  þÀ  `` -û&&& (88(þà&@ÿ€€€<c$4&"2!#4&"254&+54&+"#";;26=326+"&5!"&5#"&46346?>;463!2€KjKKjþË€žÃKjKKjËàÀààÀà&À–Ô–þ€–Ô–€&&Æ@ &€&KjKKjK€à ý­jKKjK ÀààÀàà.û€&j––jj––j&4& @Æ@&&ÿ€€#'1?I54&+54&+"#";;26=326!5!#"&5463!!35463!2+32àÀààÀàý€þþ€ \„„\ÀûÀ 8(@(8„\ \„ ÀààÀààû„\@\„û (88( àüÀ\„„€€: #32+53##'53535'575#5#5733#5;2+3€þáþ à@þÛE&&` @@ À` €ÀÀ€ `À @@ `&&E%@à`@ @ @þ    à À € À à   þ @ :#@€€!3!57#"&5'7!7!€ÿK5€û€€€Ÿá@ à À @€€ÿ5Kþ@ÀÀÀáŸ@@€€À üàÿ€€#3%4&+"!4&+";265!;26#!"&5463!2&€&þ&€&&€&&€&©wü@w©©wÀw©À€&&þÀ@&&ü€&&@þÀ&&ºü@w©©wÀw©©ÿ€€#354&#!4&+"!"3!;265!26#!"&5463!2&þÀ&€&þÀ&&@&€&@&©wü@w©©wÀw©@€&@&&þÀ&€&þÀ&&@&:ü@w©©wÀw©©-Mó3)$"'&4762 "'&4762 s 2  þ. Ò  2 þw‰Š 2  þ. Ò  2 þw‰­ 2 Ò  Ò 2  þwþw  2 Ò  Ò 2  þwþw MÓ3)"/&47 &4?62"/&47 &4?62S þ.  2 ‰þw 2  ÒŠ þ.  2 ‰þw 2  ÒM þ. 2  ‰‰  2 þ.  þ. 2  ‰‰  2 þ.M3S)$"' "/&4762"' "/&47623 2  þwþw  2 Ò  Ò 2  þwþw  2 Ò  Òí 2 ‰þw 2  Ò þ.v 2 ‰þw 2  Ò þ.M­3s)"'&4?62 62"'&4?62 623 þ.  þ. 2  ‰‰  2 þ.  þ. 2  ‰‰  2­ þ. Ò  2 þw‰ 2v þ. Ò  2 þw‰ 2-Ms3 "'&4762s þw‰ 2  þ. Ò  2í þwþw  2 Ò  Ò 2 MS3"/&47 &4?62S þ.  2 ‰þw 2  ÒM þ. 2  ‰‰  2 þ.M 3S"' "/&47623 2  þwþw  2 Ò  Òm 2 ‰þw 2  Ò þ.M-3s"'&4?62 623 þ.  þ. 2  ‰‰  2- þ. Ò  2 þw‰ 2ÿ€€/4&#!"3!26#!#!"&54>5!"&5463!2 ùÀ  @ €^Býà &þ& ýàB^^B@B^ @  üÀ MûÀB^%Q= &&& $$ ”þØú’’ú(ú’’rÎþŸþ^þŸÎÎa¢a ’úþØú’’ú(ú½þ^þŸÎÎa¢aÎ΀€!C#!"&54>;2+";2#!"&54>;2+";2pPþ€PpQнh@&&@j–8(àPp€pPþ€PpQнh@&&@j–8(àPp@þ€PppPÀh½ŠQ&€&–j (8pPþ€PppPÀh½ŠQ&€&–j (8p€€!C+"&=46;26=4&+"&5463!2+"&=46;26=4&+"&5463!2Qнh@&&@j–8(àPppP€Pp€Qнh@&&@j–8(àPppP€PpÀý@h½ŠQ&€&–j (8pP€PppPý@h½ŠQ&€&–j (8pP€Pppÿ€À !)19A$#"&4632"&462"&462"&462"&462$"&462"&462"&462ðU;bq™›bà&4þ4&àÉ¢5 ¦þã"  #D7e uU6 ÿ&4&ÿþm†ÿ€€ 1X".4>2".4>24&#""'&#";2>#".'&547&5472632>3€=T==T=™=T==T=¹Šv)šG¬G˜+vŠ@b’†R¨R†’b@à=&‡“Á–\N€§Šˆj!>ˆ3l¤k“¢”„i¤k3ˆhPTDDTPTDDTPTDDTPTDD|x¨ ¨xXƒK--KƒÏ|Mp<# )>dA{ÐíŸRXtfOT# RNftWQ €€,%4&#!"&=4&#!"3!26#!"&5463!2!28(ý@(88(þÀ(88(À(8€„\û@\„„\@\„ \„àÀ(88(@(88(ü@(88èý@\„„\À\„„\ „u€'E4#!"3!2676%!54&#!"&=4&#!">#!"&5463!2!232õ5ûÀ([þÚ5@(\&û‹8(ýÀ(88(þÀ(8,9.þÙ+’CûÀ\„„\@\„ \„À6Z]#+þ•#,k´ (88(@(88(ü«;5E£>:þ•5E„\À\„„\ „\ 1. €€#3C++"&=#"&=46;546;2324&#!"3!26#!"&5463!2€à@àà@à€8(ý@(88(À(8€ƒ]ý@]ƒƒ]À]ƒ`@àà@ààþrÀ(88(ý@(88èý@\„„\À]ƒƒ€€/2#!"&54634&#!"3!262#!"&=463 ]ƒƒ]ý@]ƒƒ] 8(ý@(88(À(8 ýÀ€ƒ]ý@\„„\À]ƒü`À(88(ý@(88È@@ÿ€€$4@"&'&676267>"&462"&462.  > $$ n%ÊþÊ%/‡¨‡02þ KjKKjKKjKKjKf«íþüí«ff«íí«æÎþŸþ^þŸÎÎa¢aÍy””y/PccP/ÏjKKjKKjKKjKýþí«ff«íþüí«ff«@þ^þŸÎÎa¢aÎÎÿ€€$4@&'."'.7>2"&462"&462.  > $$ n20‡¨‡/%ÊþÊþ7KjKKjKKjKKjKf«íþüí«ff«íí«æÎþŸþ^þŸÎÎa¢a3/PccP/y”” jKKjKKjKKjKýþí«ff«íþüí«ff«@þ^þŸÎÎa¢aÎÎÿ€€ +7#!"&463!2"&462"&462.  > $$ €&ý€&&€þ&KjKKjKKjKKjKf«íþüí«ff«íí«æÎþŸþ^þŸÎÎa¢aÚ4&&4&µjKKjKKjKKjKýþí«ff«íþüí«ff«@þ^þŸÎÎa¢aÎ΀#+3C54&+54&+"#";;26=3264&"24&"2$#"'##"3!2@À€ÀÀ€À@KjKKjKKjKKjKþÔÔÀ’Ü’ÀÔþÔ,Ô€ÔÀ€ÀÀ€ÀÀgjKKjKKjKKjKÔþXþÔ€€,¨,€€ #/;GS_kwƒŸ£³+"=4;27+"=4;2'+"=4;2#!"=43!2%+"=4;2'+"=4;2+"=4;2'+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;54;2!#!"&5463!2€``€àà€``ü `ý€``€``€``€``€``€``þ````àp`€ù€K5ù€5KK5€5Kp``ð``ð``ýð``ð``ð``þð``ð``þð``þð``ð````þ `ðý€ü€€ü€5KK5€5KK@ÿ€€*V#"'.#"63232+"&5.5462#"/.#"#"'&547>32327676€©‰R?d¨^­æõ¼7ac77,9xûm#@#KjKÀ# Ú—XF@Fp:fþõ_ #W¤IpÂp&3z¼ ëh[ 17ý©q%q#:ûò:#5KKuý't#!X: %æ#+=&>7p @ÿ€€ *2Fr56565'5&'. #"32325#"'+"&5.5462#"/.#"#"'&547>32327676@µËͳ¬Ô×éë•8 2.,#,fµk*1x­©‰-!”û¬#@#KjKÀ# Ú—XF@Fp:fþõ_ #W¤IpÂp&3z¼ Àe¹`°Åv½oþ8¸t-à  Þ:5 ¼½[Ä*î#:ûò:#5KKuý't#!X: %æ#+=&>7p  €3$ "/&47 &4?62#!"&=463!2Iþ.  2 ‰þw 2  Ò -ü@À)þ. 2  ‰‰  2 þ. þ-@@-ÿ“Sí$9%"'&4762  /.7> "/&47 &4?62i2  þ. Ò  2 þw‰ Eþ‹ > u > þ.  2 ‰þw 2  Ò ‰2 Ò  Ò 2  þwþw !úõ   ýhþ. 2  ‰‰  2 þ. ÿ€»;#"'&476#"'&7'.'#"'&476€' þ)'þs "+5+•@Õ¡' þ)'›¼©ÆF*4*Eþr4þM:—}}8 ¥GO û*4*þúÁ­ÿ­~à (-/' #"'%#"&7&67%632¢þœBŸ;>< õþ•Vþ?þ?Vþ” -öááö-Cú4 <Bü=¨cB5þžþ !% ìì %!ôb 7IÇ))þ9I7ÿ€€ #"'.5!".67632yý€( ýÀ#  £û##@,( €)ÿ€€8! !++"&=!"&5#"&=46;546;2!76232-Sý€Sý­€àÀü ààÀSö  ÷àSýÚSý`Ààà`Ààà÷  öü­ÿ€€K$4&"24&"24&"27"&5467.546267>5.5462 8P88P88P88P¸8P88P˜4,àD‡€S,4p p4,,4p p4,6d7AL*',4p pP88P8¸P88P8HP88P8`4Yþá&+(>EY4PppP4Y4Y4PppP4Yþ%*54&#"#"/.7!2Àðð<'G,')7ð‚N;2]=A+#H  ¤  0P¢‚RððH6^;<T%-S“#:/*@Z}   >h—€€.%#!"&=46;#"&=463!232#!"&=463!2€&þ&&@@&&€&@&€&ÿ&&&À€&&€&€&€&&ýÀ&fÀ&&À&&b€#!"&=463!2#!"&'&63!2&ÿ&&&'ÿ'%@% à&&à&&ý&&&&þk"G%#/&'#!53#5!36?!#!'&54>54&#"'6763235øŸ ›þþ€Å¹‰‹Œ}¸Ìêýþ4NZN4;)3.i%Sinˆ1KXL7è§§ü* ú§#¨ä& *ä¨þõþاÎ@jC?.>!&1' \%Awc8^;:+54&#"'6763235øŸ ›þþ€Å¹‰‹Œ}¸Ììýþ4NZN4;)3.i%PlnˆEcdJè§§ü* ú§#¨ä& *ä¨þõþØÙÎ-@jC?.>!&1' \%AwcBiC:D'P%! #!"&'&6763!2€Pýþ°õ ü€&:ý&? €&:&?€€þ€5"Kü,)""K,)ÿÜ€h#".#""#"&54>54&#"#"'./"'"5327654.54632326732>32€YO)I-D%n  "h.=T#)#lQTv%.%P_– % %–_P%.%vUPl#)#T=@è/#,-91P+R[¶Ql#)#|'˜' 59%D-I)OY[R+P19-,##,-91P+R[YO)I-D%95%–_P%.%vÿ€€'3!2#!"&463!5&=462 =462 &546 €þÙÙ&&ý€&&ÙþÙ&4&r&4&ÿ¼þø¼¼¼@€Ýþ¹„&4&&4&„GÝ€&&€¹þù¹€&&fþ„¼¼„„¼¼ ÿ€s CK&=462 #"'32=462!2#!"&463!5&'"/&4762%4632e*&4&iþ—¼„76`al¹&4&þÙÙ&&ý€&&}nþ  R Ò  R þzý“¼„f¥Oego€&&€5þ—€„¼`3¹€&&€Ýþ¹„&4&&4&„ Dþ R  Ò R zý“„¼vÿ€€"!676"'.5463!2@þ@w^ëÀCc‰t~5  5~t‰cC&€&@€û?J¸°ýV©ƒ|RIIR|ƒ©V&&ÿ€#G!!%4&+";26%4&+";26%#!"&546;546;2!546;232€€ú€€@@@@€L4ú€4LL4€^B@B^€^B@B^€4L€À þà þàNû4LL44L`B^^B``B^^B`Lÿ€€àL4&"2%#"'%.5!#!"&54675#"#"'.7>7&5462!467%632&4&&4¦  þ@ ÿo‘&þ&}c ;pG=(  8Ai8^„^. À  &4&&4&`þÀ ` f°süà&& j©o/;J!# 2 KAE*,B^^B! ` $ÿ €€-4&"2#"/&7#"/&767%676$!2 8P88P—²Qrþ€ @ Uþçþì @ à {`P¼TP88P8€ùþ•³P`þ… à @U @€rQ»Ž¬!6'&'&'&+!!!!2¬¼þе ÅÍþÑÌþÐÍþÐsXVqüQ ü@Àü@vtÿ€€ %764' 64/&"2 $$ fþÍ3f4þ:Æ4†ÎþŸþ^þŸÎÎa¢af4334fþ:4þ:×þ^þŸÎÎa¢aÎÎÿ€€ %64'&" 2 $$ ÍÆþ:4f3þÍf4FÎþŸþ^þŸÎÎa¢aÆ4Æf4þÍþÍ4f×þ^þŸÎÎa¢aÎÎÿ€€ 764'&"27 2 $$ fþ:4þ:f4334†ÎþŸþ^þŸÎÎa¢af4Æþ:4f3þÍ×þ^þŸÎÎa¢aÎÎÿ€€ %64/&" &"2 $$ -Æf4þÍþÍ4fÆ4æÎþŸþ^þŸÎÎa¢aíÆ4fþÍ3f4þ:wþ^þŸÎÎa¢aÎÎÿ@€€7!!/#35%!'!%jüŒ/dÅÄ ¯jg2ý|þ8€€ý¾ý«¯ýêä55Œþêdc µÕúb¢¢ ÿ@ô€! !%!!7!áþöüÜýFG)¦æDûH:¹&ûH€úËþõ d“¡¡S)¿ÿU4&"2#"/ $'#"'&5463!2#"&=46;5.546232+>7'&763!2À&4&&4f ]wþqþ4þqw] `dCõ•À&&À:F–Ô–F:À&&À•õCd`æ4&&4&ü þ  ]§§] `d[}‡&€&£"uFj––jFu"£&€&ýy}[d€#2#!"&546;4 +"&54&" (88(ü@(88( r&@&–Ô–8(ýÀ(88(@(8@¹þù¹&&j––jþÀÿ€€'3"&462&    .  > $$ –Ô––ÔáþÂáá>aþÔþXþÔ,¨¬f«íþüí«ff«íí«æÎþŸþ^þŸÎÎa¢aêÔ––Ô–þa>ááþÂáTþXþÔ,¨,ý~í«ff«íþüí«ff«@þ^þŸÎÎa¢aÎ΀€/+"&=46;2+"&=46;2+"&=46;2€8(À(88(À(88(À(88(À(88(À(88(À(8 À(88(À(88(À(88(À(88(À(88(À(88€€/+"&=46;2+"&=46;2+"&=46;2€8(À(88(À(88(À(88(À(88(À(88(À(8 À(88(À(88ØÀ(88(À(88ØÀ(88(À(88ÿ€€5E$4&"2%&'&;26%&.$'&;276#!"&5463!2KjKKjª þ¸è šÜ  € f±éþáš  Ì\Ñ € ©wü@w©©wÀw©ËjKKjK"èH  €  Üš  šé±f € Ñþ¤Ì  Íü@w©©wÀw©©ÿ€€   $64'&327/¢aÎÎþŸþ^þŸÎβ ýà! €ÎþŸþ^þŸÎÎa¢aý—J@%ý€% 6ÿ5ËÊ/ 64'&"2 "/64&"'&476227<ýÄþÄijþ–6ý–j6‹üu%k%~8p 8}%%‹%k%}8p 8~%<þÄýÄ<þij4jý–4þ–üt%%~8 p8~%k%Š%%}8 p8}%kÿ€€54&#!"3!26#!"&5463!2&ü€&&€&©wü@w©©wÀw©@€&&€&&:ü@w©©wÀw©©€€/#!"&=463!24&#!"3!26#!"&5463!2€üÀ@€^BüÀB^^B@B^€©wüÀw©©w@w©à@@þ2@B^^BüÀB^^‚üÀw©©w@w©©ú+#!"'&?63!#"'&762ú(Àý@   @À(@>@¥%ü À €%%€þ€ ÿ€ú!232"'&76;!"/&76 À À($þÀ>þÀ(ÀþÀ   ü¡J þ€€&%€ Àÿ€€$%64/&"'&"2#!"&5463!2­ff4þ-Ó4ff4f©wü@w©©wÀw©íf4fþ-Óf4þš†ü@w©©wÀw©©ÿ€€/#5#5'&76 764/&"%#!"&5463!2”˜48`ÒþÝ #þû þàýà€\˜P\ ©wü@w©©wÀw©¬˜4`8º þÝ #ý@  ýàþà`\P˜\`ü@w©©wÀw©©ÿ€€)4&#!"273276#!"&5463!2&þ *ýêf4 '©wü@w©©wÀw©`à&')ýê4f*ü@w©©wÀw©©ÿ€€%5 64'&"3276'7>332#!"&5463!2í`þ '(wƒa8! § ,j.¨Œ( &©wü@w©©wÀw©³`4`* '?_`ze<µß  bw4/ *Àü@w©©wÀw©©ÿ€€-.  6 $$ €ÿ€þ ’úþØú’’ú(úrÎþŸþ^þŸÎÎa¢aÀ€€OýâÿþÝ(ú’’úþØú’’_þ^þŸÎÎa¢aÎÎÿ€€ -"'&763!24&#!"3!26#!"&5463!2yþÀBþÀ(€(˜ ü@  À ©wü@w©©wÀw©]#þ@À##ý À  ü@ Íü@w©©wÀw©©ÿ€€ -#!"'&7624&#!"3!26#!"&5463!2y(ý€(@B@u ü@  À ©wü@w©©wÀw©£###Àþ@þÚÀ  ü@ Íü@w©©wÀw©©ÿ€€ -'&54764&#!"3!26#!"&5463!2@þ@####ÀÛü@À©wü@w©©wÀw©¡BþÀ(€(þÀýìÀü@Îü@w©©wÀw©©ó€`%#"'#"&=46;&7#"&=46;632/.#"!2#!!2#!32>?6Ð#  !"'êþ¢?_  BCbCaàf\ + ~È2Ô þË  þ}0Ë$ åŸ Ý q 90rÒ Ÿ €p r%D p u‰ü€?#!"&=46;#"&=46;54632'.#"!2#!!546;2üüD a__÷¿¹– g *`-Uh1  þÏž¢þ‘–  ƒß«Þ}   $^L׃ þ…µ 4ÿÒb+"&=.'&?676032654.'.5467546;2'.#"ÒÇŸ‡ B{PDg q‚%%Q{%P46'-N/B).Ä ‡9kC< Q 7>W*_x*%K./58`7E%_™Ý¯ ¯ ,-3‡  cVO2")#,)9;J)ŠÐ´ °"!*’ #VD,'#/&>AX‚€>++"''&=46;267!"&=463!&+"&=463!2+32‚¨Ôª§$ à þÎÀ  p„¡þU9Ó‘ @é/«*f´²þš oÌ  VRfq …f=Sÿ€E!#"&5!"&=463!5!"&=46;&76;2>76;232#!!2#![¬ þà   þà  Öþ¿  ×% )¿¿ þÇ×  þÞ"  þÞJg Uh BþW&WX¤ý½ hU gþ¶ ÿ瀀L\+"&5##"/&67>7> 7!"&=463!2+;26=46;2#!"&=463!2€¼„€„¼à$=5R9[/*G :!3' ÿÀà&€&Àþ€ü@À` „¼¼„àf±‡vQJ+- ²   (->K\rB  þ&&@ò  € n#467!!3'##467!++"'#+"&'#"&=46;'#"&=46;&76;2!6;2!6;232+32QŸKt#þÜ ¡‹#FŸN¢Qo!þ×"€Õ¤Ÿ¦Ñ§Ÿ  Ð¯!ŽmY ‰Zga~bm]‰ [o‘"³U+þÔ¬€€€ýÔ,þÕ­€€ @ý˜hý˜ h@€@X þ˜hþ˜h þ¨@€8ÿè€3H\#5"'#"&+73273&#&+5275363534."#22>4.#2>•ut 3NtRšP*šHÈo2 LoÔ@!šR(šOzh=Ñ,GID2Fýž þÁ þÀÀÀÀî8PuE>.'%&TeQ,j†m{¤þ+§>RÀ{ß?jJrL6V þÁ @`ú 7>wmR1q uWei’½/rr° :V¹ýr"ÿÎ $7V4&#"326#"'&76;46;232!5346=#'73#"'&'73267##"&54632BX;4>ID2Fýž þÁ þÀÀÀÀÐþ+§>RÀ{Ã8PuE>.'%&TeQ,j†m{¤ß?jJrL6ûª þÁ @`ú ürr° :V¹ýr3>wmR1q uWei’½ÿ€@€ \%4&#"326#!"&5463!2+".'&'.5467>767>7>7632!2&%%& &þà&& & 7.' :@…$LBœWM{#&$h1D!  .I/! NrÀ&&%%ý€&&€&&V?, L=8=9%pEL+%%r@W!<%*',<2(<&L,"rÿ@ \#"&546324&#!"3!26%#!#"'.'.'&'.'.546767>;&%%& &þà&& &i7qNþë !/I.  !D1h$&#{MWœBL$…@: '.À&&%%ýå€&&ý€&&¯=XNr%(M&<(2<,'*%<!W@r%%+LEp%9=8=L ÿ€€ +=\dŒž²Â%54#"327354"%###5#5#"'&53327#"'#3632#"'&=4762#3274645"=424'.'&!  7>76#'#3%54'&#"32763##"'&5#327#!"&5463!2—¸BBýÅPJN±C'%! B? )#!CC $) û 54f…"þ@@ B+ˆþìþíˆ,A  A+‰&‰+A ý ZK35N # J!1331µCCC $)÷©wü@w©©wÀw©é2à«"33èFþY§F~þ‘(-&"þòo’4*)$í¡(*¶ (&;;&&:LA3  8œ33œ4ý±S,;;,W­°T+<<+T;(ÃÃ\g7Éx‚:&&:‚:&&<rþåþÛ%-ü@w©©wÀw©© ÿå +=[c}‰›¯#"'632#542%35!33!3##"'&5#327%54'&#"5#353276%5##"=354'&#"32767654"2 '.'&547>76 3#&'&'3#"'&=47632%#5#"'&53327Ë''RZZü:kþÈid YYY .06­ 62+YY-06 R[!.³'CD''EH$ý VVÏX:¸ý¸:Y X;·æ¸:Y üfyd/%jG¶%EC&&CE%O[52. [$ÓC-D..D–^^ýÇîþ†* lþy1%=^ÅI86Ùýi077S 3 $EWgO%33%O­O%35 ÂÒEEÒFýWêt;PP;pîêt;PP;pþqþñJŠgTþùF¯Q%33&P¯P%33%Rþ 7>%3Šþ‘!+}ÿ€{ö'+"&72'&76;2+"'66;2U ÷&ï ý¡ ï(Ê ýðP ï*þ­'ñeþJ."À-düZý™-n Ž-ÿ€€'74'&+";27&+";276'56#!"&5463!2­~¸}Ä ¹7»þe ¸ þü™Û©wü@w©©wÀw©Ý" Øþ¦ $Q #ý'þ!# ÜÓˆü@w©©wÀw©©/4'&327$ '.'.4>7>76 þ"!!jGÞü~ÞGkjGÞ‚ÞGk[J@&ý€& @–Àl¥AIddIA¥lÀl¥AIddIA¥@ÿ€ÀŠ '5557 ’îþªþ,þþ“VWýQVþþ®.Rþþ©Wéþ®þ=þÏþã?þälþÛ%l`þäØþãþÐþòþñþÁ~þÁþò0ÿ~#%5!'#3! %% % ý=´û”#y üØÀ ý?R«'ýUÊaMýŸµŽ|þqBy•y‡——[ýC#àý–jXA–AÒ·’·ïþ˜‚hÍý·UHéýG¹ÿ€€/?%##"547#3!264&#"3254&+";267#!"&5463!2R‡€Ü‚Åþè#-$þäµ€µµ€Ñ(®((®(®tQûŠQttQvQtnˆ?D~Õ|ÀD?ýx##³ø¯¯ø¯“¥))¥((íûŠQttQvQttÿ€€2#!"&54634&"2$4&"2àw©©wü@w©©wš|°||°°|°||°€©wü@w©©wÀw©ü¨°||°||°||°|ÿ€€ !3 37! $$ Éþn6^þ5þ5^h ûÎþŸþ^þŸÎÎa¢a’þÎà³ýM 1þ^þŸÎÎa¢aÎÎÿP£ *Cg'.676.7>.'$7>&'.'&'? 7%&'.'.'>767$/u5'&$I7oÆb?K“\[zäH,1þÝþí+.@\7<äÜ?5\V ,$VÏÅg.GR@ ß7àµU,+!üþšø’  # "8$}¼{)›<¥?L RR ;kr,yE[€˜z# /1 "# #üeCI0/"5#`Ä ””"8¸§þ4~&p )4 2È{¬H- .%W.L>ÿ€€':Yi4&67&'&676'.'>7646&' '7>6'&'&7>7#!"&5463!2PR$++'TJX„j7-F¶C',›©,&C ."ÆÒ!$28 ¡þh¢ /ù³"‡ +pØþñ„^&+3$ i³µ0(©wü@w©©wÀw©š+.i6=Bn \C1XR:#"ý'jj š8Q.cAjÇ57!? "0DŒÊ$4" P[ & 2ü@w©©wÀw©©Nÿ€€#3!!327#"'&'&'&5#567676†Ûlþ” '2CusfLM`iQN<:ª[@@''€þ|ñþvˆ$%Lò02366kÙ67MNÿ€€#3%5#"'&'&5!5!#33276#!"&5463!2cXV3% þî¤ 10D*+=>NC>9ê©wü@w©©wÀw©µ8c'µ#Z99*(£þlN+*$% ü@w©©wÀw©©ÿ@ý#"'&76;46;23õ þ¢  þ  àÀà&þ€ €àû ÿýÀ++"&5#"&7632ý àÀà ^  c û à&€ þ€@ý#!'&5476!2û &þ€ €ààÀà ^  b àÀý'&=!"&=463!546À þ€û à&€ ƒ þž àÀà þ¢ ÿ€q&8#"'&#"#"5476323276326767q'T€1[VA=QQ3˜•“qp¬Hih"-bfGw^44O#AŠþá?66%CKJ°A}}Ä !"òä’""A$@C3^q|Æz=KK?6 •lk) ÿ€€ %!%!ªýVªýV€üu‹üuýu^-çým5ýwüî}•nüæÿ€~7M[264&"264&"2"&546+"&=##"&5'#"&5!467'&766276#"&54632Ý  ¼  üû*<;V<<O@-K<&4'>&4.'.'.'.'.'&6&'.'.6767645.'#.'6&'&7676"&'&627>76'&7>'&'&'&'&766'.7>7676>76&6763>6&'&232.'.6'4.?4.'&#>7626'.'&#"'.'.'&676.67>7>5'&7>.'&'&'&7>7>767&'&67636'.'&67>7>.'.67— \ þ›U7  J#!W! '  " ';%  k )"    '   /7*   I ,6 *&"!   O6* O $.(¨ *.'  .x…,  $CNý¡    £  * ´ 8   7%&&_f& ",VL,G$3¤@@$+ "  V5 3"  ""#dA++ y0D- %&n 4P'A5j$9E#"c7Y 6" & 8Z(;=I50 ' !!e  þR  þš "+0n?¢t(-z.'< >R$A"24B@( ~ 9B9, *$        < > ?0D¨9f?Ae ‡ .(;1.D 4H&.Ct iY% *  7à ì   úÈ  J  <    W 0%$  ""I! *  D  ,4A'¾4J" .0f6D4pÆZ{+*ŸD_wqi;ÐW1G("% %T7F}AG!1#%  JG 3 ÿ€€ '.2>Vb%&#'32&'!>?>'&' &>"6&#">&'>26 $$ *b6”~ˆ#¸ê„= þÉþ–XP2“Š{&%gx|ŠÀ .ÜÒÇW)o”üñO¹øLOƒsEzG<ä’ CK}E $MFD<5+ zÎþŸþ^þŸÎÎa¢a$ñMWŽM –“1>]|áYY›^D ÖÕ¥Aò—ï<ïæKåm‘¤ªÔE6<þ"è² @9I5*Èþ^þŸÎÎa¢aÎÎÿ€€>^4./.543232654.#"#".#"32>#"'#"$&547&54632632•':XM1h*+D($,/9p¬`D€oC&JV<’Z PA3Q1*223ô©I†oBkែhMIþû½oPែhMI½oPÙ2S6, M!"@-7Y.?oI=[<%$('3 -- <-\ƒ%FuŸáPo½IMh‚ŸáPo½þûIMhÿ€€#< "'&4762 '&#"327#1"'&'&4?6262Ëýµ4—5ýµ55K5–5 þr¼*9;)x**–%<'k5‚xý& þiy ,ü>*ýµ55K5–5K55þ÷þq¼*)y(;:*þh )k5–=x*ý& ˜*xü?ÿ€€/%4&#!"3!264&#!"3!26#!"&5463!2Àþ à þ à &ú€&&€&ÀüŽ€ý€ú€&&€&&ÿà19#"'#++"&5#"&5475##"&54763!2"&4628(3ã-÷ &ÀB. .BÀ& ÷-ã3(8Ig€gIþ`ƒºƒƒºà(8+U„þe&þð.BB.&›„þ«+8(€kkþ€`ºƒƒºƒÿà%-"&5#"&5#"&5#"&5463!2"&4628P8@B\B@B\B@8P8pP€Ppþàƒºƒƒº@þ`(88(`üp.BB.Ðþ0.BB.þ (88( Pppͺƒƒºƒÿ€€!%>&'&#"'.$ $$ ^/(V=$<;$=V).XÎþŸþ^þŸÎÎa¢aêÙJ`"(("`JŽþ^þŸÎÎa¢aÎÎ,ÿÔÿI4."2>%'%"/'&5%&'&?'&767%476762%6À[›ÕêÕ›[[›ÕêÕ›oþÜ þÜ´ ´þÜ þÜ ´´ $ $´ " ´$ $ ´´  êÕ›[[›ÕêÕ›[[›5`þÎ ^ø ø^ 2` øø `2 ^ø ø^ þÎ` øø ÿ€¾1%#"$54732$%#"$&546$763276î68¶þÊ´hÉÿf«í‚&^þ…àœþäÎzsÅ™,!V[’ú”vn)é ´6¶À¥<þ®×‚í«f{ÃËózΜ™Ì}))NÏs”ú’3(@ÿ€À€ +4&#!"3!2#!"&5463!2#!"&5463!2@&ÿ&&f&ú€&&€&@&ú&&&¦4&&4&ü@&&À&&¦ÿ&&&& ÿ `ÀBH+"/##"./#"'.?&5#"&46;'&462!76232!46 `&àCÐ6Æ@Bb0€3eI;·Ê:à&&à­&4­L­4&­àþFý€» »Z4&«wÑ4Å) €ü€'' Ï5ãr &4&&­4&­­&4­þÚ…»»ÿÿ}G†3#&/.#./.'&4?63%27>'./&'&7676>767>?>%6}­)N @2*&ÿ@P9A #sG–q] #lh<* 46+(  < 5ºR5"*>%"/ +[>hy  ÿª÷K !/Ui%6&'&676&'&6'.7>%.$76$% $.5476$6?62'.76&&'&676%.76&'..676£#"NDQt µ-âokQ//Ñjo_  þÿ’ßþÛ  ’ß%&JÁþýþæþôÕ‚‹€©YJA-‹Ö.-- 9\DtT+X?*<UW3' 26$>>¬W0 {òü"F!"E ›   ^f`$"¹_]\µ<`”Fí’`”FíŽDƒh>Cw·ls€©†J@‘ ;=?s  :i_^{8+?` ) O`ýs2R´DE58/K`€˜ &1:%#"'>7&54&5#"'>71654'6&5%z‹xbŠŠþ³‘»€€‘³Ä(z‹xbŠŠþ¶’……A‘³Â£CC=°gg°¸þ¨³þÚF˜ÇÈ0™˜ÇÉ–F(´U!ü,CC=°gg°¸þ¨³þÚFšÅÜ—þ É–F(´U þЃÐf5B_<õ ÍÞ<ÑÍÞ<Ñÿÿÿ€þÝ€ÿÿÿÿ€„€pUÀÀ€À3U3€ô]€€€€€€y€n€€€€€2€€@€€€€€€ €€€€€€€€z€€€5€u@€5€5 €€€z€€€€5€5€€€€@€€€€€€€€€€,€€€€€€€€€€s€€€@€€€@€€(€€€€€€€€€€€€@€€@- €M€M€-€ €M€M€€€€€ €€€€€€€@@€ €-€€€€`€€b€€€€ €€€$€ €€€6€4‚€8€"€"""""€€€@€€N@€€€ €,@€ €ÿÿ€P’ÔBp®<$‚HÎú<¦üTÄfüT’à H ¨ þ R Ú , š D x Ê 6 \ ¤D¨L¦XŠâ*ªD¶x8”ðJ¤NÀ2f²ö$P˜Žø`Ö"Vt¤êLv¤ð$~À*‚h¼þ 6 n ¨ â!&!v!Æ!ü""p"¼#&#˜#â$8$À%%f& &¼&ü'`'Œ'¼(*(‚(¦(ì)")X)¬* *B*¦+,n,ä-z..:.’.ð/@/ˆ/æ0D0¬1~1â2l2à33R3Ì44>4¼4ö585œ66V6¬7"7²8P8à9|9Ê::b:°=¾>>l>–>à?R?Ø@l@š@ÔA²BBxBâCCDC®DZDîE–FrFÔGDG²HH®IFI¢IÀIÞIüJJLJ€JžJ¼KK\K®LJLÈM*M¾MøNhNìOFOÈPPjP¾QDQ²QîR2RjRÈS2TÌVV’VúWLWxWÄXX\X¨XôY@YjY”Y¾YèZ0Z~Z¼[[6[’[î\V\t\¸]6]x]â^@^ˆ^ö_d_Â`$aa„b(bhbÐc2c€cªcþdle?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqï rstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ     " !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abÒcdefghijklmnopqrstuv”uni00A0uni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni202Funi205FuniE000glassmusicsearchenvelopeheartstar star_emptyuserfilmth_largethth_listokremovezoom_inzoom_outoffsignalcogtrashhomefile_alttimeroad download_altdownloaduploadinbox play_circlerepeatrefreshlist_altlockflag headphones volume_off volume_down volume_upqrcodebarcodetagtagsbookbookmarkprintcamerafontbolditalic text_height text_width align_left align_center align_right align_justifylist indent_left indent_rightfacetime_videopicturepencil map_markeradjusttinteditsharecheckmove step_backward fast_backwardbackwardplaypausestopforward fast_forward step_forwardeject chevron_left chevron_right plus_sign minus_sign remove_signok_sign question_sign info_sign screenshot remove_circle ok_circle ban_circle arrow_left arrow_rightarrow_up arrow_down share_alt resize_full resize_smallexclamation_signgiftleaffireeye_open eye_close warning_signplanecalendarrandomcommentmagnet chevron_up chevron_downretweet shopping_cart folder_close folder_openresize_verticalresize_horizontal bar_chart twitter_sign facebook_sign camera_retrokeycogscomments thumbs_up_altthumbs_down_alt star_half heart_emptysignout linkedin_signpushpin external_linksignintrophy github_sign upload_altlemonphone check_emptybookmark_empty phone_signtwitterfacebookgithubunlock credit_cardrsshddbullhornbell certificate hand_right hand_lefthand_up hand_downcircle_arrow_leftcircle_arrow_rightcircle_arrow_upcircle_arrow_downglobewrenchtasksfilter briefcase fullscreengrouplinkcloudbeakercutcopy paper_clipsave sign_blankreorderulol strikethrough underlinetablemagictruck pinterestpinterest_signgoogle_plus_sign google_plusmoney caret_downcaret_up caret_left caret_rightcolumnssort sort_downsort_up envelope_altlinkedinundolegal dashboard comment_alt comments_altboltsitemapumbrellapaste light_bulbexchangecloud_download cloud_uploaduser_md stethoscopesuitcasebell_altcoffeefood file_text_altbuildinghospital ambulancemedkit fighter_jetbeerh_signf0fedouble_angle_leftdouble_angle_rightdouble_angle_updouble_angle_down angle_left angle_rightangle_up angle_downdesktoplaptoptablet mobile_phone circle_blank quote_left quote_rightspinnercirclereply github_altfolder_close_altfolder_open_alt expand_alt collapse_altsmilefrownmehgamepadkeyboardflag_altflag_checkeredterminalcode reply_allstar_half_emptylocation_arrowcrop code_forkunlink_279 exclamation superscript subscript_283 puzzle_piece microphonemicrophone_offshieldcalendar_emptyfire_extinguisherrocketmaxcdnchevron_sign_leftchevron_sign_rightchevron_sign_upchevron_sign_downhtml5css3anchor unlock_altbullseyeellipsis_horizontalellipsis_vertical_303 play_signticketminus_sign_alt check_minuslevel_up level_down check_sign edit_sign_312 share_signcompasscollapse collapse_top_317eurgbpusdinrjpycnykrwbtcfile file_textsort_by_alphabet_329sort_by_attributessort_by_attributes_alt sort_by_ordersort_by_order_alt_334_335 youtube_signyoutubexing xing_sign youtube_playdropbox stackexchange instagramflickradnf171bitbucket_signtumblr tumblr_signlong_arrow_down long_arrow_uplong_arrow_leftlong_arrow_rightwindowsandroidlinuxdribbleskype foursquaretrellofemalemalegittipsun_366archivebugvkweiborenren_372_373_374Q¸ŒQmongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.svg0000775000175000017500000060230512651363712030477 0ustar travistravis mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/layout_old.html0000775000175000017500000001625512651363712024512 0ustar travistravis{# basic/layout.html ~~~~~~~~~~~~~~~~~ Master layout template for Sphinx themes. :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. #} {%- block doctype -%} {%- endblock %} {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} {%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and (sidebars != []) %} {%- set url_root = pathto('', 1) %} {# XXX necessary? #} {%- if url_root == '#' %}{% set url_root = '' %}{% endif %} {%- if not embedded and docstitle %} {%- set titlesuffix = " — "|safe + docstitle|e %} {%- else %} {%- set titlesuffix = "" %} {%- endif %} {%- macro relbar() %} {%- endmacro %} {%- macro sidebar() %} {%- if render_sidebar %}
{%- block sidebarlogo %} {%- if logo %} {%- endif %} {%- endblock %} {%- if sidebars != None %} {#- new style sidebar: explicitly include/exclude templates #} {%- for sidebartemplate in sidebars %} {%- include sidebartemplate %} {%- endfor %} {%- else %} {#- old style sidebars: using blocks -- should be deprecated #} {%- block sidebartoc %} {%- include "localtoc.html" %} {%- endblock %} {%- block sidebarrel %} {%- include "relations.html" %} {%- endblock %} {%- block sidebarsourcelink %} {%- include "sourcelink.html" %} {%- endblock %} {%- if customsidebar %} {%- include customsidebar %} {%- endif %} {%- block sidebarsearch %} {%- include "searchbox.html" %} {%- endblock %} {%- endif %}
{%- endif %} {%- endmacro %} {%- macro script() %} {%- for scriptfile in script_files %} {%- endfor %} {%- endmacro %} {%- macro css() %} {%- for cssfile in css_files %} {%- endfor %} {%- endmacro %} {{ metatags }} {%- block htmltitle %} {{ title|striptags|e }}{{ titlesuffix }} {%- endblock %} {{ css() }} {%- if not embedded %} {{ script() }} {%- if use_opensearch %} {%- endif %} {%- if favicon %} {%- endif %} {%- endif %} {%- block linktags %} {%- if hasdoc('about') %} {%- endif %} {%- if hasdoc('genindex') %} {%- endif %} {%- if hasdoc('search') %} {%- endif %} {%- if hasdoc('copyright') %} {%- endif %} {%- if parents %} {%- endif %} {%- if next %} {%- endif %} {%- if prev %} {%- endif %} {%- endblock %} {%- block extrahead %} {% endblock %} {%- block header %}{% endblock %} {%- block relbar1 %}{{ relbar() }}{% endblock %} {%- block content %} {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
{%- block document %}
{%- if render_sidebar %}
{%- endif %}
{% block body %} {% endblock %}
{%- if render_sidebar %}
{%- endif %}
{%- endblock %} {%- block sidebar2 %}{{ sidebar() }}{% endblock %}
{%- endblock %} {%- block relbar2 %}{{ relbar() }}{% endblock %} {%- block footer %}

asdf asdf asdf asdf 22

{%- endblock %} mongoengine-0.10.6/docs/_themes/sphinx_rtd_theme/footer.html0000775000175000017500000000221512651363712023624 0ustar travistravis
{% if next or prev %} {% endif %}

{%- if show_copyright %} {%- if hasdoc('copyright') %} {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} {%- else %} {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} {%- endif %} {%- endif %} {%- if last_updated %} {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} {%- endif %}

{% trans %}Sphinx theme provided by Read the Docs{% endtrans %}
mongoengine-0.10.6/docs/guide/0000775000175000017500000000000012651364023015536 5ustar travistravismongoengine-0.10.6/docs/guide/mongomock.rst0000664000175000017500000000107512651363712020270 0ustar travistravis============================== Use mongomock for testing ============================== `mongomock `_ is a package to do just what the name implies, mocking a mongo database. To use with mongoengine, simply specify mongomock when connecting with mongoengine: .. code-block:: python connect('mongoenginetest', host='mongomock://localhost') conn = get_connection() or with an alias: .. code-block:: python connect('mongoenginetest', host='mongomock://localhost', alias='testdb') conn = get_connection('testdb') mongoengine-0.10.6/docs/guide/index.rst0000664000175000017500000000030612651363712017402 0ustar travistravis========== User Guide ========== .. toctree:: :maxdepth: 2 installing connecting defining-documents document-instances querying gridfs signals text-indexes mongomock mongoengine-0.10.6/docs/guide/gridfs.rst0000664000175000017500000000422612651363712017556 0ustar travistravis====== GridFS ====== .. versionadded:: 0.4 Writing ------- GridFS support comes in the form of the :class:`~mongoengine.fields.FileField` field object. This field acts as a file-like object and provides a couple of different ways of inserting and retrieving data. Arbitrary metadata such as content type can also be stored alongside the files. In the following example, a document is created to store details about animals, including a photo:: class Animal(Document): genus = StringField() family = StringField() photo = FileField() marmot = Animal(genus='Marmota', family='Sciuridae') marmot_photo = open('marmot.jpg', 'rb') marmot.photo.put(marmot_photo, content_type = 'image/jpeg') marmot.save() Retrieval --------- So using the :class:`~mongoengine.fields.FileField` is just like using any other field. The file can also be retrieved just as easily:: marmot = Animal.objects(genus='Marmota').first() photo = marmot.photo.read() content_type = marmot.photo.content_type Streaming --------- Streaming data into a :class:`~mongoengine.fields.FileField` is achieved in a slightly different manner. First, a new file must be created by calling the :func:`new_file` method. Data can then be written using :func:`write`:: marmot.photo.new_file() marmot.photo.write('some_image_data') marmot.photo.write('some_more_image_data') marmot.photo.close() marmot.save() Deletion -------- Deleting stored files is achieved with the :func:`delete` method:: marmot.photo.delete() .. warning:: The FileField in a Document actually only stores the ID of a file in a separate GridFS collection. This means that deleting a document with a defined FileField does not actually delete the file. You must be careful to delete any files in a Document as above before deleting the Document itself. Replacing files --------------- Files can be replaced with the :func:`replace` method. This works just like the :func:`put` method so even metadata can (and should) be replaced:: another_marmot = open('another_marmot.png', 'rb') marmot.photo.replace(another_marmot, content_type='image/png') mongoengine-0.10.6/docs/guide/signals.rst0000664000175000017500000001177212651363712017744 0ustar travistravis.. _signals: ======= Signals ======= .. versionadded:: 0.5 .. note:: Signal support is provided by the excellent `blinker`_ library. If you wish to enable signal support this library must be installed, though it is not required for MongoEngine to function. Overview -------- Signals are found within the `mongoengine.signals` module. Unless specified signals receive no additional arguments beyond the `sender` class and `document` instance. Post-signals are only called if there were no exceptions raised during the processing of their related function. Available signals include: `pre_init` Called during the creation of a new :class:`~mongoengine.Document` or :class:`~mongoengine.EmbeddedDocument` instance, after the constructor arguments have been collected but before any additional processing has been done to them. (I.e. assignment of default values.) Handlers for this signal are passed the dictionary of arguments using the `values` keyword argument and may modify this dictionary prior to returning. `post_init` Called after all processing of a new :class:`~mongoengine.Document` or :class:`~mongoengine.EmbeddedDocument` instance has been completed. `pre_save` Called within :meth:`~mongoengine.Document.save` prior to performing any actions. `pre_save_post_validation` Called within :meth:`~mongoengine.Document.save` after validation has taken place but before saving. `post_save` Called within :meth:`~mongoengine.Document.save` after all actions (validation, insert/update, cascades, clearing dirty flags) have completed successfully. Passed the additional boolean keyword argument `created` to indicate if the save was an insert or an update. `pre_delete` Called within :meth:`~mongoengine.Document.delete` prior to attempting the delete operation. `post_delete` Called within :meth:`~mongoengine.Document.delete` upon successful deletion of the record. `pre_bulk_insert` Called after validation of the documents to insert, but prior to any data being written. In this case, the `document` argument is replaced by a `documents` argument representing the list of documents being inserted. `post_bulk_insert` Called after a successful bulk insert operation. As per `pre_bulk_insert`, the `document` argument is omitted and replaced with a `documents` argument. An additional boolean argument, `loaded`, identifies the contents of `documents` as either :class:`~mongoengine.Document` instances when `True` or simply a list of primary key values for the inserted records if `False`. Attaching Events ---------------- After writing a handler function like the following:: import logging from datetime import datetime from mongoengine import * from mongoengine import signals def update_modified(sender, document): document.modified = datetime.utcnow() You attach the event handler to your :class:`~mongoengine.Document` or :class:`~mongoengine.EmbeddedDocument` subclass:: class Record(Document): modified = DateTimeField() signals.pre_save.connect(update_modified) While this is not the most elaborate document model, it does demonstrate the concepts involved. As a more complete demonstration you can also define your handlers within your subclass:: class Author(Document): name = StringField() @classmethod def pre_save(cls, sender, document, **kwargs): logging.debug("Pre Save: %s" % document.name) @classmethod def post_save(cls, sender, document, **kwargs): logging.debug("Post Save: %s" % document.name) if 'created' in kwargs: if kwargs['created']: logging.debug("Created") else: logging.debug("Updated") signals.pre_save.connect(Author.pre_save, sender=Author) signals.post_save.connect(Author.post_save, sender=Author) Finally, you can also use this small decorator to quickly create a number of signals and attach them to your :class:`~mongoengine.Document` or :class:`~mongoengine.EmbeddedDocument` subclasses as class decorators:: def handler(event): """Signal decorator to allow use of callback functions as class decorators.""" def decorator(fn): def apply(cls): event.connect(fn, sender=cls) return cls fn.apply = apply return fn return decorator Using the first example of updating a modification time the code is now much cleaner looking while still allowing manual execution of the callback:: @handler(signals.pre_save) def update_modified(sender, document): document.modified = datetime.utcnow() @update_modified.apply class Record(Document): modified = DateTimeField() ReferenceFields and Signals --------------------------- Currently `reverse_delete_rule` does not trigger signals on the other part of the relationship. If this is required you must manually handle the reverse deletion. .. _blinker: http://pypi.python.org/pypi/blinker mongoengine-0.10.6/docs/guide/document-instances.rst0000664000175000017500000001136612651363712022106 0ustar travistravis=================== Documents instances =================== To create a new document object, create an instance of the relevant document class, providing values for its fields as constructor keyword arguments. You may provide values for any of the fields on the document:: >>> page = Page(title="Test Page") >>> page.title 'Test Page' You may also assign values to the document's fields using standard object attribute syntax:: >>> page.title = "Example Page" >>> page.title 'Example Page' Saving and deleting documents ============================= MongoEngine tracks changes to documents to provide efficient saving. To save the document to the database, call the :meth:`~mongoengine.Document.save` method. If the document does not exist in the database, it will be created. If it does already exist, then any changes will be updated atomically. For example:: >>> page = Page(title="Test Page") >>> page.save() # Performs an insert >>> page.title = "My Page" >>> page.save() # Performs an atomic set on the title field. .. note:: Changes to documents are tracked and on the whole perform ``set`` operations. * ``list_field.push(0)`` --- *sets* the resulting list * ``del(list_field)`` --- *unsets* whole list With lists its preferable to use ``Doc.update(push__list_field=0)`` as this stops the whole list being updated --- stopping any race conditions. .. seealso:: :ref:`guide-atomic-updates` Pre save data validation and cleaning ------------------------------------- MongoEngine allows you to create custom cleaning rules for your documents when calling :meth:`~mongoengine.Document.save`. By providing a custom :meth:`~mongoengine.Document.clean` method you can do any pre validation / data cleaning. This might be useful if you want to ensure a default value based on other document values for example:: class Essay(Document): status = StringField(choices=('Published', 'Draft'), required=True) pub_date = DateTimeField() def clean(self): """Ensures that only published essays have a `pub_date` and automatically sets the pub_date if published and not set""" if self.status == 'Draft' and self.pub_date is not None: msg = 'Draft entries should not have a publication date.' raise ValidationError(msg) # Set the pub_date for published items if not set. if self.status == 'Published' and self.pub_date is None: self.pub_date = datetime.now() .. note:: Cleaning is only called if validation is turned on and when calling :meth:`~mongoengine.Document.save`. Cascading Saves --------------- If your document contains :class:`~mongoengine.fields.ReferenceField` or :class:`~mongoengine.fields.GenericReferenceField` objects, then by default the :meth:`~mongoengine.Document.save` method will not save any changes to those objects. If you want all references to be saved also, noting each save is a separate query, then passing :attr:`cascade` as True to the save method will cascade any saves. Deleting documents ------------------ To delete a document, call the :meth:`~mongoengine.Document.delete` method. Note that this will only work if the document exists in the database and has a valid :attr:`id`. Document IDs ============ Each document in the database has a unique id. This may be accessed through the :attr:`id` attribute on :class:`~mongoengine.Document` objects. Usually, the id will be generated automatically by the database server when the object is save, meaning that you may only access the :attr:`id` field once a document has been saved:: >>> page = Page(title="Test Page") >>> page.id >>> page.save() >>> page.id ObjectId('123456789abcdef000000000') Alternatively, you may define one of your own fields to be the document's "primary key" by providing ``primary_key=True`` as a keyword argument to a field's constructor. Under the hood, MongoEngine will use this field as the :attr:`id`; in fact :attr:`id` is actually aliased to your primary key field so you may still use :attr:`id` to access the primary key if you want:: >>> class User(Document): ... email = StringField(primary_key=True) ... name = StringField() ... >>> bob = User(email='bob@example.com', name='Bob') >>> bob.save() >>> bob.id == bob.email == 'bob@example.com' True You can also access the document's "primary key" using the :attr:`pk` field, it's an alias to :attr:`id`:: >>> page = Page(title="Another Test Page") >>> page.save() >>> page.id == page.pk True .. note:: If you define your own primary key field, the field implicitly becomes required, so a :class:`~mongoengine.ValidationError` will be thrown if you don't provide it. mongoengine-0.10.6/docs/guide/querying.rst0000664000175000017500000006640712651363712020154 0ustar travistravis===================== Querying the database ===================== :class:`~mongoengine.Document` classes have an :attr:`objects` attribute, which is used for accessing the objects in the database associated with the class. The :attr:`objects` attribute is actually a :class:`~mongoengine.queryset.QuerySetManager`, which creates and returns a new :class:`~mongoengine.queryset.QuerySet` object on access. The :class:`~mongoengine.queryset.QuerySet` object may be iterated over to fetch documents from the database:: # Prints out the names of all the users in the database for user in User.objects: print user.name .. note:: As of MongoEngine 0.8 the querysets utilise a local cache. So iterating it multiple times will only cause a single query. If this is not the desired behaviour you can call :class:`~mongoengine.QuerySet.no_cache` (version **0.8.3+**) to return a non-caching queryset. Filtering queries ================= The query may be filtered by calling the :class:`~mongoengine.queryset.QuerySet` object with field lookup keyword arguments. The keys in the keyword arguments correspond to fields on the :class:`~mongoengine.Document` you are querying:: # This will return a QuerySet that will only iterate over users whose # 'country' field is set to 'uk' uk_users = User.objects(country='uk') Fields on embedded documents may also be referred to using field lookup syntax by using a double-underscore in place of the dot in object attribute access syntax:: # This will return a QuerySet that will only iterate over pages that have # been written by a user whose 'country' field is set to 'uk' uk_pages = Page.objects(author__country='uk') .. note:: (version **0.9.1+**) if your field name is like mongodb operator name (for example type, lte, lt...) and you want to place it at the end of lookup keyword mongoengine automatically prepend $ to it. To avoid this use __ at the end of your lookup keyword. For example if your field name is ``type`` and you want to query by this field you must use ``.objects(user__type__="admin")`` instead of ``.objects(user__type="admin")`` Query operators =============== Operators other than equality may also be used in queries --- just attach the operator name to a key with a double-underscore:: # Only find users whose age is 18 or less young_users = Users.objects(age__lte=18) Available operators are as follows: * ``ne`` -- not equal to * ``lt`` -- less than * ``lte`` -- less than or equal to * ``gt`` -- greater than * ``gte`` -- greater than or equal to * ``not`` -- negate a standard check, may be used before other operators (e.g. ``Q(age__not__mod=5)``) * ``in`` -- value is in list (a list of values should be provided) * ``nin`` -- value is not in list (a list of values should be provided) * ``mod`` -- ``value % x == y``, where ``x`` and ``y`` are two provided values * ``all`` -- every item in list of values provided is in array * ``size`` -- the size of the array is * ``exists`` -- value for field exists String queries -------------- The following operators are available as shortcuts to querying with regular expressions: * ``exact`` -- string field exactly matches value * ``iexact`` -- string field exactly matches value (case insensitive) * ``contains`` -- string field contains value * ``icontains`` -- string field contains value (case insensitive) * ``startswith`` -- string field starts with value * ``istartswith`` -- string field starts with value (case insensitive) * ``endswith`` -- string field ends with value * ``iendswith`` -- string field ends with value (case insensitive) * ``match`` -- performs an $elemMatch so you can match an entire document within an array Geo queries ----------- There are a few special operators for performing geographical queries. The following were added in MongoEngine 0.8 for :class:`~mongoengine.fields.PointField`, :class:`~mongoengine.fields.LineStringField` and :class:`~mongoengine.fields.PolygonField`: * ``geo_within`` -- check if a geometry is within a polygon. For ease of use it accepts either a geojson geometry or just the polygon coordinates eg:: loc.objects(point__geo_within=[[[40, 5], [40, 6], [41, 6], [40, 5]]]) loc.objects(point__geo_within={"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]}) * ``geo_within_box`` -- simplified geo_within searching with a box eg:: loc.objects(point__geo_within_box=[(-125.0, 35.0), (-100.0, 40.0)]) loc.objects(point__geo_within_box=[, ]) * ``geo_within_polygon`` -- simplified geo_within searching within a simple polygon eg:: loc.objects(point__geo_within_polygon=[[40, 5], [40, 6], [41, 6], [40, 5]]) loc.objects(point__geo_within_polygon=[ [ , ] , [ , ] , [ , ] ]) * ``geo_within_center`` -- simplified geo_within the flat circle radius of a point eg:: loc.objects(point__geo_within_center=[(-125.0, 35.0), 1]) loc.objects(point__geo_within_center=[ [ , ] , ]) * ``geo_within_sphere`` -- simplified geo_within the spherical circle radius of a point eg:: loc.objects(point__geo_within_sphere=[(-125.0, 35.0), 1]) loc.objects(point__geo_within_sphere=[ [ , ] , ]) * ``geo_intersects`` -- selects all locations that intersect with a geometry eg:: # Inferred from provided points lists: loc.objects(poly__geo_intersects=[40, 6]) loc.objects(poly__geo_intersects=[[40, 5], [40, 6]]) loc.objects(poly__geo_intersects=[[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]]) # With geoJson style objects loc.objects(poly__geo_intersects={"type": "Point", "coordinates": [40, 6]}) loc.objects(poly__geo_intersects={"type": "LineString", "coordinates": [[40, 5], [40, 6]]}) loc.objects(poly__geo_intersects={"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]]}) * ``near`` -- find all the locations near a given point:: loc.objects(point__near=[40, 5]) loc.objects(point__near={"type": "Point", "coordinates": [40, 5]}) You can also set the maximum and/or the minimum distance in meters as well:: loc.objects(point__near=[40, 5], point__max_distance=1000) loc.objects(point__near=[40, 5], point__min_distance=100) The older 2D indexes are still supported with the :class:`~mongoengine.fields.GeoPointField`: * ``within_distance`` -- provide a list containing a point and a maximum distance (e.g. [(41.342, -87.653), 5]) * ``within_spherical_distance`` -- same as above but using the spherical geo model (e.g. [(41.342, -87.653), 5/earth_radius]) * ``near`` -- order the documents by how close they are to a given point * ``near_sphere`` -- Same as above but using the spherical geo model * ``within_box`` -- filter documents to those within a given bounding box (e.g. [(35.0, -125.0), (40.0, -100.0)]) * ``within_polygon`` -- filter documents to those within a given polygon (e.g. [(41.91,-87.69), (41.92,-87.68), (41.91,-87.65), (41.89,-87.65)]). .. note:: Requires Mongo Server 2.0 * ``max_distance`` -- can be added to your location queries to set a maximum distance. * ``min_distance`` -- can be added to your location queries to set a minimum distance. Querying lists -------------- On most fields, this syntax will look up documents where the field specified matches the given value exactly, but when the field refers to a :class:`~mongoengine.fields.ListField`, a single item may be provided, in which case lists that contain that item will be matched:: class Page(Document): tags = ListField(StringField()) # This will match all pages that have the word 'coding' as an item in the # 'tags' list Page.objects(tags='coding') It is possible to query by position in a list by using a numerical value as a query operator. So if you wanted to find all pages whose first tag was ``db``, you could use the following query:: Page.objects(tags__0='db') If you only want to fetch part of a list eg: you want to paginate a list, then the `slice` operator is required:: # comments - skip 5, limit 10 Page.objects.fields(slice__comments=[5, 10]) For updating documents, if you don't know the position in a list, you can use the $ positional operator :: Post.objects(comments__by="joe").update(**{'inc__comments__$__votes': 1}) However, this doesn't map well to the syntax so you can also use a capital S instead :: Post.objects(comments__by="joe").update(inc__comments__S__votes=1) .. note:: Due to :program:`Mongo`, currently the $ operator only applies to the first matched item in the query. Raw queries ----------- It is possible to provide a raw :mod:`PyMongo` query as a query parameter, which will be integrated directly into the query. This is done using the ``__raw__`` keyword argument:: Page.objects(__raw__={'tags': 'coding'}) .. versionadded:: 0.4 Limiting and skipping results ============================= Just as with traditional ORMs, you may limit the number of results returned or skip a number or results in you query. :meth:`~mongoengine.queryset.QuerySet.limit` and :meth:`~mongoengine.queryset.QuerySet.skip` and methods are available on :class:`~mongoengine.queryset.QuerySet` objects, but the `array-slicing` syntax is preferred for achieving this:: # Only the first 5 people users = User.objects[:5] # All except for the first 5 people users = User.objects[5:] # 5 users, starting from the 10th user found users = User.objects[10:15] You may also index the query to retrieve a single result. If an item at that index does not exists, an :class:`IndexError` will be raised. A shortcut for retrieving the first result and returning :attr:`None` if no result exists is provided (:meth:`~mongoengine.queryset.QuerySet.first`):: >>> # Make sure there are no users >>> User.drop_collection() >>> User.objects[0] IndexError: list index out of range >>> User.objects.first() == None True >>> User(name='Test User').save() >>> User.objects[0] == User.objects.first() True Retrieving unique results ------------------------- To retrieve a result that should be unique in the collection, use :meth:`~mongoengine.queryset.QuerySet.get`. This will raise :class:`~mongoengine.queryset.DoesNotExist` if no document matches the query, and :class:`~mongoengine.queryset.MultipleObjectsReturned` if more than one document matched the query. These exceptions are merged into your document definitions eg: `MyDoc.DoesNotExist` A variation of this method, get_or_create() existed, but it was unsafe. It could not be made safe, because there are no transactions in mongoDB. Other approaches should be investigated, to ensure you don't accidentally duplicate data when using something similar to this method. Therefore it was deprecated in 0.8 and removed in 0.10. Default Document queries ======================== By default, the objects :attr:`~Document.objects` attribute on a document returns a :class:`~mongoengine.queryset.QuerySet` that doesn't filter the collection -- it returns all objects. This may be changed by defining a method on a document that modifies a queryset. The method should accept two arguments -- :attr:`doc_cls` and :attr:`queryset`. The first argument is the :class:`~mongoengine.Document` class that the method is defined on (in this sense, the method is more like a :func:`classmethod` than a regular method), and the second argument is the initial queryset. The method needs to be decorated with :func:`~mongoengine.queryset.queryset_manager` in order for it to be recognised. :: class BlogPost(Document): title = StringField() date = DateTimeField() @queryset_manager def objects(doc_cls, queryset): # This may actually also be done by defining a default ordering for # the document, but this illustrates the use of manager methods return queryset.order_by('-date') You don't need to call your method :attr:`objects` -- you may define as many custom manager methods as you like:: class BlogPost(Document): title = StringField() published = BooleanField() @queryset_manager def live_posts(doc_cls, queryset): return queryset.filter(published=True) BlogPost(title='test1', published=False).save() BlogPost(title='test2', published=True).save() assert len(BlogPost.objects) == 2 assert len(BlogPost.live_posts()) == 1 Custom QuerySets ================ Should you want to add custom methods for interacting with or filtering documents, extending the :class:`~mongoengine.queryset.QuerySet` class may be the way to go. To use a custom :class:`~mongoengine.queryset.QuerySet` class on a document, set ``queryset_class`` to the custom class in a :class:`~mongoengine.Document`'s ``meta`` dictionary:: class AwesomerQuerySet(QuerySet): def get_awesome(self): return self.filter(awesome=True) class Page(Document): meta = {'queryset_class': AwesomerQuerySet} # To call: Page.objects.get_awesome() .. versionadded:: 0.4 Aggregation =========== MongoDB provides some aggregation methods out of the box, but there are not as many as you typically get with an RDBMS. MongoEngine provides a wrapper around the built-in methods and provides some of its own, which are implemented as Javascript code that is executed on the database server. Counting results ---------------- Just as with limiting and skipping results, there is a method on :class:`~mongoengine.queryset.QuerySet` objects -- :meth:`~mongoengine.queryset.QuerySet.count`, but there is also a more Pythonic way of achieving this:: num_users = len(User.objects) Even if len() is the Pythonic way of counting results, keep in mind that if you concerned about performance, :meth:`~mongoengine.queryset.QuerySet.count` is the way to go since it only execute a server side count query, while len() retrieves the results, places them in cache, and finally counts them. If we compare the performance of the two operations, len() is much slower than :meth:`~mongoengine.queryset.QuerySet.count`. Further aggregation ------------------- You may sum over the values of a specific field on documents using :meth:`~mongoengine.queryset.QuerySet.sum`:: yearly_expense = Employee.objects.sum('salary') .. note:: If the field isn't present on a document, that document will be ignored from the sum. To get the average (mean) of a field on a collection of documents, use :meth:`~mongoengine.queryset.QuerySet.average`:: mean_age = User.objects.average('age') As MongoDB provides native lists, MongoEngine provides a helper method to get a dictionary of the frequencies of items in lists across an entire collection -- :meth:`~mongoengine.queryset.QuerySet.item_frequencies`. An example of its use would be generating "tag-clouds":: class Article(Document): tag = ListField(StringField()) # After adding some tagged articles... tag_freqs = Article.objects.item_frequencies('tag', normalize=True) from operator import itemgetter top_tags = sorted(tag_freqs.items(), key=itemgetter(1), reverse=True)[:10] Query efficiency and performance ================================ There are a couple of methods to improve efficiency when querying, reducing the information returned by the query or efficient dereferencing . Retrieving a subset of fields ----------------------------- Sometimes a subset of fields on a :class:`~mongoengine.Document` is required, and for efficiency only these should be retrieved from the database. This issue is especially important for MongoDB, as fields may often be extremely large (e.g. a :class:`~mongoengine.fields.ListField` of :class:`~mongoengine.EmbeddedDocument`\ s, which represent the comments on a blog post. To select only a subset of fields, use :meth:`~mongoengine.queryset.QuerySet.only`, specifying the fields you want to retrieve as its arguments. Note that if fields that are not downloaded are accessed, their default value (or :attr:`None` if no default value is provided) will be given:: >>> class Film(Document): ... title = StringField() ... year = IntField() ... rating = IntField(default=3) ... >>> Film(title='The Shawshank Redemption', year=1994, rating=5).save() >>> f = Film.objects.only('title').first() >>> f.title 'The Shawshank Redemption' >>> f.year # None >>> f.rating # default value 3 .. note:: The :meth:`~mongoengine.queryset.QuerySet.exclude` is the opposite of :meth:`~mongoengine.queryset.QuerySet.only` if you want to exclude a field. If you later need the missing fields, just call :meth:`~mongoengine.Document.reload` on your document. Getting related data -------------------- When iterating the results of :class:`~mongoengine.fields.ListField` or :class:`~mongoengine.fields.DictField` we automatically dereference any :class:`~pymongo.dbref.DBRef` objects as efficiently as possible, reducing the number the queries to mongo. There are times when that efficiency is not enough, documents that have :class:`~mongoengine.fields.ReferenceField` objects or :class:`~mongoengine.fields.GenericReferenceField` objects at the top level are expensive as the number of queries to MongoDB can quickly rise. To limit the number of queries use :func:`~mongoengine.queryset.QuerySet.select_related` which converts the QuerySet to a list and dereferences as efficiently as possible. By default :func:`~mongoengine.queryset.QuerySet.select_related` only dereferences any references to the depth of 1 level. If you have more complicated documents and want to dereference more of the object at once then increasing the :attr:`max_depth` will dereference more levels of the document. Turning off dereferencing ------------------------- Sometimes for performance reasons you don't want to automatically dereference data. To turn off dereferencing of the results of a query use :func:`~mongoengine.queryset.QuerySet.no_dereference` on the queryset like so:: post = Post.objects.no_dereference().first() assert(isinstance(post.author, ObjectId)) You can also turn off all dereferencing for a fixed period by using the :class:`~mongoengine.context_managers.no_dereference` context manager:: with no_dereference(Post) as Post: post = Post.objects.first() assert(isinstance(post.author, ObjectId)) # Outside the context manager dereferencing occurs. assert(isinstance(post.author, User)) Advanced queries ================ Sometimes calling a :class:`~mongoengine.queryset.QuerySet` object with keyword arguments can't fully express the query you want to use -- for example if you need to combine a number of constraints using *and* and *or*. This is made possible in MongoEngine through the :class:`~mongoengine.queryset.Q` class. A :class:`~mongoengine.queryset.Q` object represents part of a query, and can be initialised using the same keyword-argument syntax you use to query documents. To build a complex query, you may combine :class:`~mongoengine.queryset.Q` objects using the ``&`` (and) and ``|`` (or) operators. To use a :class:`~mongoengine.queryset.Q` object, pass it in as the first positional argument to :attr:`Document.objects` when you filter it by calling it with keyword arguments:: # Get published posts Post.objects(Q(published=True) | Q(publish_date__lte=datetime.now())) # Get top posts Post.objects((Q(featured=True) & Q(hits__gte=1000)) | Q(hits__gte=5000)) .. warning:: You have to use bitwise operators. You cannot use ``or``, ``and`` to combine queries as ``Q(a=a) or Q(b=b)`` is not the same as ``Q(a=a) | Q(b=b)``. As ``Q(a=a)`` equates to true ``Q(a=a) or Q(b=b)`` is the same as ``Q(a=a)``. .. _guide-atomic-updates: Atomic updates ============== Documents may be updated atomically by using the :meth:`~mongoengine.queryset.QuerySet.update_one`, :meth:`~mongoengine.queryset.QuerySet.update` and :meth:`~mongoengine.queryset.QuerySet.modify` methods on a :class:`~mongoengine.queryset.QuerySet` or :meth:`~mongoengine.Document.modify` and :meth:`~mongoengine.Document.save` (with :attr:`save_condition` argument) on a :class:`~mongoengine.Document`. There are several different "modifiers" that you may use with these methods: * ``set`` -- set a particular value * ``unset`` -- delete a particular value (since MongoDB v1.3) * ``inc`` -- increment a value by a given amount * ``dec`` -- decrement a value by a given amount * ``push`` -- append a value to a list * ``push_all`` -- append several values to a list * ``pop`` -- remove the first or last element of a list `depending on the value`_ * ``pull`` -- remove a value from a list * ``pull_all`` -- remove several values from a list * ``add_to_set`` -- add value to a list only if its not in the list already .. _depending on the value: http://docs.mongodb.org/manual/reference/operator/update/pop/ The syntax for atomic updates is similar to the querying syntax, but the modifier comes before the field, not after it:: >>> post = BlogPost(title='Test', page_views=0, tags=['database']) >>> post.save() >>> BlogPost.objects(id=post.id).update_one(inc__page_views=1) >>> post.reload() # the document has been changed, so we need to reload it >>> post.page_views 1 >>> BlogPost.objects(id=post.id).update_one(set__title='Example Post') >>> post.reload() >>> post.title 'Example Post' >>> BlogPost.objects(id=post.id).update_one(push__tags='nosql') >>> post.reload() >>> post.tags ['database', 'nosql'] .. note:: If no modifier operator is specified the default will be ``$set``. So the following sentences are identical:: >>> BlogPost.objects(id=post.id).update(title='Example Post') >>> BlogPost.objects(id=post.id).update(set__title='Example Post') .. note:: In version 0.5 the :meth:`~mongoengine.Document.save` runs atomic updates on changed documents by tracking changes to that document. The positional operator allows you to update list items without knowing the index position, therefore making the update a single atomic operation. As we cannot use the `$` syntax in keyword arguments it has been mapped to `S`:: >>> post = BlogPost(title='Test', page_views=0, tags=['database', 'mongo']) >>> post.save() >>> BlogPost.objects(id=post.id, tags='mongo').update(set__tags__S='mongodb') >>> post.reload() >>> post.tags ['database', 'mongodb'] .. note:: Currently only top level lists are handled, future versions of mongodb / pymongo plan to support nested positional operators. See `The $ positional operator `_. Server-side javascript execution ================================ Javascript functions may be written and sent to the server for execution. The result of this is the return value of the Javascript function. This functionality is accessed through the :meth:`~mongoengine.queryset.QuerySet.exec_js` method on :meth:`~mongoengine.queryset.QuerySet` objects. Pass in a string containing a Javascript function as the first argument. The remaining positional arguments are names of fields that will be passed into you Javascript function as its arguments. This allows functions to be written that may be executed on any field in a collection (e.g. the :meth:`~mongoengine.queryset.QuerySet.sum` method, which accepts the name of the field to sum over as its argument). Note that field names passed in in this manner are automatically translated to the names used on the database (set using the :attr:`name` keyword argument to a field constructor). Keyword arguments to :meth:`~mongoengine.queryset.QuerySet.exec_js` are combined into an object called :attr:`options`, which is available in the Javascript function. This may be used for defining specific parameters for your function. Some variables are made available in the scope of the Javascript function: * ``collection`` -- the name of the collection that corresponds to the :class:`~mongoengine.Document` class that is being used; this should be used to get the :class:`Collection` object from :attr:`db` in Javascript code * ``query`` -- the query that has been generated by the :class:`~mongoengine.queryset.QuerySet` object; this may be passed into the :meth:`find` method on a :class:`Collection` object in the Javascript function * ``options`` -- an object containing the keyword arguments passed into :meth:`~mongoengine.queryset.QuerySet.exec_js` The following example demonstrates the intended usage of :meth:`~mongoengine.queryset.QuerySet.exec_js` by defining a function that sums over a field on a document (this functionality is already available through :meth:`~mongoengine.queryset.QuerySet.sum` but is shown here for sake of example):: def sum_field(document, field_name, include_negatives=True): code = """ function(sumField) { var total = 0.0; db[collection].find(query).forEach(function(doc) { var val = doc[sumField]; if (val >= 0.0 || options.includeNegatives) { total += val; } }); return total; } """ options = {'includeNegatives': include_negatives} return document.objects.exec_js(code, field_name, **options) As fields in MongoEngine may use different names in the database (set using the :attr:`db_field` keyword argument to a :class:`Field` constructor), a mechanism exists for replacing MongoEngine field names with the database field names in Javascript code. When accessing a field on a collection object, use square-bracket notation, and prefix the MongoEngine field name with a tilde. The field name that follows the tilde will be translated to the name used in the database. Note that when referring to fields on embedded documents, the name of the :class:`~mongoengine.fields.EmbeddedDocumentField`, followed by a dot, should be used before the name of the field on the embedded document. The following example shows how the substitutions are made:: class Comment(EmbeddedDocument): content = StringField(db_field='body') class BlogPost(Document): title = StringField(db_field='doctitle') comments = ListField(EmbeddedDocumentField(Comment), name='cs') # Returns a list of dictionaries. Each dictionary contains a value named # "document", which corresponds to the "title" field on a BlogPost, and # "comment", which corresponds to an individual comment. The substitutions # made are shown in the comments. BlogPost.objects.exec_js(""" function() { var comments = []; db[collection].find(query).forEach(function(doc) { // doc[~comments] -> doc["cs"] var docComments = doc[~comments]; for (var i = 0; i < docComments.length; i++) { // doc[~comments][i] -> doc["cs"][i] var comment = doc[~comments][i]; comments.push({ // doc[~title] -> doc["doctitle"] 'document': doc[~title], // comment[~comments.content] -> comment["body"] 'comment': comment[~comments.content] }); } }); return comments; } """) mongoengine-0.10.6/docs/guide/text-indexes.rst0000664000175000017500000000224112651363712020714 0ustar travistravis=========== Text Search =========== After MongoDB 2.4 version, supports search documents by text indexes. Defining a Document with text index =================================== Use the *$* prefix to set a text index, Look the declaration:: class News(Document): title = StringField() content = StringField() is_active = BooleanField() meta = {'indexes': [ {'fields': ['$title', "$content"], 'default_language': 'english', 'weights': {'title': 10, 'content': 2} } ]} Querying ======== Saving a document:: News(title="Using mongodb text search", content="Testing text search").save() News(title="MongoEngine 0.9 released", content="Various improvements").save() Next, start a text search using :attr:`QuerySet.search_text` method:: document = News.objects.search_text('testing').first() document.title # may be: "Using mongodb text search" document = News.objects.search_text('released').first() document.title # may be: "MongoEngine 0.9 released" Ordering by text score ====================== :: objects = News.objects.search('mongo').order_by('$text_score') mongoengine-0.10.6/docs/guide/connecting.rst0000664000175000017500000001060012651363712020420 0ustar travistravis.. _guide-connecting: ===================== Connecting to MongoDB ===================== To connect to a running instance of :program:`mongod`, use the :func:`~mongoengine.connect` function. The first argument is the name of the database to connect to:: from mongoengine import connect connect('project1') By default, MongoEngine assumes that the :program:`mongod` instance is running on **localhost** on port **27017**. If MongoDB is running elsewhere, you should provide the :attr:`host` and :attr:`port` arguments to :func:`~mongoengine.connect`:: connect('project1', host='192.168.1.35', port=12345) If the database requires authentication, :attr:`username` and :attr:`password` arguments should be provided:: connect('project1', username='webapp', password='pwd123') URI style connections are also supported -- just supply the URI as the :attr:`host` to :func:`~mongoengine.connect`:: connect('project1', host='mongodb://localhost/database_name') .. note:: Database, username and password from URI string overrides corresponding parameters in :func:`~mongoengine.connect`: :: connect( name='test', username='user', password='12345', host='mongodb://admin:qwerty@localhost/production' ) will establish connection to ``production`` database using ``admin`` username and ``qwerty`` password. ReplicaSets =========== MongoEngine supports :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`. To use them, please use an URI style connection and provide the ``replicaSet`` name in the connection kwargs. Read preferences are supported through the connection or via individual queries by passing the read_preference :: Bar.objects().read_preference(ReadPreference.PRIMARY) Bar.objects(read_preference=ReadPreference.PRIMARY) Multiple Databases ================== Multiple database support was added in MongoEngine 0.6. To use multiple databases you can use :func:`~mongoengine.connect` and provide an `alias` name for the connection - if no `alias` is provided then "default" is used. In the background this uses :func:`~mongoengine.register_connection` to store the data and you can register all aliases up front if required. Individual documents can also support multiple databases by providing a `db_alias` in their meta data. This allows :class:`~pymongo.dbref.DBRef` objects to point across databases and collections. Below is an example schema, using 3 different databases to store data:: class User(Document): name = StringField() meta = {"db_alias": "user-db"} class Book(Document): name = StringField() meta = {"db_alias": "book-db"} class AuthorBooks(Document): author = ReferenceField(User) book = ReferenceField(Book) meta = {"db_alias": "users-books-db"} Context Managers ================ Sometimes you may want to switch the database or collection to query against for a class. For example, archiving older data into a separate database for performance reasons or writing functions that dynamically choose collections to write document to. Switch Database --------------- The :class:`~mongoengine.context_managers.switch_db` context manager allows you to change the database alias for a given class allowing quick and easy access the same User document across databases:: from mongoengine.context_managers import switch_db class User(Document): name = StringField() meta = {"db_alias": "user-db"} with switch_db(User, 'archive-user-db') as User: User(name="Ross").save() # Saves the 'archive-user-db' Switch Collection ----------------- The :class:`~mongoengine.context_managers.switch_collection` context manager allows you to change the collection for a given class allowing quick and easy access the same Group document across collection:: from mongoengine.context_managers import switch_collection class Group(Document): name = StringField() Group(name="test").save() # Saves in the default db with switch_collection(Group, 'group2000') as Group: Group(name="hello Group 2000 collection!").save() # Saves in group2000 collection .. note:: Make sure any aliases have been registered with :func:`~mongoengine.register_connection` or :func:`~mongoengine.connect` before using the context manager. mongoengine-0.10.6/docs/guide/defining-documents.rst0000664000175000017500000007034712651363712022071 0ustar travistravis================== Defining documents ================== In MongoDB, a **document** is roughly equivalent to a **row** in an RDBMS. When working with relational databases, rows are stored in **tables**, which have a strict **schema** that the rows follow. MongoDB stores documents in **collections** rather than tables --- the principal difference is that no schema is enforced at a database level. Defining a document's schema ============================ MongoEngine allows you to define schemata for documents as this helps to reduce coding errors, and allows for utility methods to be defined on fields which may be present. To define a schema for a document, create a class that inherits from :class:`~mongoengine.Document`. Fields are specified by adding **field objects** as class attributes to the document class:: from mongoengine import * import datetime class Page(Document): title = StringField(max_length=200, required=True) date_modified = DateTimeField(default=datetime.datetime.now) As BSON (the binary format for storing data in mongodb) is order dependent, documents are serialized based on their field order. Dynamic document schemas ======================== One of the benefits of MongoDb is dynamic schemas for a collection, whilst data should be planned and organised (after all explicit is better than implicit!) there are scenarios where having dynamic / expando style documents is desirable. :class:`~mongoengine.DynamicDocument` documents work in the same way as :class:`~mongoengine.Document` but any data / attributes set to them will also be saved :: from mongoengine import * class Page(DynamicDocument): title = StringField(max_length=200, required=True) # Create a new page and add tags >>> page = Page(title='Using MongoEngine') >>> page.tags = ['mongodb', 'mongoengine'] >>> page.save() >>> Page.objects(tags='mongoengine').count() >>> 1 .. note:: There is one caveat on Dynamic Documents: fields cannot start with `_` Dynamic fields are stored in creation order *after* any declared fields. Fields ====== By default, fields are not required. To make a field mandatory, set the :attr:`required` keyword argument of a field to ``True``. Fields also may have validation constraints available (such as :attr:`max_length` in the example above). Fields may also take default values, which will be used if a value is not provided. Default values may optionally be a callable, which will be called to retrieve the value (such as in the above example). The field types available are as follows: * :class:`~mongoengine.fields.BinaryField` * :class:`~mongoengine.fields.BooleanField` * :class:`~mongoengine.fields.ComplexDateTimeField` * :class:`~mongoengine.fields.DateTimeField` * :class:`~mongoengine.fields.DecimalField` * :class:`~mongoengine.fields.DictField` * :class:`~mongoengine.fields.DynamicField` * :class:`~mongoengine.fields.EmailField` * :class:`~mongoengine.fields.EmbeddedDocumentField` * :class:`~mongoengine.fields.FileField` * :class:`~mongoengine.fields.FloatField` * :class:`~mongoengine.fields.GenericEmbeddedDocumentField` * :class:`~mongoengine.fields.GenericReferenceField` * :class:`~mongoengine.fields.GeoPointField` * :class:`~mongoengine.fields.ImageField` * :class:`~mongoengine.fields.IntField` * :class:`~mongoengine.fields.ListField` * :class:`~mongoengine.fields.MapField` * :class:`~mongoengine.fields.ObjectIdField` * :class:`~mongoengine.fields.ReferenceField` * :class:`~mongoengine.fields.SequenceField` * :class:`~mongoengine.fields.SortedListField` * :class:`~mongoengine.fields.StringField` * :class:`~mongoengine.fields.URLField` * :class:`~mongoengine.fields.UUIDField` * :class:`~mongoengine.fields.PointField` * :class:`~mongoengine.fields.LineStringField` * :class:`~mongoengine.fields.PolygonField` * :class:`~mongoengine.fields.MultiPointField` * :class:`~mongoengine.fields.MultiLineStringField` * :class:`~mongoengine.fields.MultiPolygonField` Field arguments --------------- Each field type can be customized by keyword arguments. The following keyword arguments can be set on all fields: :attr:`db_field` (Default: None) The MongoDB field name. :attr:`required` (Default: False) If set to True and the field is not set on the document instance, a :class:`~mongoengine.ValidationError` will be raised when the document is validated. :attr:`default` (Default: None) A value to use when no value is set for this field. The definition of default parameters follow `the general rules on Python `__, which means that some care should be taken when dealing with default mutable objects (like in :class:`~mongoengine.fields.ListField` or :class:`~mongoengine.fields.DictField`):: class ExampleFirst(Document): # Default an empty list values = ListField(IntField(), default=list) class ExampleSecond(Document): # Default a set of values values = ListField(IntField(), default=lambda: [1,2,3]) class ExampleDangerous(Document): # This can make an .append call to add values to the default (and all the following objects), # instead to just an object values = ListField(IntField(), default=[1,2,3]) .. note:: Unsetting a field with a default value will revert back to the default. :attr:`unique` (Default: False) When True, no documents in the collection will have the same value for this field. :attr:`unique_with` (Default: None) A field name (or list of field names) that when taken together with this field, will not have two documents in the collection with the same value. :attr:`primary_key` (Default: False) When True, use this field as a primary key for the collection. `DictField` and `EmbeddedDocuments` both support being the primary key for a document. .. note:: If set, this field is also accessible through the `pk` field. :attr:`choices` (Default: None) An iterable (e.g. a list or tuple) of choices to which the value of this field should be limited. Can be either be a nested tuples of value (stored in mongo) and a human readable key :: SIZE = (('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ('XL', 'Extra Large'), ('XXL', 'Extra Extra Large')) class Shirt(Document): size = StringField(max_length=3, choices=SIZE) Or a flat iterable just containing values :: SIZE = ('S', 'M', 'L', 'XL', 'XXL') class Shirt(Document): size = StringField(max_length=3, choices=SIZE) :attr:`**kwargs` (Optional) You can supply additional metadata as arbitrary additional keyword arguments. You can not override existing attributes, however. Common choices include `help_text` and `verbose_name`, commonly used by form and widget libraries. List fields ----------- MongoDB allows storing lists of items. To add a list of items to a :class:`~mongoengine.Document`, use the :class:`~mongoengine.fields.ListField` field type. :class:`~mongoengine.fields.ListField` takes another field object as its first argument, which specifies which type elements may be stored within the list:: class Page(Document): tags = ListField(StringField(max_length=50)) Embedded documents ------------------ MongoDB has the ability to embed documents within other documents. Schemata may be defined for these embedded documents, just as they may be for regular documents. To create an embedded document, just define a document as usual, but inherit from :class:`~mongoengine.EmbeddedDocument` rather than :class:`~mongoengine.Document`:: class Comment(EmbeddedDocument): content = StringField() To embed the document within another document, use the :class:`~mongoengine.fields.EmbeddedDocumentField` field type, providing the embedded document class as the first argument:: class Page(Document): comments = ListField(EmbeddedDocumentField(Comment)) comment1 = Comment(content='Good work!') comment2 = Comment(content='Nice article!') page = Page(comments=[comment1, comment2]) Dictionary Fields ----------------- Often, an embedded document may be used instead of a dictionary -- generally this is recommended as dictionaries don't support validation or custom field types. However, sometimes you will not know the structure of what you want to store; in this situation a :class:`~mongoengine.fields.DictField` is appropriate:: class SurveyResponse(Document): date = DateTimeField() user = ReferenceField(User) answers = DictField() survey_response = SurveyResponse(date=datetime.now(), user=request.user) response_form = ResponseForm(request.POST) survey_response.answers = response_form.cleaned_data() survey_response.save() Dictionaries can store complex data, other dictionaries, lists, references to other objects, so are the most flexible field type available. Reference fields ---------------- References may be stored to other documents in the database using the :class:`~mongoengine.fields.ReferenceField`. Pass in another document class as the first argument to the constructor, then simply assign document objects to the field:: class User(Document): name = StringField() class Page(Document): content = StringField() author = ReferenceField(User) john = User(name="John Smith") john.save() post = Page(content="Test Page") post.author = john post.save() The :class:`User` object is automatically turned into a reference behind the scenes, and dereferenced when the :class:`Page` object is retrieved. To add a :class:`~mongoengine.fields.ReferenceField` that references the document being defined, use the string ``'self'`` in place of the document class as the argument to :class:`~mongoengine.fields.ReferenceField`'s constructor. To reference a document that has not yet been defined, use the name of the undefined document as the constructor's argument:: class Employee(Document): name = StringField() boss = ReferenceField('self') profile_page = ReferenceField('ProfilePage') class ProfilePage(Document): content = StringField() .. _one-to-many-with-listfields: One to Many with ListFields ''''''''''''''''''''''''''' If you are implementing a one to many relationship via a list of references, then the references are stored as DBRefs and to query you need to pass an instance of the object to the query:: class User(Document): name = StringField() class Page(Document): content = StringField() authors = ListField(ReferenceField(User)) bob = User(name="Bob Jones").save() john = User(name="John Smith").save() Page(content="Test Page", authors=[bob, john]).save() Page(content="Another Page", authors=[john]).save() # Find all pages Bob authored Page.objects(authors__in=[bob]) # Find all pages that both Bob and John have authored Page.objects(authors__all=[bob, john]) # Remove Bob from the authors for a page. Page.objects(id='...').update_one(pull__authors=bob) # Add John to the authors for a page. Page.objects(id='...').update_one(push__authors=john) Dealing with deletion of referred documents ''''''''''''''''''''''''''''''''''''''''''' By default, MongoDB doesn't check the integrity of your data, so deleting documents that other documents still hold references to will lead to consistency issues. Mongoengine's :class:`ReferenceField` adds some functionality to safeguard against these kinds of database integrity problems, providing each reference with a delete rule specification. A delete rule is specified by supplying the :attr:`reverse_delete_rule` attributes on the :class:`ReferenceField` definition, like this:: class ProfilePage(Document): ... employee = ReferenceField('Employee', reverse_delete_rule=mongoengine.CASCADE) The declaration in this example means that when an :class:`Employee` object is removed, the :class:`ProfilePage` that references that employee is removed as well. If a whole batch of employees is removed, all profile pages that are linked are removed as well. Its value can take any of the following constants: :const:`mongoengine.DO_NOTHING` This is the default and won't do anything. Deletes are fast, but may cause database inconsistency or dangling references. :const:`mongoengine.DENY` Deletion is denied if there still exist references to the object being deleted. :const:`mongoengine.NULLIFY` Any object's fields still referring to the object being deleted are removed (using MongoDB's "unset" operation), effectively nullifying the relationship. :const:`mongoengine.CASCADE` Any object containing fields that are referring to the object being deleted are deleted first. :const:`mongoengine.PULL` Removes the reference to the object (using MongoDB's "pull" operation) from any object's fields of :class:`~mongoengine.fields.ListField` (:class:`~mongoengine.fields.ReferenceField`). .. warning:: A safety note on setting up these delete rules! Since the delete rules are not recorded on the database level by MongoDB itself, but instead at runtime, in-memory, by the MongoEngine module, it is of the upmost importance that the module that declares the relationship is loaded **BEFORE** the delete is invoked. If, for example, the :class:`Employee` object lives in the :mod:`payroll` app, and the :class:`ProfilePage` in the :mod:`people` app, it is extremely important that the :mod:`people` app is loaded before any employee is removed, because otherwise, MongoEngine could never know this relationship exists. In Django, be sure to put all apps that have such delete rule declarations in their :file:`models.py` in the :const:`INSTALLED_APPS` tuple. .. warning:: Signals are not triggered when doing cascading updates / deletes - if this is required you must manually handle the update / delete. Generic reference fields '''''''''''''''''''''''' A second kind of reference field also exists, :class:`~mongoengine.fields.GenericReferenceField`. This allows you to reference any kind of :class:`~mongoengine.Document`, and hence doesn't take a :class:`~mongoengine.Document` subclass as a constructor argument:: class Link(Document): url = StringField() class Post(Document): title = StringField() class Bookmark(Document): bookmark_object = GenericReferenceField() link = Link(url='http://hmarr.com/mongoengine/') link.save() post = Post(title='Using MongoEngine') post.save() Bookmark(bookmark_object=link).save() Bookmark(bookmark_object=post).save() .. note:: Using :class:`~mongoengine.fields.GenericReferenceField`\ s is slightly less efficient than the standard :class:`~mongoengine.fields.ReferenceField`\ s, so if you will only be referencing one document type, prefer the standard :class:`~mongoengine.fields.ReferenceField`. Uniqueness constraints ---------------------- MongoEngine allows you to specify that a field should be unique across a collection by providing ``unique=True`` to a :class:`~mongoengine.fields.Field`\ 's constructor. If you try to save a document that has the same value for a unique field as a document that is already in the database, a :class:`~mongoengine.NotUniqueError` will be raised. You may also specify multi-field uniqueness constraints by using :attr:`unique_with`, which may be either a single field name, or a list or tuple of field names:: class User(Document): username = StringField(unique=True) first_name = StringField() last_name = StringField(unique_with='first_name') Skipping Document validation on save ------------------------------------ You can also skip the whole document validation process by setting ``validate=False`` when calling the :meth:`~mongoengine.document.Document.save` method:: class Recipient(Document): name = StringField() email = EmailField() recipient = Recipient(name='admin', email='root@localhost') recipient.save() # will raise a ValidationError while recipient.save(validate=False) # won't Document collections ==================== Document classes that inherit **directly** from :class:`~mongoengine.Document` will have their own **collection** in the database. The name of the collection is by default the name of the class, converted to lowercase (so in the example above, the collection would be called `page`). If you need to change the name of the collection (e.g. to use MongoEngine with an existing database), then create a class dictionary attribute called :attr:`meta` on your document, and set :attr:`collection` to the name of the collection that you want your document class to use:: class Page(Document): title = StringField(max_length=200, required=True) meta = {'collection': 'cmsPage'} Capped collections ------------------ A :class:`~mongoengine.Document` may use a **Capped Collection** by specifying :attr:`max_documents` and :attr:`max_size` in the :attr:`meta` dictionary. :attr:`max_documents` is the maximum number of documents that is allowed to be stored in the collection, and :attr:`max_size` is the maximum size of the collection in bytes. :attr:`max_size` is rounded up to the next multiple of 256 by MongoDB internally and mongoengine before. Use also a multiple of 256 to avoid confusions. If :attr:`max_size` is not specified and :attr:`max_documents` is, :attr:`max_size` defaults to 10485760 bytes (10MB). The following example shows a :class:`Log` document that will be limited to 1000 entries and 2MB of disk space:: class Log(Document): ip_address = StringField() meta = {'max_documents': 1000, 'max_size': 2000000} .. defining-indexes_ Indexes ======= You can specify indexes on collections to make querying faster. This is done by creating a list of index specifications called :attr:`indexes` in the :attr:`~mongoengine.Document.meta` dictionary, where an index specification may either be a single field name, a tuple containing multiple field names, or a dictionary containing a full index definition. A direction may be specified on fields by prefixing the field name with a **+** (for ascending) or a **-** sign (for descending). Note that direction only matters on multi-field indexes. Text indexes may be specified by prefixing the field name with a **$**. Hashed indexes may be specified by prefixing the field name with a **#**:: class Page(Document): category = IntField() title = StringField() rating = StringField() created = DateTimeField() meta = { 'indexes': [ 'title', '$title', # text index '#title', # hashed index ('title', '-rating'), ('category', '_cls'), { 'fields': ['created'], 'expireAfterSeconds': 3600 } ] } If a dictionary is passed then the following options are available: :attr:`fields` (Default: None) The fields to index. Specified in the same format as described above. :attr:`cls` (Default: True) If you have polymorphic models that inherit and have :attr:`allow_inheritance` turned on, you can configure whether the index should have the :attr:`_cls` field added automatically to the start of the index. :attr:`sparse` (Default: False) Whether the index should be sparse. :attr:`unique` (Default: False) Whether the index should be unique. :attr:`expireAfterSeconds` (Optional) Allows you to automatically expire data from a collection by setting the time in seconds to expire the a field. .. note:: Inheritance adds extra fields indices see: :ref:`document-inheritance`. Global index default options ---------------------------- There are a few top level defaults for all indexes that can be set:: class Page(Document): title = StringField() rating = StringField() meta = { 'index_options': {}, 'index_background': True, 'index_drop_dups': True, 'index_cls': False } :attr:`index_options` (Optional) Set any default index options - see the `full options list `_ :attr:`index_background` (Optional) Set the default value for if an index should be indexed in the background :attr:`index_cls` (Optional) A way to turn off a specific index for _cls. :attr:`index_drop_dups` (Optional) Set the default value for if an index should drop duplicates .. note:: Since MongoDB 3.0 drop_dups is not supported anymore. Raises a Warning and has no effect Compound Indexes and Indexing sub documents ------------------------------------------- Compound indexes can be created by adding the Embedded field or dictionary field name to the index definition. Sometimes its more efficient to index parts of Embedded / dictionary fields, in this case use 'dot' notation to identify the value to index eg: `rank.title` .. _geospatial-indexes: Geospatial indexes ------------------ The best geo index for mongodb is the new "2dsphere", which has an improved spherical model and provides better performance and more options when querying. The following fields will explicitly add a "2dsphere" index: - :class:`~mongoengine.fields.PointField` - :class:`~mongoengine.fields.LineStringField` - :class:`~mongoengine.fields.PolygonField` - :class:`~mongoengine.fields.MultiPointField` - :class:`~mongoengine.fields.MultiLineStringField` - :class:`~mongoengine.fields.MultiPolygonField` As "2dsphere" indexes can be part of a compound index, you may not want the automatic index but would prefer a compound index. In this example we turn off auto indexing and explicitly declare a compound index on ``location`` and ``datetime``:: class Log(Document): location = PointField(auto_index=False) datetime = DateTimeField() meta = { 'indexes': [[("location", "2dsphere"), ("datetime", 1)]] } Pre MongoDB 2.4 Geo ''''''''''''''''''' .. note:: For MongoDB < 2.4 this is still current, however the new 2dsphere index is a big improvement over the previous 2D model - so upgrading is advised. Geospatial indexes will be automatically created for all :class:`~mongoengine.fields.GeoPointField`\ s It is also possible to explicitly define geospatial indexes. This is useful if you need to define a geospatial index on a subfield of a :class:`~mongoengine.fields.DictField` or a custom field that contains a point. To create a geospatial index you must prefix the field with the ***** sign. :: class Place(Document): location = DictField() meta = { 'indexes': [ '*location.point', ], } Time To Live indexes -------------------- A special index type that allows you to automatically expire data from a collection after a given period. See the official `ttl `_ documentation for more information. A common usecase might be session data:: class Session(Document): created = DateTimeField(default=datetime.now) meta = { 'indexes': [ {'fields': ['created'], 'expireAfterSeconds': 3600} ] } .. warning:: TTL indexes happen on the MongoDB server and not in the application code, therefore no signals will be fired on document deletion. If you need signals to be fired on deletion, then you must handle the deletion of Documents in your application code. Comparing Indexes ----------------- Use :func:`mongoengine.Document.compare_indexes` to compare actual indexes in the database to those that your document definitions define. This is useful for maintenance purposes and ensuring you have the correct indexes for your schema. Ordering ======== A default ordering can be specified for your :class:`~mongoengine.queryset.QuerySet` using the :attr:`ordering` attribute of :attr:`~mongoengine.Document.meta`. Ordering will be applied when the :class:`~mongoengine.queryset.QuerySet` is created, and can be overridden by subsequent calls to :meth:`~mongoengine.queryset.QuerySet.order_by`. :: from datetime import datetime class BlogPost(Document): title = StringField() published_date = DateTimeField() meta = { 'ordering': ['-published_date'] } blog_post_1 = BlogPost(title="Blog Post #1") blog_post_1.published_date = datetime(2010, 1, 5, 0, 0 ,0) blog_post_2 = BlogPost(title="Blog Post #2") blog_post_2.published_date = datetime(2010, 1, 6, 0, 0 ,0) blog_post_3 = BlogPost(title="Blog Post #3") blog_post_3.published_date = datetime(2010, 1, 7, 0, 0 ,0) blog_post_1.save() blog_post_2.save() blog_post_3.save() # get the "first" BlogPost using default ordering # from BlogPost.meta.ordering latest_post = BlogPost.objects.first() assert latest_post.title == "Blog Post #3" # override default ordering, order BlogPosts by "published_date" first_post = BlogPost.objects.order_by("+published_date").first() assert first_post.title == "Blog Post #1" Shard keys ========== If your collection is sharded, then you need to specify the shard key as a tuple, using the :attr:`shard_key` attribute of :attr:`~mongoengine.Document.meta`. This ensures that the shard key is sent with the query when calling the :meth:`~mongoengine.document.Document.save` or :meth:`~mongoengine.document.Document.update` method on an existing :class:`~mongoengine.Document` instance:: class LogEntry(Document): machine = StringField() app = StringField() timestamp = DateTimeField() data = StringField() meta = { 'shard_key': ('machine', 'timestamp',) } .. _document-inheritance: Document inheritance ==================== To create a specialised type of a :class:`~mongoengine.Document` you have defined, you may subclass it and add any extra fields or methods you may need. As this is new class is not a direct subclass of :class:`~mongoengine.Document`, it will not be stored in its own collection; it will use the same collection as its superclass uses. This allows for more convenient and efficient retrieval of related documents -- all you need do is set :attr:`allow_inheritance` to True in the :attr:`meta` data for a document.:: # Stored in a collection named 'page' class Page(Document): title = StringField(max_length=200, required=True) meta = {'allow_inheritance': True} # Also stored in the collection named 'page' class DatedPage(Page): date = DateTimeField() .. note:: From 0.8 onwards :attr:`allow_inheritance` defaults to False, meaning you must set it to True to use inheritance. Working with existing data -------------------------- As MongoEngine no longer defaults to needing :attr:`_cls`, you can quickly and easily get working with existing data. Just define the document to match the expected schema in your database :: # Will work with data in an existing collection named 'cmsPage' class Page(Document): title = StringField(max_length=200, required=True) meta = { 'collection': 'cmsPage' } If you have wildly varying schemas then using a :class:`~mongoengine.DynamicDocument` might be more appropriate, instead of defining all possible field types. If you use :class:`~mongoengine.Document` and the database contains data that isn't defined then that data will be stored in the `document._data` dictionary. Abstract classes ================ If you want to add some extra functionality to a group of Document classes but you don't need or want the overhead of inheritance you can use the :attr:`abstract` attribute of :attr:`~mongoengine.Document.meta`. This won't turn on :ref:`document-inheritance` but will allow you to keep your code DRY:: class BaseDocument(Document): meta = { 'abstract': True, } def check_permissions(self): ... class User(BaseDocument): ... Now the User class will have access to the inherited `check_permissions` method and won't store any of the extra `_cls` information. mongoengine-0.10.6/docs/guide/installing.rst0000664000175000017500000000173712651363712020450 0ustar travistravis====================== Installing MongoEngine ====================== To use MongoEngine, you will need to download `MongoDB `_ and ensure it is running in an accessible location. You will also need `PyMongo `_ to use MongoEngine, but if you install MongoEngine using setuptools, then the dependencies will be handled for you. MongoEngine is available on PyPI, so to use it you can use :program:`pip`: .. code-block:: console $ pip install mongoengine Alternatively, if you don't have setuptools installed, `download it from PyPi `_ and run .. code-block:: console $ python setup.py install To use the bleeding-edge version of MongoEngine, you can get the source from `GitHub `_ and install it as above: .. code-block:: console $ git clone git://github.com/mongoengine/mongoengine $ cd mongoengine $ python setup.py install mongoengine-0.10.6/docs/tutorial.rst0000664000175000017500000003106312651363712017045 0ustar travistravis======== Tutorial ======== This tutorial introduces **MongoEngine** by means of example --- we will walk through how to create a simple **Tumblelog** application. A Tumblelog is a type of blog where posts are not constrained to being conventional text-based posts. As well as text-based entries, users may post images, links, videos, etc. For simplicity's sake, we'll stick to text, image and link entries in our application. As the purpose of this tutorial is to introduce MongoEngine, we'll focus on the data-modelling side of the application, leaving out a user interface. Getting started =============== Before we start, make sure that a copy of MongoDB is running in an accessible location --- running it locally will be easier, but if that is not an option then it may be run on a remote server. If you haven't installed mongoengine, simply use pip to install it like so:: $ pip install mongoengine Before we can start using MongoEngine, we need to tell it how to connect to our instance of :program:`mongod`. For this we use the :func:`~mongoengine.connect` function. If running locally the only argument we need to provide is the name of the MongoDB database to use:: from mongoengine import * connect('tumblelog') There are lots of options for connecting to MongoDB, for more information about them see the :ref:`guide-connecting` guide. Defining our documents ====================== MongoDB is *schemaless*, which means that no schema is enforced by the database --- we may add and remove fields however we want and MongoDB won't complain. This makes life a lot easier in many regards, especially when there is a change to the data model. However, defining schemata for our documents can help to iron out bugs involving incorrect types or missing fields, and also allow us to define utility methods on our documents in the same way that traditional :abbr:`ORMs (Object-Relational Mappers)` do. In our Tumblelog application we need to store several different types of information. We will need to have a collection of **users**, so that we may link posts to an individual. We also need to store our different types of **posts** (eg: text, image and link) in the database. To aid navigation of our Tumblelog, posts may have **tags** associated with them, so that the list of posts shown to the user may be limited to posts that have been assigned a specific tag. Finally, it would be nice if **comments** could be added to posts. We'll start with **users**, as the other document models are slightly more involved. Users ----- Just as if we were using a relational database with an ORM, we need to define which fields a :class:`User` may have, and what types of data they might store:: class User(Document): email = StringField(required=True) first_name = StringField(max_length=50) last_name = StringField(max_length=50) This looks similar to how the structure of a table would be defined in a regular ORM. The key difference is that this schema will never be passed on to MongoDB --- this will only be enforced at the application level, making future changes easy to manage. Also, the User documents will be stored in a MongoDB *collection* rather than a table. Posts, Comments and Tags ------------------------ Now we'll think about how to store the rest of the information. If we were using a relational database, we would most likely have a table of **posts**, a table of **comments** and a table of **tags**. To associate the comments with individual posts, we would put a column in the comments table that contained a foreign key to the posts table. We'd also need a link table to provide the many-to-many relationship between posts and tags. Then we'd need to address the problem of storing the specialised post-types (text, image and link). There are several ways we can achieve this, but each of them have their problems --- none of them stand out as particularly intuitive solutions. Posts ^^^^^ Happily mongoDB *isn't* a relational database, so we're not going to do it that way. As it turns out, we can use MongoDB's schemaless nature to provide us with a much nicer solution. We will store all of the posts in *one collection* and each post type will only store the fields it needs. If we later want to add video posts, we don't have to modify the collection at all, we just *start using* the new fields we need to support video posts. This fits with the Object-Oriented principle of *inheritance* nicely. We can think of :class:`Post` as a base class, and :class:`TextPost`, :class:`ImagePost` and :class:`LinkPost` as subclasses of :class:`Post`. In fact, MongoEngine supports this kind of modelling out of the box --- all you need do is turn on inheritance by setting :attr:`allow_inheritance` to True in the :attr:`meta`:: class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) meta = {'allow_inheritance': True} class TextPost(Post): content = StringField() class ImagePost(Post): image_path = StringField() class LinkPost(Post): link_url = StringField() We are storing a reference to the author of the posts using a :class:`~mongoengine.fields.ReferenceField` object. These are similar to foreign key fields in traditional ORMs, and are automatically translated into references when they are saved, and dereferenced when they are loaded. Tags ^^^^ Now that we have our Post models figured out, how will we attach tags to them? MongoDB allows us to store lists of items natively, so rather than having a link table, we can just store a list of tags in each post. So, for both efficiency and simplicity's sake, we'll store the tags as strings directly within the post, rather than storing references to tags in a separate collection. Especially as tags are generally very short (often even shorter than a document's id), this denormalisation won't impact very strongly on the size of our database. So let's take a look that the code our modified :class:`Post` class:: class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) tags = ListField(StringField(max_length=30)) The :class:`~mongoengine.fields.ListField` object that is used to define a Post's tags takes a field object as its first argument --- this means that you can have lists of any type of field (including lists). .. note:: We don't need to modify the specialised post types as they all inherit from :class:`Post`. Comments ^^^^^^^^ A comment is typically associated with *one* post. In a relational database, to display a post with its comments, we would have to retrieve the post from the database, then query the database again for the comments associated with the post. This works, but there is no real reason to be storing the comments separately from their associated posts, other than to work around the relational model. Using MongoDB we can store the comments as a list of *embedded documents* directly on a post document. An embedded document should be treated no differently that a regular document; it just doesn't have its own collection in the database. Using MongoEngine, we can define the structure of embedded documents, along with utility methods, in exactly the same way we do with regular documents:: class Comment(EmbeddedDocument): content = StringField() name = StringField(max_length=120) We can then store a list of comment documents in our post document:: class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) tags = ListField(StringField(max_length=30)) comments = ListField(EmbeddedDocumentField(Comment)) Handling deletions of references ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The :class:`~mongoengine.fields.ReferenceField` object takes a keyword `reverse_delete_rule` for handling deletion rules if the reference is deleted. To delete all the posts if a user is deleted set the rule:: class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User, reverse_delete_rule=CASCADE) tags = ListField(StringField(max_length=30)) comments = ListField(EmbeddedDocumentField(Comment)) See :class:`~mongoengine.fields.ReferenceField` for more information. .. note:: MapFields and DictFields currently don't support automatic handling of deleted references Adding data to our Tumblelog ============================ Now that we've defined how our documents will be structured, let's start adding some documents to the database. Firstly, we'll need to create a :class:`User` object:: ross = User(email='ross@example.com', first_name='Ross', last_name='Lawley').save() .. note:: We could have also defined our user using attribute syntax:: ross = User(email='ross@example.com') ross.first_name = 'Ross' ross.last_name = 'Lawley' ross.save() Now that we've got our user in the database, let's add a couple of posts:: post1 = TextPost(title='Fun with MongoEngine', author=john) post1.content = 'Took a look at MongoEngine today, looks pretty cool.' post1.tags = ['mongodb', 'mongoengine'] post1.save() post2 = LinkPost(title='MongoEngine Documentation', author=ross) post2.link_url = 'http://docs.mongoengine.com/' post2.tags = ['mongoengine'] post2.save() .. note:: If you change a field on a object that has already been saved, then call :meth:`save` again, the document will be updated. Accessing our data ================== So now we've got a couple of posts in our database, how do we display them? Each document class (i.e. any class that inherits either directly or indirectly from :class:`~mongoengine.Document`) has an :attr:`objects` attribute, which is used to access the documents in the database collection associated with that class. So let's see how we can get our posts' titles:: for post in Post.objects: print post.title Retrieving type-specific information ------------------------------------ This will print the titles of our posts, one on each line. But What if we want to access the type-specific data (link_url, content, etc.)? One way is simply to use the :attr:`objects` attribute of a subclass of :class:`Post`:: for post in TextPost.objects: print post.content Using TextPost's :attr:`objects` attribute only returns documents that were created using :class:`TextPost`. Actually, there is a more general rule here: the :attr:`objects` attribute of any subclass of :class:`~mongoengine.Document` only looks for documents that were created using that subclass or one of its subclasses. So how would we display all of our posts, showing only the information that corresponds to each post's specific type? There is a better way than just using each of the subclasses individually. When we used :class:`Post`'s :attr:`objects` attribute earlier, the objects being returned weren't actually instances of :class:`Post` --- they were instances of the subclass of :class:`Post` that matches the post's type. Let's look at how this works in practice:: for post in Post.objects: print post.title print '=' * len(post.title) if isinstance(post, TextPost): print post.content if isinstance(post, LinkPost): print 'Link:', post.link_url print This would print the title of each post, followed by the content if it was a text post, and "Link: " if it was a link post. Searching our posts by tag -------------------------- The :attr:`objects` attribute of a :class:`~mongoengine.Document` is actually a :class:`~mongoengine.queryset.QuerySet` object. This lazily queries the database only when you need the data. It may also be filtered to narrow down your query. Let's adjust our query so that only posts with the tag "mongodb" are returned:: for post in Post.objects(tags='mongodb'): print post.title There are also methods available on :class:`~mongoengine.queryset.QuerySet` objects that allow different results to be returned, for example, calling :meth:`first` on the :attr:`objects` attribute will return a single document, the first matched by the query you provide. Aggregation functions may also be used on :class:`~mongoengine.queryset.QuerySet` objects:: num_posts = Post.objects(tags='mongodb').count() print 'Found %d posts with tag "mongodb"' % num_posts Learning more about mongoengine ------------------------------- If you got this far you've made a great start, so well done! The next step on your mongoengine journey is the `full user guide `_, where you can learn indepth about how to use mongoengine and mongodb. mongoengine-0.10.6/docs/index.rst0000664000175000017500000000415512651363712016313 0ustar travistravis============================== MongoEngine User Documentation ============================== **MongoEngine** is an Object-Document Mapper, written in Python for working with MongoDB. To install it, simply run .. code-block:: console $ pip install -U mongoengine :doc:`tutorial` A quick tutorial building a tumblelog to get you up and running with MongoEngine. :doc:`guide/index` The Full guide to MongoEngine --- from modeling documents to storing files, from querying for data to firing signals and *everything* between. :doc:`apireference` The complete API documentation --- the innards of documents, querysets and fields. :doc:`upgrade` How to upgrade MongoEngine. :doc:`django` Using MongoEngine and Django Community --------- To get help with using MongoEngine, use the `MongoEngine Users mailing list `_ or the ever popular `stackoverflow `_. Contributing ------------ **Yes please!** We are always looking for contributions, additions and improvements. The source is available on `GitHub `_ and contributions are always encouraged. Contributions can be as simple as minor tweaks to this documentation, the website or the core. To contribute, fork the project on `GitHub `_ and send a pull request. Changes ------- See the :doc:`changelog` for a full list of changes to MongoEngine and :doc:`upgrade` for upgrade information. .. note:: Always read and test the `upgrade `_ documentation before putting updates live in production **;)** Offline Reading --------------- Download the docs in `pdf `_ or `epub `_ formats for offline reading. .. toctree:: :maxdepth: 1 :numbered: :hidden: tutorial guide/index apireference changelog upgrade django Indices and tables ------------------ * :ref:`genindex` * :ref:`modindex` * :ref:`search` mongoengine-0.10.6/docs/changelog.rst0000664000175000017500000010425212651363712017132 0ustar travistravis========= Changelog ========= Changes in 0.10.6 ================= - Add support for mocking MongoEngine based on mongomock. #1151 - Fixed not being able to run tests on Windows. #1153 - Allow creation of sparse compound indexes. #1114 Changes in 0.10.5 ================= - Fix for reloading of strict with special fields. #1156 Changes in 0.10.4 ================= - SaveConditionError is now importable from the top level package. #1165 - upsert_one method added. #1157 Changes in 0.10.3 ================= - Fix `read_preference` (it had chaining issues with PyMongo 2.x and it didn't work at all with PyMongo 3.x) #1042 Changes in 0.10.2 ================= - Allow shard key to point to a field in an embedded document. #551 - Allow arbirary metadata in fields. #1129 - ReferenceFields now support abstract document types. #837 Changes in 0.10.1 ================= - Fix infinite recursion with CASCADE delete rules under specific conditions. #1046 - Fix CachedReferenceField bug when loading cached docs as DBRef but failing to save them. #1047 - Fix ignored chained options #842 - Document save's save_condition error raises `SaveConditionError` exception #1070 - Fix Document.reload for DynamicDocument. #1050 - StrictDict & SemiStrictDict are shadowed at init time. #1105 - Remove test dependencies (nose and rednose) from install dependencies list. #1079 - Recursively build query when using elemMatch operator. #1130 - Fix instance back references for lists of embedded documents. #1131 Changes in 0.10.0 ================= - Django support was removed and will be available as a separate extension. #958 - Allow to load undeclared field with meta attribute 'strict': False #957 - Support for PyMongo 3+ #946 - Removed get_or_create() deprecated since 0.8.0. #300 - Improve Document._created status when switch collection and db #1020 - Queryset update doesn't go through field validation #453 - Added support for specifying authentication source as option `authSource` in URI. #967 - Fixed mark_as_changed to handle higher/lower level fields changed. #927 - ListField of embedded docs doesn't set the _instance attribute when iterating over it #914 - Support += and *= for ListField #595 - Use sets for populating dbrefs to dereference - Fixed unpickled documents replacing the global field's list. #888 - Fixed storage of microseconds in ComplexDateTimeField and unused separator option. #910 - Don't send a "cls" option to ensureIndex (related to https://jira.mongodb.org/browse/SERVER-769) - Fix for updating sorting in SortedListField. #978 - Added __ support to escape field name in fields lookup keywords that match operators names #949 - Fix for issue where FileField deletion did not free space in GridFS. - No_dereference() not respected on embedded docs containing reference. #517 - Document save raise an exception if save_condition fails #1005 - Fixes some internal _id handling issue. #961 - Updated URL and Email Field regex validators, added schemes argument to URLField validation. #652 - Capped collection multiple of 256. #1011 - Added `BaseQuerySet.aggregate_sum` and `BaseQuerySet.aggregate_average` methods. - Fix for delete with write_concern {'w': 0}. #1008 - Allow dynamic lookup for more than two parts. #882 - Added support for min_distance on geo queries. #831 - Allow to add custom metadata to fields #705 Changes in 0.9.0 ================ - Update FileField when creating a new file #714 - Added `EmbeddedDocumentListField` for Lists of Embedded Documents. #826 - ComplexDateTimeField should fall back to None when null=True #864 - Request Support for $min, $max Field update operators #863 - `BaseDict` does not follow `setdefault` #866 - Add support for $type operator # 766 - Fix tests for pymongo 2.8+ #877 - No module named 'django.utils.importlib' (Django dev) #872 - Field Choices Now Accept Subclasses of Documents - Ensure Indexes before Each Save #812 - Generate Unique Indices for Lists of EmbeddedDocuments #358 - Sparse fields #515 - write_concern not in params of Collection#remove #801 - Better BaseDocument equality check when not saved #798 - OperationError: Shard Keys are immutable. Tried to update id even though the document is not yet saved #771 - with_limit_and_skip for count should default like in pymongo #759 - Fix storing value of precision attribute in DecimalField #787 - Set attribute to None does not work (at least for fields with default values) #734 - Querying by a field defined in a subclass raises InvalidQueryError #744 - Add Support For MongoDB 2.6.X's maxTimeMS #778 - abstract shouldn't be inherited in EmbeddedDocument # 789 - Allow specifying the '_cls' as a field for indexes #397 - Stop ensure_indexes running on a secondaries unless connection is through mongos #746 - Not overriding default values when loading a subset of fields #399 - Saving document doesn't create new fields in existing collection #620 - Added `Queryset.aggregate` wrapper to aggregation framework #703 - Added support to show original model fields on to_json calls instead of db_field #697 - Added Queryset.search_text to Text indexes searchs #700 - Fixed tests for Django 1.7 #696 - Follow ReferenceFields in EmbeddedDocuments with select_related #690 - Added preliminary support for text indexes #680 - Added `elemMatch` operator as well - `match` is too obscure #653 - Added support for progressive JPEG #486 #548 - Allow strings to be used in index creation #675 - Fixed EmbeddedDoc weakref proxy issue #592 - Fixed nested reference field distinct error #583 - Fixed change tracking on nested MapFields #539 - Dynamic fields in embedded documents now visible to queryset.only() / qs.exclude() #425 #507 - Add authentication_source option to register_connection #178 #464 #573 #580 #590 - Implemented equality between Documents and DBRefs #597 - Fixed ReferenceField inside nested ListFields dereferencing problem #368 - Added the ability to reload specific document fields #100 - Added db_alias support and fixes for custom map/reduce output #586 - post_save signal now has access to delta information about field changes #594 #589 - Don't query with $orderby for qs.get() #600 - Fix id shard key save issue #636 - Fixes issue with recursive embedded document errors #557 - Fix clear_changed_fields() clearing unsaved documents bug #602 - Removing support for Django 1.4.x, pymongo 2.5.x, pymongo 2.6.x. - Removing support for Python < 2.6.6 - Fixed $maxDistance location for geoJSON $near queries with MongoDB 2.6+ #664 - QuerySet.modify() and Document.modify() methods to provide find_and_modify() like behaviour #677 #773 - Added support for the using() method on a queryset #676 - PYPY support #673 - Connection pooling #674 - Avoid to open all documents from cursors in an if stmt #655 - Ability to clear the ordering #657 - Raise NotUniqueError in Document.update() on pymongo.errors.DuplicateKeyError #626 - Slots - memory improvements #625 - Fixed incorrectly split a query key when it ends with "_" #619 - Geo docs updates #613 - Workaround a dateutil bug #608 - Conditional save for atomic-style operations #511 - Allow dynamic dictionary-style field access #559 - Increase email field length to accommodate new TLDs #726 - index_cls is ignored when deciding to set _cls as index prefix #733 - Make 'db' argument to connection optional #737 - Allow atomic update for the entire `DictField` #742 - Added MultiPointField, MultiLineField, MultiPolygonField - Fix multiple connections aliases being rewritten #748 - Fixed a few instances where reverse_delete_rule was written as reverse_delete_rules. #791 - Make `in_bulk()` respect `no_dereference()` #775 - Handle None from model __str__; Fixes #753 #754 - _get_changed_fields fix for embedded documents with id field. #925 Changes in 0.8.7 ================ - Calling reload on deleted / nonexistent documents raises DoesNotExist (#538) - Stop ensure_indexes running on a secondaries (#555) - Fix circular import issue with django auth (#531) (#545) Changes in 0.8.6 ================ - Fix django auth import (#531) Changes in 0.8.5 ================ - Fix multi level nested fields getting marked as changed (#523) - Django 1.6 login fix (#522) (#527) - Django 1.6 session fix (#509) - EmbeddedDocument._instance is now set when setting the attribute (#506) - Fixed EmbeddedDocument with ReferenceField equality issue (#502) - Fixed GenericReferenceField serialization order (#499) - Fixed count and none bug (#498) - Fixed bug with .only() and DictField with digit keys (#496) - Added user_permissions to Django User object (#491, #492) - Fix updating Geo Location fields (#488) - Fix handling invalid dict field value (#485) - Added app_label to MongoUser (#484) - Use defaults when host and port are passed as None (#483) - Fixed distinct casting issue with ListField of EmbeddedDocuments (#470) - Fixed Django 1.6 sessions (#454, #480) Changes in 0.8.4 ================ - Remove database name necessity in uri connection schema (#452) - Fixed "$pull" semantics for nested ListFields (#447) - Allow fields to be named the same as query operators (#445) - Updated field filter logic - can now exclude subclass fields (#443) - Fixed dereference issue with embedded listfield referencefields (#439) - Fixed slice when using inheritance causing fields to be excluded (#437) - Fixed ._get_db() attribute after a Document.switch_db() (#441) - Dynamic Fields store and recompose Embedded Documents / Documents correctly (#449) - Handle dynamic fieldnames that look like digits (#434) - Added get_user_document and improve mongo_auth module (#423) - Added str representation of GridFSProxy (#424) - Update transform to handle docs erroneously passed to unset (#416) - Fixed indexing - turn off _cls (#414) - Fixed dereference threading issue in ComplexField.__get__ (#412) - Fixed QuerySetNoCache.count() caching (#410) - Don't follow references in _get_changed_fields (#422, #417) - Allow args and kwargs to be passed through to_json (#420) Changes in 0.8.3 ================ - Fixed EmbeddedDocuments with `id` also storing `_id` (#402) - Added get_proxy_object helper to filefields (#391) - Added QuerySetNoCache and QuerySet.no_cache() for lower memory consumption (#365) - Fixed sum and average mapreduce dot notation support (#375, #376, #393) - Fixed as_pymongo to return the id (#386) - Document.select_related() now respects `db_alias` (#377) - Reload uses shard_key if applicable (#384) - Dynamic fields are ordered based on creation and stored in _fields_ordered (#396) **Potential breaking change:** http://docs.mongoengine.org/en/latest/upgrade.html#to-0-8-3 - Fixed pickling dynamic documents `_dynamic_fields` (#387) - Fixed ListField setslice and delslice dirty tracking (#390) - Added Django 1.5 PY3 support (#392) - Added match ($elemMatch) support for EmbeddedDocuments (#379) - Fixed weakref being valid after reload (#374) - Fixed queryset.get() respecting no_dereference (#373) - Added full_result kwarg to update (#380) Changes in 0.8.2 ================ - Added compare_indexes helper (#361) - Fixed cascading saves which weren't turned off as planned (#291) - Fixed Datastructures so instances are a Document or EmbeddedDocument (#363) - Improved cascading saves write performance (#361) - Fixed ambiguity and differing behaviour regarding field defaults (#349) - ImageFields now include PIL error messages if invalid error (#353) - Added lock when calling doc.Delete() for when signals have no sender (#350) - Reload forces read preference to be PRIMARY (#355) - Querysets are now lest restrictive when querying duplicate fields (#332, #333) - FileField now honouring db_alias (#341) - Removed customised __set__ change tracking in ComplexBaseField (#344) - Removed unused var in _get_changed_fields (#347) - Added pre_save_post_validation signal (#345) - DateTimeField now auto converts valid datetime isostrings into dates (#343) - DateTimeField now uses dateutil for parsing if available (#343) - Fixed Doc.objects(read_preference=X) not setting read preference (#352) - Django session ttl index expiry fixed (#329) - Fixed pickle.loads (#342) - Documentation fixes Changes in 0.8.1 ================ - Fixed Python 2.6 django auth importlib issue (#326) - Fixed pickle unsaved document regression (#327) Changes in 0.8.0 ================ - Fixed querying ReferenceField custom_id (#317) - Fixed pickle issues with collections (#316) - Added `get_next_value` preview for SequenceFields (#319) - Added no_sub_classes context manager and queryset helper (#312) - Querysets now utilises a local cache - Changed __len__ behaviour in the queryset (#247, #311) - Fixed querying string versions of ObjectIds issue with ReferenceField (#307) - Added $setOnInsert support for upserts (#308) - Upserts now possible with just query parameters (#309) - Upserting is the only way to ensure docs are saved correctly (#306) - Fixed register_delete_rule inheritance issue - Fix cloning of sliced querysets (#303) - Fixed update_one write concern (#302) - Updated minimum requirement for pymongo to 2.5 - Add support for new geojson fields, indexes and queries (#299) - If values cant be compared mark as changed (#287) - Ensure as_pymongo() and to_json honour only() and exclude() (#293) - Document serialization uses field order to ensure a strict order is set (#296) - DecimalField now stores as float not string (#289) - UUIDField now stores as a binary by default (#292) - Added Custom User Model for Django 1.5 (#285) - Cascading saves now default to off (#291) - ReferenceField now store ObjectId's by default rather than DBRef (#290) - Added ImageField support for inline replacements (#86) - Added SequenceField.set_next_value(value) helper (#159) - Updated .only() behaviour - now like exclude it is chainable (#202) - Added with_limit_and_skip support to count() (#235) - Objects queryset manager now inherited (#256) - Updated connection to use MongoClient (#262, #274) - Fixed db_alias and inherited Documents (#143) - Documentation update for document errors (#124) - Deprecated `get_or_create` (#35) - Updated inheritable objects created by upsert now contain _cls (#118) - Added support for creating documents with embedded documents in a single operation (#6) - Added to_json and from_json to Document (#1) - Added to_json and from_json to QuerySet (#131) - Updated index creation now tied to Document class (#102) - Added none() to queryset (#127) - Updated SequenceFields to allow post processing of the calculated counter value (#141) - Added clean method to documents for pre validation data cleaning (#60) - Added support setting for read prefrence at a query level (#157) - Added _instance to EmbeddedDocuments pointing to the parent (#139) - Inheritance is off by default (#122) - Remove _types and just use _cls for inheritance (#148) - Only allow QNode instances to be passed as query objects (#199) - Dynamic fields are now validated on save (#153) (#154) - Added support for multiple slices and made slicing chainable. (#170) (#190) (#191) - Fixed GridFSProxy __getattr__ behaviour (#196) - Fix Django timezone support (#151) - Simplified Q objects, removed QueryTreeTransformerVisitor (#98) (#171) - FileFields now copyable (#198) - Querysets now return clones and are no longer edit in place (#56) - Added support for $maxDistance (#179) - Uses getlasterror to test created on updated saves (#163) - Fixed inheritance and unique index creation (#140) - Fixed reverse delete rule with inheritance (#197) - Fixed validation for GenericReferences which haven't been dereferenced - Added switch_db context manager (#106) - Added switch_db method to document instances (#106) - Added no_dereference context manager (#82) (#61) - Added switch_collection context manager (#220) - Added switch_collection method to document instances (#220) - Added support for compound primary keys (#149) (#121) - Fixed overriding objects with custom manager (#58) - Added no_dereference method for querysets (#82) (#61) - Undefined data should not override instance methods (#49) - Added Django Group and Permission (#142) - Added Doc class and pk to Validation messages (#69) - Fixed Documents deleted via a queryset don't call any signals (#105) - Added the "get_decoded" method to the MongoSession class (#216) - Fixed invalid choices error bubbling (#214) - Updated Save so it calls $set and $unset in a single operation (#211) - Fixed inner queryset looping (#204) Changes in 0.7.10 ================= - Fix UnicodeEncodeError for dbref (#278) - Allow construction using positional parameters (#268) - Updated EmailField length to support long domains (#243) - Added 64-bit integer support (#251) - Added Django sessions TTL support (#224) - Fixed issue with numerical keys in MapField(EmbeddedDocumentField()) (#240) - Fixed clearing _changed_fields for complex nested embedded documents (#237, #239, #242) - Added "id" back to _data dictionary (#255) - Only mark a field as changed if the value has changed (#258) - Explicitly check for Document instances when dereferencing (#261) - Fixed order_by chaining issue (#265) - Added dereference support for tuples (#250) - Resolve field name to db field name when using distinct(#260, #264, #269) - Added kwargs to doc.save to help interop with django (#223, #270) - Fixed cloning querysets in PY3 - Int fields no longer unset in save when changed to 0 (#272) - Fixed ReferenceField query chaining bug fixed (#254) Changes in 0.7.9 ================ - Better fix handling for old style _types - Embedded SequenceFields follow collection naming convention Changes in 0.7.8 ================ - Fix sequence fields in embedded documents (#166) - Fix query chaining with .order_by() (#176) - Added optional encoding and collection config for Django sessions (#180, #181, #183) - Fixed EmailField so can add extra validation (#173, #174, #187) - Fixed bulk inserts can now handle custom pk's (#192) - Added as_pymongo method to return raw or cast results from pymongo (#193) Changes in 0.7.7 ================ - Fix handling for old style _types Changes in 0.7.6 ================ - Unicode fix for repr (#133) - Allow updates with match operators (#144) - Updated URLField - now can have a override the regex (#136) - Allow Django AuthenticationBackends to work with Django user (hmarr/mongoengine#573) - Fixed reload issue with ReferenceField where dbref=False (#138) Changes in 0.7.5 ================ - ReferenceFields with dbref=False use ObjectId instead of strings (#134) See ticket for upgrade notes (#134) Changes in 0.7.4 ================ - Fixed index inheritance issues - firmed up testcases (#123) (#125) Changes in 0.7.3 ================ - Reverted EmbeddedDocuments meta handling - now can turn off inheritance (#119) Changes in 0.7.2 ================ - Update index spec generation so its not destructive (#113) Changes in 0.7.1 ================ - Fixed index spec inheritance (#111) Changes in 0.7.0 ================ - Updated queryset.delete so you can use with skip / limit (#107) - Updated index creation allows kwargs to be passed through refs (#104) - Fixed Q object merge edge case (#109) - Fixed reloading on sharded documents (hmarr/mongoengine#569) - Added NotUniqueError for duplicate keys (#62) - Added custom collection / sequence naming for SequenceFields (#92) - Fixed UnboundLocalError in composite index with pk field (#88) - Updated ReferenceField's to optionally store ObjectId strings this will become the default in 0.8 (#89) - Added FutureWarning - save will default to `cascade=False` in 0.8 - Added example of indexing embedded document fields (#75) - Fixed ImageField resizing when forcing size (#80) - Add flexibility for fields handling bad data (#78) - Embedded Documents no longer handle meta definitions - Use weakref proxies in base lists / dicts (#74) - Improved queryset filtering (hmarr/mongoengine#554) - Fixed Dynamic Documents and Embedded Documents (hmarr/mongoengine#561) - Fixed abstract classes and shard keys (#64) - Fixed Python 2.5 support - Added Python 3 support (thanks to Laine Heron) Changes in 0.6.20 ================= - Added support for distinct and db_alias (#59) - Improved support for chained querysets when constraining the same fields (hmarr/mongoengine#554) - Fixed BinaryField lookup re (#48) Changes in 0.6.19 ================= - Added Binary support to UUID (#47) - Fixed MapField lookup for fields without declared lookups (#46) - Fixed BinaryField python value issue (#48) - Fixed SequenceField non numeric value lookup (#41) - Fixed queryset manager issue (#52) - Fixed FileField comparision (hmarr/mongoengine#547) Changes in 0.6.18 ================= - Fixed recursion loading bug in _get_changed_fields Changes in 0.6.17 ================= - Fixed issue with custom queryset manager expecting explict variable names Changes in 0.6.16 ================= - Fixed issue where db_alias wasn't inherited Changes in 0.6.15 ================= - Updated validation error messages - Added support for null / zero / false values in item_frequencies - Fixed cascade save edge case - Fixed geo index creation through reference fields - Added support for args / kwargs when using @queryset_manager - Deref list custom id fix Changes in 0.6.14 ================= - Fixed error dict with nested validation - Fixed Int/Float fields and not equals None - Exclude tests from installation - Allow tuples for index meta - Fixed use of str in instance checks - Fixed unicode support in transform update - Added support for add_to_set and each Changes in 0.6.13 ================= - Fixed EmbeddedDocument db_field validation issue - Fixed StringField unicode issue - Fixes __repr__ modifying the cursor Changes in 0.6.12 ================= - Fixes scalar lookups for primary_key - Fixes error with _delta handling DBRefs Changes in 0.6.11 ================= - Fixed inconsistency handling None values field attrs - Fixed map_field embedded db_field issue - Fixed .save() _delta issue with DbRefs - Fixed Django TestCase - Added cmp to Embedded Document - Added PULL reverse_delete_rule - Fixed CASCADE delete bug - Fixed db_field data load error - Fixed recursive save with FileField Changes in 0.6.10 ================= - Fixed basedict / baselist to return super(..) - Promoted BaseDynamicField to DynamicField Changes in 0.6.9 ================ - Fixed sparse indexes on inherited docs - Removed FileField auto deletion, needs more work maybe 0.7 Changes in 0.6.8 ================ - Fixed FileField losing reference when no default set - Removed possible race condition from FileField (grid_file) - Added assignment to save, can now do: `b = MyDoc(**kwargs).save()` - Added support for pull operations on nested EmbeddedDocuments - Added support for choices with GenericReferenceFields - Added support for choices with GenericEmbeddedDocumentFields - Fixed Django 1.4 sessions first save data loss - FileField now automatically delete files on .delete() - Fix for GenericReference to_mongo method - Fixed connection regression - Updated Django User document, now allows inheritance Changes in 0.6.7 ================ - Fixed indexing on '_id' or 'pk' or 'id' - Invalid data from the DB now raises a InvalidDocumentError - Cleaned up the Validation Error - docs and code - Added meta `auto_create_index` so you can disable index creation - Added write concern options to inserts - Fixed typo in meta for index options - Bug fix Read preference now passed correctly - Added support for File like objects for GridFS - Fix for #473 - Dereferencing abstracts Changes in 0.6.6 ================ - Django 1.4 fixed (finally) - Added tests for Django Changes in 0.6.5 ================ - More Django updates Changes in 0.6.4 ================ - Refactored connection / fixed replicasetconnection - Bug fix for unknown connection alias error message - Sessions support Django 1.3 and Django 1.4 - Minor fix for ReferenceField Changes in 0.6.3 ================ - Updated sessions for Django 1.4 - Bug fix for updates where listfields contain embedded documents - Bug fix for collection naming and mixins Changes in 0.6.2 ================ - Updated documentation for ReplicaSet connections - Hack round _types issue with SERVER-5247 - querying other arrays may also cause problems. Changes in 0.6.1 ================ - Fix for replicaSet connections Changes in 0.6 ============== - Added FutureWarning to inherited classes not declaring 'allow_inheritance' as the default will change in 0.7 - Added support for covered indexes when inheritance is off - No longer always upsert on save for items with a '_id' - Error raised if update doesn't have an operation - DeReferencing is now thread safe - Errors raised if trying to perform a join in a query - Updates can now take __raw__ queries - Added custom 2D index declarations - Added replicaSet connection support - Updated deprecated imports from pymongo (safe for pymongo 2.2) - Added uri support for connections - Added scalar for efficiently returning partial data values (aliased to values_list) - Fixed limit skip bug - Improved Inheritance / Mixin - Added sharding support - Added pymongo 2.1 support - Fixed Abstract documents can now declare indexes - Added db_alias support to individual documents - Fixed GridFS documents can now be pickled - Added Now raises an InvalidDocumentError when declaring multiple fields with the same db_field - Added InvalidQueryError when calling with_id with a filter - Added support for DBRefs in distinct() - Fixed issue saving False booleans - Fixed issue with dynamic documents deltas - Added Reverse Delete Rule support to ListFields - MapFields aren't supported - Added customisable cascade kwarg options - Fixed Handle None values for non-required fields - Removed Document._get_subclasses() - no longer required - Fixed bug requiring subclasses when not actually needed - Fixed deletion of dynamic data - Added support for the $elementMatch operator - Added reverse option to SortedListFields - Fixed dereferencing - multi directional list dereferencing - Fixed issue creating indexes with recursive embedded documents - Fixed recursive lookup in _unique_with_indexes - Fixed passing ComplexField defaults to constructor for ReferenceFields - Fixed validation of DictField Int keys - Added optional cascade saving - Fixed dereferencing - max_depth now taken into account - Fixed document mutation saving issue - Fixed positional operator when replacing embedded documents - Added Non-Django Style choices back (you can have either) - Fixed __repr__ of a sliced queryset - Added recursive validation error of documents / complex fields - Fixed breaking during queryset iteration - Added pre and post bulk-insert signals - Added ImageField - requires PIL - Fixed Reference Fields can be None in get_or_create / queries - Fixed accessing pk on an embedded document - Fixed calling a queryset after drop_collection now recreates the collection - Add field name to validation exception messages - Added UUID field - Improved efficiency of .get() - Updated ComplexFields so if required they won't accept empty lists / dicts - Added spec file for rpm-based distributions - Fixed ListField so it doesnt accept strings - Added DynamicDocument and EmbeddedDynamicDocument classes for expando schemas Changes in v0.5.2 ================= - A Robust Circular reference bugfix Changes in v0.5.1 ================= - Fixed simple circular reference bug Changes in v0.5 =============== - Added InvalidDocumentError - so Document core methods can't be overwritten - Added GenericEmbeddedDocument - so you can embed any type of embeddable document - Added within_polygon support - for those with mongodb 1.9 - Updated sum / average to use map_reduce as db.eval doesn't work in sharded environments - Added where() - filter to allowing users to specify query expressions as Javascript - Added SequenceField - for creating sequential counters - Added update() convenience method to a document - Added cascading saves - so changes to Referenced documents are saved on .save() - Added select_related() support - Added support for the positional operator - Updated geo index checking to be recursive and check in embedded documents - Updated default collection naming convention - Added Document Mixin support - Fixed queryet __repr__ mid iteration - Added hint() support, so can tell Mongo the proper index to use for the query - Fixed issue with inconsistent setting of _cls breaking inherited referencing - Added help_text and verbose_name to fields to help with some form libs - Updated item_frequencies to handle embedded document lookups - Added delta tracking now only sets / unsets explicitly changed fields - Fixed saving so sets updated values rather than overwrites - Added ComplexDateTimeField - Handles datetimes correctly with microseconds - Added ComplexBaseField - for improved flexibility and performance - Added get_FIELD_display() method for easy choice field displaying - Added queryset.slave_okay(enabled) method - Updated queryset.timeout(enabled) and queryset.snapshot(enabled) to be chainable - Added insert method for bulk inserts - Added blinker signal support - Added query_counter context manager for tests - Added map_reduce method item_frequencies and set as default (as db.eval doesn't work in sharded environments) - Added inline_map_reduce option to map_reduce - Updated connection exception so it provides more info on the cause. - Added searching multiple levels deep in ``DictField`` - Added ``DictField`` entries containing strings to use matching operators - Added ``MapField``, similar to ``DictField`` - Added Abstract Base Classes - Added Custom Objects Managers - Added sliced subfields updating - Added ``NotRegistered`` exception if dereferencing ``Document`` not in the registry - Added a write concern for ``save``, ``update``, ``update_one`` and ``get_or_create`` - Added slicing / subarray fetching controls - Fixed various unique index and other index issues - Fixed threaded connection issues - Added spherical geospatial query operators - Updated queryset to handle latest version of pymongo map_reduce now requires an output. - Added ``Document`` __hash__, __ne__ for pickling - Added ``FileField`` optional size arg for read method - Fixed ``FileField`` seek and tell methods for reading files - Added ``QuerySet.clone`` to support copying querysets - Fixed item_frequencies when using name thats the same as a native js function - Added reverse delete rules - Fixed issue with unset operation - Fixed Q-object bug - Added ``QuerySet.all_fields`` resets previous .only() and .exclude() - Added ``QuerySet.exclude`` - Added django style choices - Fixed order and filter issue - Added ``QuerySet.only`` subfield support - Added creation_counter to ``BaseField`` allowing fields to be sorted in the way the user has specified them - Fixed various errors - Added many tests Changes in v0.4 =============== - Added ``GridFSStorage`` Django storage backend - Added ``FileField`` for GridFS support - New Q-object implementation, which is no longer based on Javascript - Added ``SortedListField`` - Added ``EmailField`` - Added ``GeoPointField`` - Added ``exact`` and ``iexact`` match operators to ``QuerySet`` - Added ``get_document_or_404`` and ``get_list_or_404`` Django shortcuts - Added new query operators for Geo queries - Added ``not`` query operator - Added new update operators: ``pop`` and ``add_to_set`` - Added ``__raw__`` query parameter - Added support for custom querysets - Fixed document inheritance primary key issue - Added support for querying by array element position - Base class can now be defined for ``DictField`` - Fixed MRO error that occured on document inheritance - Added ``QuerySet.distinct``, ``QuerySet.create``, ``QuerySet.snapshot``, ``QuerySet.timeout`` and ``QuerySet.all`` - Subsequent calls to ``connect()`` now work - Introduced ``min_length`` for ``StringField`` - Fixed multi-process connection issue - Other minor fixes Changes in v0.3 =============== - Added MapReduce support - Added ``contains``, ``startswith`` and ``endswith`` query operators (and case-insensitive versions that are prefixed with 'i') - Deprecated fields' ``name`` parameter, replaced with ``db_field`` - Added ``QuerySet.only`` for only retrieving specific fields - Added ``QuerySet.in_bulk()`` for bulk querying using ids - ``QuerySet``\ s now have a ``rewind()`` method, which is called automatically when the iterator is exhausted, allowing ``QuerySet``\ s to be reused - Added ``DictField`` - Added ``URLField`` - Added ``DecimalField`` - Added ``BinaryField`` - Added ``GenericReferenceField`` - Added ``get()`` and ``get_or_create()`` methods to ``QuerySet`` - ``ReferenceField``\ s may now reference the document they are defined on (recursive references) and documents that have not yet been defined - ``Document`` objects may now be compared for equality (equal if _ids are equal and documents are of same type) - ``QuerySet`` update methods now have an ``upsert`` parameter - Added field name substitution for Javascript code (allows the user to use the Python names for fields in JS, which are later substituted for the real field names) - ``Q`` objects now support regex querying - Fixed bug where referenced documents within lists weren't properly dereferenced - ``ReferenceField``\ s may now be queried using their _id - Fixed bug where ``EmbeddedDocuments`` couldn't be non-polymorphic - ``queryset_manager`` functions now accept two arguments -- the document class as the first and the queryset as the second - Fixed bug where ``QuerySet.exec_js`` ignored ``Q`` objects - Other minor fixes Changes in v0.2.2 ================= - Fixed bug that prevented indexes from being used on ``ListField``\ s - ``Document.filter()`` added as an alias to ``Document.__call__()`` - ``validate()`` may now be used on ``EmbeddedDocument``\ s Changes in v0.2.1 ================= - Added a MongoEngine backend for Django sessions - Added ``force_insert`` to ``Document.save()`` - Improved querying syntax for ``ListField`` and ``EmbeddedDocumentField`` - Added support for user-defined primary keys (``_id`` in MongoDB) Changes in v0.2 =============== - Added ``Q`` class for building advanced queries - Added ``QuerySet`` methods for atomic updates to documents - Fields may now specify ``unique=True`` to enforce uniqueness across a collection - Added option for default document ordering - Fixed bug in index definitions Changes in v0.1.3 ================= - Added Django authentication backend - Added ``Document.meta`` support for indexes, which are ensured just before querying takes place - A few minor bugfixes Changes in v0.1.2 ================= - Query values may be processed before before being used in queries - Made connections lazy - Fixed bug in Document dictionary-style access - Added ``BooleanField`` - Added ``Document.reload()`` method Changes in v0.1.1 ================= - Documents may now use capped collections mongoengine-0.10.6/docs/upgrade.rst0000664000175000017500000004102312651363712016626 0ustar travistravis######### Upgrading ######### 0.9.0 ***** The 0.8.7 package on pypi was corrupted. If upgrading from 0.8.7 to 0.9.0 please follow: :: pip uninstall pymongo pip uninstall mongoengine pip install pymongo==2.8 pip install mongoengine 0.8.7 ***** Calling reload on deleted / nonexistent documents now raises a DoesNotExist exception. 0.8.2 to 0.8.3 ************** Minor change that may impact users: DynamicDocument fields are now stored in creation order after any declared fields. Previously they were stored alphabetically. 0.7 to 0.8 ********** There have been numerous backwards breaking changes in 0.8. The reasons for these are to ensure that MongoEngine has sane defaults going forward and that it performs the best it can out of the box. Where possible there have been FutureWarnings to help get you ready for the change, but that hasn't been possible for the whole of the release. .. warning:: Breaking changes - test upgrading on a test system before putting live. There maybe multiple manual steps in migrating and these are best honed on a staging / test system. Python and PyMongo ================== MongoEngine requires python 2.6 (or above) and pymongo 2.5 (or above) Data Model ========== Inheritance ----------- The inheritance model has changed, we no longer need to store an array of :attr:`types` with the model we can just use the classname in :attr:`_cls`. This means that you will have to update your indexes for each of your inherited classes like so: :: # 1. Declaration of the class class Animal(Document): name = StringField() meta = { 'allow_inheritance': True, 'indexes': ['name'] } # 2. Remove _types collection = Animal._get_collection() collection.update({}, {"$unset": {"_types": 1}}, multi=True) # 3. Confirm extra data is removed count = collection.find({'_types': {"$exists": True}}).count() assert count == 0 # 4. Remove indexes info = collection.index_information() indexes_to_drop = [key for key, value in info.iteritems() if '_types' in dict(value['key'])] for index in indexes_to_drop: collection.drop_index(index) # 5. Recreate indexes Animal.ensure_indexes() Document Definition ------------------- The default for inheritance has changed - it is now off by default and :attr:`_cls` will not be stored automatically with the class. So if you extend your :class:`~mongoengine.Document` or :class:`~mongoengine.EmbeddedDocuments` you will need to declare :attr:`allow_inheritance` in the meta data like so: :: class Animal(Document): name = StringField() meta = {'allow_inheritance': True} Previously, if you had data in the database that wasn't defined in the Document definition, it would set it as an attribute on the document. This is no longer the case and the data is set only in the ``document._data`` dictionary: :: >>> from mongoengine import * >>> class Animal(Document): ... name = StringField() ... >>> cat = Animal(name="kit", size="small") # 0.7 >>> cat.size u'small' # 0.8 >>> cat.size Traceback (most recent call last): File "", line 1, in AttributeError: 'Animal' object has no attribute 'size' The Document class has introduced a reserved function `clean()`, which will be called before saving the document. If your document class happens to have a method with the same name, please try to rename it. def clean(self): pass ReferenceField -------------- ReferenceFields now store ObjectIds by default - this is more efficient than DBRefs as we already know what Document types they reference:: # Old code class Animal(Document): name = ReferenceField('self') # New code to keep dbrefs class Animal(Document): name = ReferenceField('self', dbref=True) To migrate all the references you need to touch each object and mark it as dirty eg:: # Doc definition class Person(Document): name = StringField() parent = ReferenceField('self') friends = ListField(ReferenceField('self')) # Mark all ReferenceFields as dirty and save for p in Person.objects: p._mark_as_changed('parent') p._mark_as_changed('friends') p.save() `An example test migration for ReferenceFields is available on github `_. .. Note:: Internally mongoengine handles ReferenceFields the same, so they are converted to DBRef on loading and ObjectIds or DBRefs depending on settings on storage. UUIDField --------- UUIDFields now default to storing binary values:: # Old code class Animal(Document): uuid = UUIDField() # New code class Animal(Document): uuid = UUIDField(binary=False) To migrate all the uuids you need to touch each object and mark it as dirty eg:: # Doc definition class Animal(Document): uuid = UUIDField() # Mark all UUIDFields as dirty and save for a in Animal.objects: a._mark_as_changed('uuid') a.save() `An example test migration for UUIDFields is available on github `_. DecimalField ------------ DecimalFields now store floats - previously it was storing strings and that made it impossible to do comparisons when querying correctly.:: # Old code class Person(Document): balance = DecimalField() # New code class Person(Document): balance = DecimalField(force_string=True) To migrate all the DecimalFields you need to touch each object and mark it as dirty eg:: # Doc definition class Person(Document): balance = DecimalField() # Mark all DecimalField's as dirty and save for p in Person.objects: p._mark_as_changed('balance') p.save() .. note:: DecimalFields have also been improved with the addition of precision and rounding. See :class:`~mongoengine.fields.DecimalField` for more information. `An example test migration for DecimalFields is available on github `_. Cascading Saves --------------- To improve performance document saves will no longer automatically cascade. Any changes to a Document's references will either have to be saved manually or you will have to explicitly tell it to cascade on save:: # At the class level: class Person(Document): meta = {'cascade': True} # Or on save: my_document.save(cascade=True) Storage ------- Document and Embedded Documents are now serialized based on declared field order. Previously, the data was passed to mongodb as a dictionary and which meant that order wasn't guaranteed - so things like ``$addToSet`` operations on :class:`~mongoengine.EmbeddedDocument` could potentially fail in unexpected ways. If this impacts you, you may want to rewrite the objects using the ``doc.mark_as_dirty('field')`` pattern described above. If you are using a compound primary key then you will need to ensure the order is fixed and match your EmbeddedDocument to that order. Querysets ========= Attack of the clones -------------------- Querysets now return clones and should no longer be considered editable in place. This brings us in line with how Django's querysets work and removes a long running gotcha. If you edit your querysets inplace you will have to update your code like so: :: # Old code: mammals = Animal.objects(type="mammal") mammals.filter(order="Carnivora") # Returns a cloned queryset that isn't assigned to anything - so this will break in 0.8 [m for m in mammals] # This will return all mammals in 0.8 as the 2nd filter returned a new queryset # Update example a) assign queryset after a change: mammals = Animal.objects(type="mammal") carnivores = mammals.filter(order="Carnivora") # Reassign the new queryset so filter can be applied [m for m in carnivores] # This will return all carnivores # Update example b) chain the queryset: mammals = Animal.objects(type="mammal").filter(order="Carnivora") # The final queryset is assgined to mammals [m for m in mammals] # This will return all carnivores Len iterates the queryset ------------------------- If you ever did `len(queryset)` it previously did a `count()` under the covers, this caused some unusual issues. As `len(queryset)` is most often used by `list(queryset)` we now cache the queryset results and use that for the length. This isn't as performant as a `count()` and if you aren't iterating the queryset you should upgrade to use count:: # Old code len(Animal.objects(type="mammal")) # New code Animal.objects(type="mammal").count() .only() now inline with .exclude() ---------------------------------- The behaviour of `.only()` was highly ambiguous, now it works in mirror fashion to `.exclude()`. Chaining `.only()` calls will increase the fields required:: # Old code Animal.objects().only(['type', 'name']).only('name', 'order') # Would have returned just `name` # New code Animal.objects().only('name') # Note: Animal.objects().only(['name']).only('order') # Now returns `name` *and* `order` Client ====== PyMongo 2.4 came with a new connection client; MongoClient_ and started the depreciation of the old :class:`~pymongo.connection.Connection`. MongoEngine now uses the latest `MongoClient` for connections. By default operations were `safe` but if you turned them off or used the connection directly this will impact your queries. Querysets --------- Safe ^^^^ `safe` has been depreciated in the new MongoClient connection. Please use `write_concern` instead. As `safe` always defaulted as `True` normally no code change is required. To disable confirmation of the write just pass `{"w": 0}` eg: :: # Old Animal(name="Dinasour").save(safe=False) # new code: Animal(name="Dinasour").save(write_concern={"w": 0}) Write Concern ^^^^^^^^^^^^^ `write_options` has been replaced with `write_concern` to bring it inline with pymongo. To upgrade simply rename any instances where you used the `write_option` keyword to `write_concern` like so:: # Old code: Animal(name="Dinasour").save(write_options={"w": 2}) # new code: Animal(name="Dinasour").save(write_concern={"w": 2}) Indexes ======= Index methods are no longer tied to querysets but rather to the document class. Although `QuerySet._ensure_indexes` and `QuerySet.ensure_index` still exist. They should be replaced with :func:`~mongoengine.Document.ensure_indexes` / :func:`~mongoengine.Document.ensure_index`. SequenceFields ============== :class:`~mongoengine.fields.SequenceField` now inherits from `BaseField` to allow flexible storage of the calculated value. As such MIN and MAX settings are no longer handled. .. _MongoClient: http://blog.mongodb.org/post/36666163412/introducing-mongoclient 0.6 to 0.7 ********** Cascade saves ============= Saves will raise a `FutureWarning` if they cascade and cascade hasn't been set to True. This is because in 0.8 it will default to False. If you require cascading saves then either set it in the `meta` or pass via `save` eg :: # At the class level: class Person(Document): meta = {'cascade': True} # Or in code: my_document.save(cascade=True) .. note:: Remember: cascading saves **do not** cascade through lists. ReferenceFields =============== ReferenceFields now can store references as ObjectId strings instead of DBRefs. This will become the default in 0.8 and if `dbref` is not set a `FutureWarning` will be raised. To explicitly continue to use DBRefs change the `dbref` flag to True :: class Person(Document): groups = ListField(ReferenceField(Group, dbref=True)) To migrate to using strings instead of DBRefs you will have to manually migrate :: # Step 1 - Migrate the model definition class Group(Document): author = ReferenceField(User, dbref=False) members = ListField(ReferenceField(User, dbref=False)) # Step 2 - Migrate the data for g in Group.objects(): g.author = g.author g.members = g.members g.save() item_frequencies ================ In the 0.6 series we added support for null / zero / false values in item_frequencies. A side effect was to return keys in the value they are stored in rather than as string representations. Your code may need to be updated to handle native types rather than strings keys for the results of item frequency queries. BinaryFields ============ Binary fields have been updated so that they are native binary types. If you previously were doing `str` comparisons with binary field values you will have to update and wrap the value in a `str`. 0.5 to 0.6 ********** Embedded Documents - if you had a `pk` field you will have to rename it from `_id` to `pk` as pk is no longer a property of Embedded Documents. Reverse Delete Rules in Embedded Documents, MapFields and DictFields now throw an InvalidDocument error as they aren't currently supported. Document._get_subclasses - Is no longer used and the class method has been removed. Document.objects.with_id - now raises an InvalidQueryError if used with a filter. FutureWarning - A future warning has been added to all inherited classes that don't define :attr:`allow_inheritance` in their meta. You may need to update pyMongo to 2.0 for use with Sharding. 0.4 to 0.5 ********** There have been the following backwards incompatibilities from 0.4 to 0.5. The main areas of changed are: choices in fields, map_reduce and collection names. Choice options: =============== Are now expected to be an iterable of tuples, with the first element in each tuple being the actual value to be stored. The second element is the human-readable name for the option. PyMongo / MongoDB ================= map reduce now requires pymongo 1.11+- The pymongo `merge_output` and `reduce_output` parameters, have been depreciated. More methods now use map_reduce as db.eval is not supported for sharding as such the following have been changed: * :meth:`~mongoengine.queryset.QuerySet.sum` * :meth:`~mongoengine.queryset.QuerySet.average` * :meth:`~mongoengine.queryset.QuerySet.item_frequencies` Default collection naming ========================= Previously it was just lowercase, it's now much more pythonic and readable as it's lowercase and underscores, previously :: class MyAceDocument(Document): pass MyAceDocument._meta['collection'] == myacedocument In 0.5 this will change to :: class MyAceDocument(Document): pass MyAceDocument._get_collection_name() == my_ace_document To upgrade use a Mixin class to set meta like so :: class BaseMixin(object): meta = { 'collection': lambda c: c.__name__.lower() } class MyAceDocument(Document, BaseMixin): pass MyAceDocument._get_collection_name() == "myacedocument" Alternatively, you can rename your collections eg :: from mongoengine.connection import _get_db from mongoengine.base import _document_registry def rename_collections(): db = _get_db() failure = False collection_names = [d._get_collection_name() for d in _document_registry.values()] for new_style_name in collection_names: if not new_style_name: # embedded documents don't have collections continue old_style_name = new_style_name.replace('_', '') if old_style_name == new_style_name: continue # Nothing to do existing = db.collection_names() if old_style_name in existing: if new_style_name in existing: failure = True print "FAILED to rename: %s to %s (already exists)" % ( old_style_name, new_style_name) else: db[old_style_name].rename(new_style_name) print "Renamed: %s to %s" % (old_style_name, new_style_name) if failure: print "Upgrading collection names failed" else: print "Upgraded collection names" mongodb 1.8 > 2.0 + =================== It's been reported that indexes may need to be recreated to the newer version of indexes. To do this drop indexes and call ``ensure_indexes`` on each model. mongoengine-0.10.6/docs/conf.py0000664000175000017500000001501512651363712015746 0ustar travistravis# -*- coding: utf-8 -*- # # MongoEngine documentation build configuration file, created by # sphinx-quickstart on Sun Nov 22 18:14:13 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'MongoEngine' copyright = u'2009, MongoEngine Authors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # import mongoengine # The short X.Y version. version = mongoengine.get_version() # The full version, including alpha/beta/rc tags. release = mongoengine.get_version() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = "favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = True # Custom sidebar templates, maps document names to template names. html_sidebars = { 'index': ['globaltoc.html', 'searchbox.html'], '**': ['localtoc.html', 'relations.html', 'searchbox.html'] } # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'MongoEnginedoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). latex_paper_size = 'a4' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'MongoEngine.tex', 'MongoEngine Documentation', 'Ross Lawley', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True autoclass_content = 'both' html_theme_options = dict( canonical_url='http://docs.mongoengine.org/en/latest/' ) mongoengine-0.10.6/docs/django.rst0000664000175000017500000000116412651363712016443 0ustar travistravis============== Django Support ============== .. note:: Django support has been split from the main MongoEngine repository. The *legacy* Django extension may be found bundled with the 0.9 release of MongoEngine. Help Wanted! ------------ The MongoEngine team is looking for help contributing and maintaining a new Django extension for MongoEngine! If you have Django experience and would like to help contribute to the project, please get in touch on the `mailing list `_ or by simply contributing on `GitHub `_. mongoengine-0.10.6/docs/code/0000775000175000017500000000000012651364023015353 5ustar travistravismongoengine-0.10.6/docs/code/tumblelog.py0000664000175000017500000000323512651363712017726 0ustar travistravisfrom mongoengine import * connect('tumblelog') class Comment(EmbeddedDocument): content = StringField() name = StringField(max_length=120) class User(Document): email = StringField(required=True) first_name = StringField(max_length=50) last_name = StringField(max_length=50) class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) tags = ListField(StringField(max_length=30)) comments = ListField(EmbeddedDocumentField(Comment)) # bugfix meta = {'allow_inheritance': True} class TextPost(Post): content = StringField() class ImagePost(Post): image_path = StringField() class LinkPost(Post): link_url = StringField() Post.drop_collection() john = User(email='jdoe@example.com', first_name='John', last_name='Doe') john.save() post1 = TextPost(title='Fun with MongoEngine', author=john) post1.content = 'Took a look at MongoEngine today, looks pretty cool.' post1.tags = ['mongodb', 'mongoengine'] post1.save() post2 = LinkPost(title='MongoEngine Documentation', author=john) post2.link_url = 'http://tractiondigital.com/labs/mongoengine/docs' post2.tags = ['mongoengine'] post2.save() print 'ALL POSTS' print for post in Post.objects: print post.title #print '=' * post.title.count() print "=" * 20 if isinstance(post, TextPost): print post.content if isinstance(post, LinkPost): print 'Link:', post.link_url print print print 'POSTS TAGGED \'MONGODB\'' print for post in Post.objects(tags='mongodb'): print post.title print num_posts = Post.objects(tags='mongodb').count() print 'Found %d posts with tag "mongodb"' % num_posts mongoengine-0.10.6/AUTHORS0000664000175000017500000001516212651363712014572 0ustar travistravisThe PRIMARY AUTHORS are (and/or have been): Ross Lawley Harry Marr Matt Dennewitz Deepak Thukral Florian Schlachter Steve Challis Wilson Júnior Dan Crosta https://github.com/dcrosta Laine Herron https://github.com/LaineHerron CONTRIBUTORS Derived from the git logs, inevitably incomplete but all of whom and others have submitted patches, reported bugs and generally helped make MongoEngine that much better: * blackbrrr * Florian Schlachter * Vincent Driessen * Steve Challis * flosch * Deepak Thukral * Colin Howe * Wilson Júnior (https://github.com/wpjunior) * Alistair Roche * Dan Crosta * Viktor Kerkez * Stephan Jaekel * Rached Ben Mustapha * Greg Turner * Daniel Hasselrot * Mircea Pasoi * Matt Chisholm * James Punteney * TimothéePeignier * Stuart Rackham * Serge Matveenko * Matt Dennewitz * Don Spaulding * Ales Zoulek * sshwsfc * sib * Samuel Clay * Nick Vlku * martin * Flavio Amieiro * ÐнхбаÑÑ€ Лхагвадорж * Zak Johnson * Victor Farazdagi * vandersonmota * Theo Julienne * sp * Slavi Pantaleev * Richard Henry * Nicolas Perriault * Nick Vlku Jr * Michael Henson * Leo Honkanen * kuno * Josh Ourisman * Jaime * Igor Ivanov * Gregg Lind * Gareth Lloyd * Albert Choi * John Arnfield * grubberr * Paul Aliagas * Paul Cunnane * Julien Rebetez * Marc Tamlyn * Karim Allah * Adam Parrish * jpfarias * jonrscott * Alice Zoë Bevan-McGregor (https://github.com/amcgregor/) * Stephen Young * tkloc * aid * yamaneko1212 * dave mankoff * Alexander G. Morano * jwilder * Joe Shaw * Adam Flynn * Ankhbayar * Jan Schrewe * David Koblas * Crittercism * Alvin Liang * andrewmlevy * Chris Faulkner * Ashwin Purohit * Shalabh Aggarwal * Chris Williams * Robert Kajic * Jacob Peddicord * Nils Hasenbanck * mostlystatic * Greg Banks * swashbuckler * Adam Reeve * Anthony Nemitz * deignacio * Shaun Duncan * Meir Kriheli * Andrey Fedoseev * aparajita * Tristan Escalada * Alexander Koshelev * Jaime Irurzun * Alexandre González * Thomas Steinacher * Tommi Komulainen * Peter Landry * biszkoptwielki * Anton Kolechkin * Sergey Nikitin * psychogenic * Stefan Wójcik (https://github.com/wojcikstefan) * dimonb * Garry Polley * James Slagle * Adrian Scott * Peter Teichman * Jakub Kot * Jorge Bastida * Aleksandr Sorokoumov * Yohan Graterol * bool-dev * Russ Weeks * Paul Swartz * Sundar Raman * Benoit Louy * Loic Raucy (https://github.com/lraucy) * hellysmile * Jaepil Jeong * Daniil Sharou * Pete Campton * Martyn Smith * Marcelo Anton * Aleksey Porfirov (https://github.com/lexqt) * Nicolas Trippar * Manuel Hermann * Gustavo Gawryszewski * Max Countryman * caitifbrito * lcya86 刘春洋 * Martin Alderete (https://github.com/malderete) * Nick Joyce * Jared Forsyth * Kenneth Falck * Lukasz Balcerzak * Nicolas Cortot * Alex (https://github.com/kelsta) * Jin Zhang * Daniel Axtens * Leo-Naeka * Ryan Witt (https://github.com/ryanwitt) * Jiequan (https://github.com/Jiequan) * hensom (https://github.com/hensom) * zhy0216 (https://github.com/zhy0216) * istinspring (https://github.com/istinspring) * Massimo Santini (https://github.com/mapio) * Nigel McNie (https://github.com/nigelmcnie) * ygbourhis (https://github.com/ygbourhis) * Bob Dickinson (https://github.com/BobDickinson) * Michael Bartnett (https://github.com/michaelbartnett) * Alon Horev (https://github.com/alonho) * Kelvin Hammond (https://github.com/kelvinhammond) * Jatin Chopra (https://github.com/jatin) * Paul Uithol (https://github.com/PaulUithol) * Thom Knowles (https://github.com/fleat) * Paul (https://github.com/squamous) * Olivier Cortès (https://github.com/Karmak23) * crazyzubr (https://github.com/crazyzubr) * FrankSomething (https://github.com/FrankSomething) * Alexandr Morozov (https://github.com/LK4D4) * mishudark (https://github.com/mishudark) * Joe Friedl (https://github.com/grampajoe) * Daniel Ward (https://github.com/danielward) * Aniket Deshpande (https://github.com/anicake) * rfkrocktk (https://github.com/rfkrocktk) * Gustavo Andrés Angulo (https://github.com/woakas) * Dmytro Popovych (https://github.com/drudim) * Tom (https://github.com/tomprimozic) * j0hnsmith (https://github.com/j0hnsmith) * Damien Churchill (https://github.com/damoxc) * Jonathan Simon Prates (https://github.com/jonathansp) * Thiago Papageorgiou (https://github.com/tmpapageorgiou) * Omer Katz (https://github.com/thedrow) * Falcon Dai (https://github.com/falcondai) * Polyrabbit (https://github.com/polyrabbit) * Sagiv Malihi (https://github.com/sagivmalihi) * Dmitry Konishchev (https://github.com/KonishchevDmitry) * Martyn Smith (https://github.com/martynsmith) * Andrei Zbikowski (https://github.com/b1naryth1ef) * Ronald van Rij (https://github.com/ronaldvanrij) * François Schmidts (https://github.com/jaesivsm) * Eric Plumb (https://github.com/professorplumb) * Damien Churchill (https://github.com/damoxc) * Aleksandr Sorokoumov (https://github.com/Gerrrr) * Clay McClure (https://github.com/claymation) * Bruno Rocha (https://github.com/rochacbruno) * Norberto Leite (https://github.com/nleite) * Bob Cribbs (https://github.com/bocribbz) * Jay Shirley (https://github.com/jshirley) * David Bordeynik (https://github.com/DavidBord) * Axel Haustant (https://github.com/noirbizarre) * David Czarnecki (https://github.com/czarneckid) * Vyacheslav Murashkin (https://github.com/a4tunado) * André Ericson https://github.com/aericson) * Mikhail Moshnogorsky (https://github.com/mikhailmoshnogorsky) * Diego Berrocal (https://github.com/cestdiego) * Matthew Ellison (https://github.com/seglberg) * Jimmy Shen (https://github.com/jimmyshen) * J. Fernando Sánchez (https://github.com/balkian) * Michael Chase (https://github.com/rxsegrxup) * Eremeev Danil (https://github.com/elephanter) * Catstyle Lee (https://github.com/Catstyle) * Kiryl Yermakou (https://github.com/rma4ok) * Matthieu Rigal (https://github.com/MRigal) * Charanpal Dhanjal (https://github.com/charanpald) * Emmanuel Leblond (https://github.com/touilleMan) * Breeze.Kay (https://github.com/9nix00) * Vicki Donchenko (https://github.com/kivistein) * Emile Caron (https://github.com/emilecaron) * Amit Lichtenberg (https://github.com/amitlicht) * Lars Butler (https://github.com/larsbutler) * George Macon (https://github.com/gmacon) * Ashley Whetter (https://github.com/AWhetter) * Paul-Armand Verhaegen (https://github.com/paularmand) * Steven Rossiter (https://github.com/BeardedSteve) * Luo Peng (https://github.com/RussellLuo) mongoengine-0.10.6/mongoengine.egg-info/0000775000175000017500000000000012651364023017510 5ustar travistravismongoengine-0.10.6/mongoengine.egg-info/requires.txt0000664000175000017500000000001712651364023022106 0ustar travistravispymongo>=2.7.1 mongoengine-0.10.6/mongoengine.egg-info/top_level.txt0000664000175000017500000000001412651364023022235 0ustar travistravismongoengine mongoengine-0.10.6/mongoengine.egg-info/PKG-INFO0000664000175000017500000001531012651364023020605 0ustar travistravisMetadata-Version: 1.0 Name: mongoengine Version: 0.10.6 Summary: MongoEngine is a Python Object-Document Mapper for working with MongoDB. Home-page: http://mongoengine.org/ Author: Ross Lawley Author-email: ross.lawley@{nospam}gmail.com License: MIT Download-URL: https://github.com/MongoEngine/mongoengine/tarball/master Description: =========== MongoEngine =========== :Info: MongoEngine is an ORM-like layer on top of PyMongo. :Repository: https://github.com/MongoEngine/mongoengine :Author: Harry Marr (http://github.com/hmarr) :Maintainer: Ross Lawley (http://github.com/rozza) .. image:: https://secure.travis-ci.org/MongoEngine/mongoengine.png?branch=master :target: http://travis-ci.org/MongoEngine/mongoengine .. image:: https://coveralls.io/repos/MongoEngine/mongoengine/badge.png?branch=master :target: https://coveralls.io/r/MongoEngine/mongoengine?branch=master .. image:: https://landscape.io/github/MongoEngine/mongoengine/master/landscape.png :target: https://landscape.io/github/MongoEngine/mongoengine/master :alt: Code Health About ===== MongoEngine is a Python Object-Document Mapper for working with MongoDB. Documentation available at http://mongoengine-odm.rtfd.org - there is currently a `tutorial `_, a `user guide `_ and an `API reference `_. Installation ============ We recommend the use of `virtualenv `_ and of `pip `_. You can then use ``pip install -U mongoengine``. You may also have `setuptools `_ and thus you can use ``easy_install -U mongoengine``. Otherwise, you can download the source from `GitHub `_ and run ``python setup.py install``. Dependencies ============ - pymongo>=2.7.1 - sphinx (optional - for documentation generation) Optional Dependencies --------------------- - **Image Fields**: Pillow>=2.0.0 - dateutil>=2.1.0 .. note MongoEngine always runs it's test suite against the latest patch version of each dependecy. e.g.: PyMongo 3.0.1 Examples ======== Some simple examples of what MongoEngine code looks like: .. code :: python class BlogPost(Document): title = StringField(required=True, max_length=200) posted = DateTimeField(default=datetime.datetime.now) tags = ListField(StringField(max_length=50)) class TextPost(BlogPost): content = StringField(required=True) class LinkPost(BlogPost): url = StringField(required=True) # Create a text-based post >>> post1 = TextPost(title='Using MongoEngine', content='See the tutorial') >>> post1.tags = ['mongodb', 'mongoengine'] >>> post1.save() # Create a link-based post >>> post2 = LinkPost(title='MongoEngine Docs', url='hmarr.com/mongoengine') >>> post2.tags = ['mongoengine', 'documentation'] >>> post2.save() # Iterate over all posts using the BlogPost superclass >>> for post in BlogPost.objects: ... print '===', post.title, '===' ... if isinstance(post, TextPost): ... print post.content ... elif isinstance(post, LinkPost): ... print 'Link:', post.url ... print ... >>> len(BlogPost.objects) 2 >>> len(TextPost.objects) 1 >>> len(LinkPost.objects) 1 # Find tagged posts >>> len(BlogPost.objects(tags='mongoengine')) 2 >>> len(BlogPost.objects(tags='mongodb')) 1 Tests ===== To run the test suite, ensure you are running a local instance of MongoDB on the standard port, and run: ``python setup.py nosetests``. To run the test suite on every supported Python version and every supported PyMongo version, you can use ``tox``. tox and each supported Python version should be installed in your environment: .. code-block:: shell # Install tox $ pip install tox # Run the test suites $ tox If you wish to run one single or selected tests, use the nosetest convention. It will find the folder, eventually the file, go to the TestClass specified after the colon and eventually right to the single test. Also use the -s argument if you want to print out whatever or access pdb while testing. .. code-block:: shell $ python setup.py nosetests --tests tests/fields/fields.py:FieldTest.test_cls_field -s Community ========= - `MongoEngine Users mailing list `_ - `MongoEngine Developers mailing list `_ - `#mongoengine IRC channel `_ Contributing ============ We welcome contributions! see the `Contribution guidelines `_ Platform: any Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Database Classifier: Topic :: Software Development :: Libraries :: Python Modules mongoengine-0.10.6/mongoengine.egg-info/dependency_links.txt0000664000175000017500000000000112651364023023556 0ustar travistravis mongoengine-0.10.6/mongoengine.egg-info/SOURCES.txt0000664000175000017500000000417312651364023021401 0ustar travistravisAUTHORS LICENSE MANIFEST.in README.rst setup.cfg setup.py docs/Makefile docs/apireference.rst docs/changelog.rst docs/conf.py docs/django.rst docs/index.rst docs/tutorial.rst docs/upgrade.rst docs/_themes/sphinx_rtd_theme/__init__.py docs/_themes/sphinx_rtd_theme/breadcrumbs.html docs/_themes/sphinx_rtd_theme/footer.html docs/_themes/sphinx_rtd_theme/layout.html docs/_themes/sphinx_rtd_theme/layout_old.html docs/_themes/sphinx_rtd_theme/search.html docs/_themes/sphinx_rtd_theme/searchbox.html docs/_themes/sphinx_rtd_theme/theme.conf docs/_themes/sphinx_rtd_theme/versions.html docs/_themes/sphinx_rtd_theme/static/favicon.ico docs/_themes/sphinx_rtd_theme/static/css/badge_only.css docs/_themes/sphinx_rtd_theme/static/css/theme.css docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.eot docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.svg docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.ttf docs/_themes/sphinx_rtd_theme/static/font/fontawesome_webfont.woff docs/_themes/sphinx_rtd_theme/static/js/theme.js docs/code/tumblelog.py docs/guide/connecting.rst docs/guide/defining-documents.rst docs/guide/document-instances.rst docs/guide/gridfs.rst docs/guide/index.rst docs/guide/installing.rst docs/guide/mongomock.rst docs/guide/querying.rst docs/guide/signals.rst docs/guide/text-indexes.rst mongoengine/__init__.py mongoengine/common.py mongoengine/connection.py mongoengine/context_managers.py mongoengine/dereference.py mongoengine/document.py mongoengine/errors.py mongoengine/fields.py mongoengine/python_support.py mongoengine/signals.py mongoengine.egg-info/PKG-INFO mongoengine.egg-info/SOURCES.txt mongoengine.egg-info/dependency_links.txt mongoengine.egg-info/requires.txt mongoengine.egg-info/top_level.txt mongoengine/base/__init__.py mongoengine/base/common.py mongoengine/base/datastructures.py mongoengine/base/document.py mongoengine/base/fields.py mongoengine/base/metaclasses.py mongoengine/queryset/__init__.py mongoengine/queryset/base.py mongoengine/queryset/field_list.py mongoengine/queryset/manager.py mongoengine/queryset/queryset.py mongoengine/queryset/transform.py mongoengine/queryset/visitor.pymongoengine-0.10.6/LICENSE0000664000175000017500000000203712651363712014524 0ustar travistravisCopyright (c) 2009 See AUTHORS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mongoengine-0.10.6/mongoengine/0000775000175000017500000000000012651364023016016 5ustar travistravismongoengine-0.10.6/mongoengine/fields.py0000664000175000017500000020741412651363712017652 0ustar travistravisimport datetime import decimal import itertools import re import time import urllib2 import uuid import warnings from operator import itemgetter try: import dateutil except ImportError: dateutil = None else: import dateutil.parser import pymongo import gridfs from bson import Binary, DBRef, SON, ObjectId from mongoengine.errors import ValidationError from mongoengine.python_support import (PY3, bin_type, txt_type, str_types, StringIO) from base import (BaseField, ComplexBaseField, ObjectIdField, GeoJsonBaseField, get_document, BaseDocument) from queryset import DO_NOTHING, QuerySet from document import Document, EmbeddedDocument from connection import get_db, DEFAULT_CONNECTION_NAME try: from PIL import Image, ImageOps except ImportError: Image = None ImageOps = None __all__ = [ 'StringField', 'URLField', 'EmailField', 'IntField', 'LongField', 'FloatField', 'DecimalField', 'BooleanField', 'DateTimeField', 'ComplexDateTimeField', 'EmbeddedDocumentField', 'ObjectIdField', 'GenericEmbeddedDocumentField', 'DynamicField', 'ListField', 'SortedListField', 'EmbeddedDocumentListField', 'DictField', 'MapField', 'ReferenceField', 'CachedReferenceField', 'GenericReferenceField', 'BinaryField', 'GridFSError', 'GridFSProxy', 'FileField', 'ImageGridFsProxy', 'ImproperlyConfigured', 'ImageField', 'GeoPointField', 'PointField', 'LineStringField', 'PolygonField', 'SequenceField', 'UUIDField', 'MultiPointField', 'MultiLineStringField', 'MultiPolygonField', 'GeoJsonBaseField'] RECURSIVE_REFERENCE_CONSTANT = 'self' class StringField(BaseField): """A unicode string field. """ def __init__(self, regex=None, max_length=None, min_length=None, **kwargs): self.regex = re.compile(regex) if regex else None self.max_length = max_length self.min_length = min_length super(StringField, self).__init__(**kwargs) def to_python(self, value): if isinstance(value, unicode): return value try: value = value.decode('utf-8') except: pass return value def validate(self, value): if not isinstance(value, basestring): self.error('StringField only accepts string values') if self.max_length is not None and len(value) > self.max_length: self.error('String value is too long') if self.min_length is not None and len(value) < self.min_length: self.error('String value is too short') if self.regex is not None and self.regex.match(value) is None: self.error('String value did not match validation regex') def lookup_member(self, member_name): return None def prepare_query_value(self, op, value): if not isinstance(op, basestring): return value if op.lstrip('i') in ('startswith', 'endswith', 'contains', 'exact'): flags = 0 if op.startswith('i'): flags = re.IGNORECASE op = op.lstrip('i') regex = r'%s' if op == 'startswith': regex = r'^%s' elif op == 'endswith': regex = r'%s$' elif op == 'exact': regex = r'^%s$' # escape unsafe characters which could lead to a re.error value = re.escape(value) value = re.compile(regex % value, flags) return super(StringField, self).prepare_query_value(op, value) class URLField(StringField): """A field that validates input as an URL. .. versionadded:: 0.3 """ _URL_REGEX = re.compile( r'^(?:[a-z0-9\.\-]*)://' # scheme is validated separately r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}(? self.max_value: self.error('Integer value is too large') def prepare_query_value(self, op, value): if value is None: return value return super(IntField, self).prepare_query_value(op, int(value)) class LongField(BaseField): """An 64-bit integer field. """ def __init__(self, min_value=None, max_value=None, **kwargs): self.min_value, self.max_value = min_value, max_value super(LongField, self).__init__(**kwargs) def to_python(self, value): try: value = long(value) except ValueError: pass return value def validate(self, value): try: value = long(value) except: self.error('%s could not be converted to long' % value) if self.min_value is not None and value < self.min_value: self.error('Long value is too small') if self.max_value is not None and value > self.max_value: self.error('Long value is too large') def prepare_query_value(self, op, value): if value is None: return value return super(LongField, self).prepare_query_value(op, long(value)) class FloatField(BaseField): """An floating point number field. """ def __init__(self, min_value=None, max_value=None, **kwargs): self.min_value, self.max_value = min_value, max_value super(FloatField, self).__init__(**kwargs) def to_python(self, value): try: value = float(value) except ValueError: pass return value def validate(self, value): if isinstance(value, int): value = float(value) if not isinstance(value, float): self.error('FloatField only accepts float values') if self.min_value is not None and value < self.min_value: self.error('Float value is too small') if self.max_value is not None and value > self.max_value: self.error('Float value is too large') def prepare_query_value(self, op, value): if value is None: return value return super(FloatField, self).prepare_query_value(op, float(value)) class DecimalField(BaseField): """A fixed-point decimal number field. .. versionchanged:: 0.8 .. versionadded:: 0.3 """ def __init__(self, min_value=None, max_value=None, force_string=False, precision=2, rounding=decimal.ROUND_HALF_UP, **kwargs): """ :param min_value: Validation rule for the minimum acceptable value. :param max_value: Validation rule for the maximum acceptable value. :param force_string: Store as a string. :param precision: Number of decimal places to store. :param rounding: The rounding rule from the python decimal library: - decimal.ROUND_CEILING (towards Infinity) - decimal.ROUND_DOWN (towards zero) - decimal.ROUND_FLOOR (towards -Infinity) - decimal.ROUND_HALF_DOWN (to nearest with ties going towards zero) - decimal.ROUND_HALF_EVEN (to nearest with ties going to nearest even integer) - decimal.ROUND_HALF_UP (to nearest with ties going away from zero) - decimal.ROUND_UP (away from zero) - decimal.ROUND_05UP (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero) Defaults to: ``decimal.ROUND_HALF_UP`` """ self.min_value = min_value self.max_value = max_value self.force_string = force_string self.precision = precision self.rounding = rounding super(DecimalField, self).__init__(**kwargs) def to_python(self, value): if value is None: return value # Convert to string for python 2.6 before casting to Decimal try: value = decimal.Decimal("%s" % value) except decimal.InvalidOperation: return value return value.quantize(decimal.Decimal(".%s" % ("0" * self.precision)), rounding=self.rounding) def to_mongo(self, value, use_db_field=True): if value is None: return value if self.force_string: return unicode(value) return float(self.to_python(value)) def validate(self, value): if not isinstance(value, decimal.Decimal): if not isinstance(value, basestring): value = unicode(value) try: value = decimal.Decimal(value) except Exception, exc: self.error('Could not convert value to decimal: %s' % exc) if self.min_value is not None and value < self.min_value: self.error('Decimal value is too small') if self.max_value is not None and value > self.max_value: self.error('Decimal value is too large') def prepare_query_value(self, op, value): return super(DecimalField, self).prepare_query_value(op, self.to_mongo(value)) class BooleanField(BaseField): """A boolean field type. .. versionadded:: 0.1.2 """ def to_python(self, value): try: value = bool(value) except ValueError: pass return value def validate(self, value): if not isinstance(value, bool): self.error('BooleanField only accepts boolean values') class DateTimeField(BaseField): """A datetime field. Uses the python-dateutil library if available alternatively use time.strptime to parse the dates. Note: python-dateutil's parser is fully featured and when installed you can utilise it to convert varying types of date formats into valid python datetime objects. Note: Microseconds are rounded to the nearest millisecond. Pre UTC microsecond support is effectively broken. Use :class:`~mongoengine.fields.ComplexDateTimeField` if you need accurate microsecond support. """ def validate(self, value): new_value = self.to_mongo(value) if not isinstance(new_value, (datetime.datetime, datetime.date)): self.error(u'cannot parse date "%s"' % value) def to_mongo(self, value): if value is None: return value if isinstance(value, datetime.datetime): return value if isinstance(value, datetime.date): return datetime.datetime(value.year, value.month, value.day) if callable(value): return value() if not isinstance(value, basestring): return None # Attempt to parse a datetime: if dateutil: try: return dateutil.parser.parse(value) except (TypeError, ValueError): return None # split usecs, because they are not recognized by strptime. if '.' in value: try: value, usecs = value.split('.') usecs = int(usecs) except ValueError: return None else: usecs = 0 kwargs = {'microsecond': usecs} try: # Seconds are optional, so try converting seconds first. return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6], **kwargs) except ValueError: try: # Try without seconds. return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5], **kwargs) except ValueError: # Try without hour/minutes/seconds. try: return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3], **kwargs) except ValueError: return None def prepare_query_value(self, op, value): return super(DateTimeField, self).prepare_query_value(op, self.to_mongo(value)) class ComplexDateTimeField(StringField): """ ComplexDateTimeField handles microseconds exactly instead of rounding like DateTimeField does. Derives from a StringField so you can do `gte` and `lte` filtering by using lexicographical comparison when filtering / sorting strings. The stored string has the following format: YYYY,MM,DD,HH,MM,SS,NNNNNN Where NNNNNN is the number of microseconds of the represented `datetime`. The `,` as the separator can be easily modified by passing the `separator` keyword when initializing the field. .. versionadded:: 0.5 """ def __init__(self, separator=',', **kwargs): self.names = ['year', 'month', 'day', 'hour', 'minute', 'second', 'microsecond'] self.separator = separator self.format = separator.join(['%Y', '%m', '%d', '%H', '%M', '%S', '%f']) super(ComplexDateTimeField, self).__init__(**kwargs) def _convert_from_datetime(self, val): """ Convert a `datetime` object to a string representation (which will be stored in MongoDB). This is the reverse function of `_convert_from_string`. >>> a = datetime(2011, 6, 8, 20, 26, 24, 92284) >>> ComplexDateTimeField()._convert_from_datetime(a) '2011,06,08,20,26,24,092284' """ return val.strftime(self.format) def _convert_from_string(self, data): """ Convert a string representation to a `datetime` object (the object you will manipulate). This is the reverse function of `_convert_from_datetime`. >>> a = '2011,06,08,20,26,24,092284' >>> ComplexDateTimeField()._convert_from_string(a) datetime.datetime(2011, 6, 8, 20, 26, 24, 92284) """ values = map(int, data.split(self.separator)) return datetime.datetime(*values) def __get__(self, instance, owner): data = super(ComplexDateTimeField, self).__get__(instance, owner) if data is None: return None if self.null else datetime.datetime.now() if isinstance(data, datetime.datetime): return data return self._convert_from_string(data) def __set__(self, instance, value): value = self._convert_from_datetime(value) if value else value return super(ComplexDateTimeField, self).__set__(instance, value) def validate(self, value): value = self.to_python(value) if not isinstance(value, datetime.datetime): self.error('Only datetime objects may used in a ' 'ComplexDateTimeField') def to_python(self, value): original_value = value try: return self._convert_from_string(value) except: return original_value def to_mongo(self, value): value = self.to_python(value) return self._convert_from_datetime(value) def prepare_query_value(self, op, value): return super(ComplexDateTimeField, self).prepare_query_value(op, self._convert_from_datetime(value)) class EmbeddedDocumentField(BaseField): """An embedded document field - with a declared document_type. Only valid values are subclasses of :class:`~mongoengine.EmbeddedDocument`. """ def __init__(self, document_type, **kwargs): if not isinstance(document_type, basestring): if not issubclass(document_type, EmbeddedDocument): self.error('Invalid embedded document class provided to an ' 'EmbeddedDocumentField') self.document_type_obj = document_type super(EmbeddedDocumentField, self).__init__(**kwargs) @property def document_type(self): if isinstance(self.document_type_obj, basestring): if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT: self.document_type_obj = self.owner_document else: self.document_type_obj = get_document(self.document_type_obj) return self.document_type_obj def to_python(self, value): if not isinstance(value, self.document_type): return self.document_type._from_son(value, _auto_dereference=self._auto_dereference) return value def to_mongo(self, value, use_db_field=True, fields=[]): if not isinstance(value, self.document_type): return value return self.document_type.to_mongo(value, use_db_field, fields=fields) def validate(self, value, clean=True): """Make sure that the document instance is an instance of the EmbeddedDocument subclass provided when the document was defined. """ # Using isinstance also works for subclasses of self.document if not isinstance(value, self.document_type): self.error('Invalid embedded document instance provided to an ' 'EmbeddedDocumentField') self.document_type.validate(value, clean) def lookup_member(self, member_name): return self.document_type._fields.get(member_name) def prepare_query_value(self, op, value): if not isinstance(value, self.document_type): value = self.document_type._from_son(value) super(EmbeddedDocumentField, self).prepare_query_value(op, value) return self.to_mongo(value) class GenericEmbeddedDocumentField(BaseField): """A generic embedded document field - allows any :class:`~mongoengine.EmbeddedDocument` to be stored. Only valid values are subclasses of :class:`~mongoengine.EmbeddedDocument`. .. note :: You can use the choices param to limit the acceptable EmbeddedDocument types """ def prepare_query_value(self, op, value): return super(GenericEmbeddedDocumentField, self).prepare_query_value(op, self.to_mongo(value)) def to_python(self, value): if isinstance(value, dict): doc_cls = get_document(value['_cls']) value = doc_cls._from_son(value) return value def validate(self, value, clean=True): if not isinstance(value, EmbeddedDocument): self.error('Invalid embedded document instance provided to an ' 'GenericEmbeddedDocumentField') value.validate(clean=clean) def to_mongo(self, document, use_db_field=True): if document is None: return None data = document.to_mongo(use_db_field) if '_cls' not in data: data['_cls'] = document._class_name return data class DynamicField(BaseField): """A truly dynamic field type capable of handling different and varying types of data. Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data""" def to_mongo(self, value): """Convert a Python type to a MongoDB compatible type. """ if isinstance(value, basestring): return value if hasattr(value, 'to_mongo'): cls = value.__class__ val = value.to_mongo() # If we its a document thats not inherited add _cls if isinstance(value, Document): val = {"_ref": value.to_dbref(), "_cls": cls.__name__} if isinstance(value, EmbeddedDocument): val['_cls'] = cls.__name__ return val if not isinstance(value, (dict, list, tuple)): return value is_list = False if not hasattr(value, 'items'): is_list = True value = dict([(k, v) for k, v in enumerate(value)]) data = {} for k, v in value.iteritems(): data[k] = self.to_mongo(v) value = data if is_list: # Convert back to a list value = [v for k, v in sorted(data.iteritems(), key=itemgetter(0))] return value def to_python(self, value): if isinstance(value, dict) and '_cls' in value: doc_cls = get_document(value['_cls']) if '_ref' in value: value = doc_cls._get_db().dereference(value['_ref']) return doc_cls._from_son(value) return super(DynamicField, self).to_python(value) def lookup_member(self, member_name): return member_name def prepare_query_value(self, op, value): if isinstance(value, basestring): return StringField().prepare_query_value(op, value) return super(DynamicField, self).prepare_query_value(op, self.to_mongo(value)) def validate(self, value, clean=True): if hasattr(value, "validate"): value.validate(clean=clean) class ListField(ComplexBaseField): """A list field that wraps a standard field, allowing multiple instances of the field to be used as a list in the database. If using with ReferenceFields see: :ref:`one-to-many-with-listfields` .. note:: Required means it cannot be empty - as the default for ListFields is [] """ def __init__(self, field=None, **kwargs): self.field = field kwargs.setdefault('default', lambda: []) super(ListField, self).__init__(**kwargs) def validate(self, value): """Make sure that a list of valid fields is being used. """ if (not isinstance(value, (list, tuple, QuerySet)) or isinstance(value, basestring)): self.error('Only lists and tuples may be used in a list field') super(ListField, self).validate(value) def prepare_query_value(self, op, value): if self.field: if op in ('set', 'unset') and ( not isinstance(value, basestring) and not isinstance(value, BaseDocument) and hasattr(value, '__iter__')): return [self.field.prepare_query_value(op, v) for v in value] return self.field.prepare_query_value(op, value) return super(ListField, self).prepare_query_value(op, value) class EmbeddedDocumentListField(ListField): """A :class:`~mongoengine.ListField` designed specially to hold a list of embedded documents to provide additional query helpers. .. note:: The only valid list values are subclasses of :class:`~mongoengine.EmbeddedDocument`. .. versionadded:: 0.9 """ def __init__(self, document_type, **kwargs): """ :param document_type: The type of :class:`~mongoengine.EmbeddedDocument` the list will hold. :param kwargs: Keyword arguments passed directly into the parent :class:`~mongoengine.ListField`. """ super(EmbeddedDocumentListField, self).__init__( field=EmbeddedDocumentField(document_type), **kwargs ) class SortedListField(ListField): """A ListField that sorts the contents of its list before writing to the database in order to ensure that a sorted list is always retrieved. .. warning:: There is a potential race condition when handling lists. If you set / save the whole list then other processes trying to save the whole list as well could overwrite changes. The safest way to append to a list is to perform a push operation. .. versionadded:: 0.4 .. versionchanged:: 0.6 - added reverse keyword """ _ordering = None _order_reverse = False def __init__(self, field, **kwargs): if 'ordering' in kwargs.keys(): self._ordering = kwargs.pop('ordering') if 'reverse' in kwargs.keys(): self._order_reverse = kwargs.pop('reverse') super(SortedListField, self).__init__(field, **kwargs) def to_mongo(self, value): value = super(SortedListField, self).to_mongo(value) if self._ordering is not None: return sorted(value, key=itemgetter(self._ordering), reverse=self._order_reverse) return sorted(value, reverse=self._order_reverse) def key_not_string(d): """ Helper function to recursively determine if any key in a dictionary is not a string. """ for k, v in d.items(): if not isinstance(k, basestring) or (isinstance(v, dict) and key_not_string(v)): return True def key_has_dot_or_dollar(d): """ Helper function to recursively determine if any key in a dictionary contains a dot or a dollar sign. """ for k, v in d.items(): if ('.' in k or '$' in k) or (isinstance(v, dict) and key_has_dot_or_dollar(v)): return True class DictField(ComplexBaseField): """A dictionary field that wraps a standard Python dictionary. This is similar to an embedded document, but the structure is not defined. .. note:: Required means it cannot be empty - as the default for DictFields is {} .. versionadded:: 0.3 .. versionchanged:: 0.5 - Can now handle complex / varying types of data """ def __init__(self, basecls=None, field=None, *args, **kwargs): self.field = field self._auto_dereference = False self.basecls = basecls or BaseField if not issubclass(self.basecls, BaseField): self.error('DictField only accepts dict values') kwargs.setdefault('default', lambda: {}) super(DictField, self).__init__(*args, **kwargs) def validate(self, value): """Make sure that a list of valid fields is being used. """ if not isinstance(value, dict): self.error('Only dictionaries may be used in a DictField') if key_not_string(value): msg = ("Invalid dictionary key - documents must " "have only string keys") self.error(msg) if key_has_dot_or_dollar(value): self.error('Invalid dictionary key name - keys may not contain "."' ' or "$" characters') super(DictField, self).validate(value) def lookup_member(self, member_name): return DictField(basecls=self.basecls, db_field=member_name) def prepare_query_value(self, op, value): match_operators = ['contains', 'icontains', 'startswith', 'istartswith', 'endswith', 'iendswith', 'exact', 'iexact'] if op in match_operators and isinstance(value, basestring): return StringField().prepare_query_value(op, value) if hasattr(self.field, 'field'): if op in ('set', 'unset') and isinstance(value, dict): return dict( (k, self.field.prepare_query_value(op, v)) for k, v in value.items()) return self.field.prepare_query_value(op, value) return super(DictField, self).prepare_query_value(op, value) class MapField(DictField): """A field that maps a name to a specified field type. Similar to a DictField, except the 'value' of each item must match the specified field type. .. versionadded:: 0.5 """ def __init__(self, field=None, *args, **kwargs): if not isinstance(field, BaseField): self.error('Argument to MapField constructor must be a valid ' 'field') super(MapField, self).__init__(field=field, *args, **kwargs) class ReferenceField(BaseField): """A reference to a document that will be automatically dereferenced on access (lazily). Use the `reverse_delete_rule` to handle what should happen if the document the field is referencing is deleted. EmbeddedDocuments, DictFields and MapFields does not support reverse_delete_rule and an `InvalidDocumentError` will be raised if trying to set on one of these Document / Field types. The options are: * DO_NOTHING (0) - don't do anything (default). * NULLIFY (1) - Updates the reference to null. * CASCADE (2) - Deletes the documents associated with the reference. * DENY (3) - Prevent the deletion of the reference object. * PULL (4) - Pull the reference from a :class:`~mongoengine.fields.ListField` of references Alternative syntax for registering delete rules (useful when implementing bi-directional delete rules) .. code-block:: python class Bar(Document): content = StringField() foo = ReferenceField('Foo') Bar.register_delete_rule(Foo, 'bar', NULLIFY) .. note :: `reverse_delete_rule` does not trigger pre / post delete signals to be triggered. .. versionchanged:: 0.5 added `reverse_delete_rule` """ def __init__(self, document_type, dbref=False, reverse_delete_rule=DO_NOTHING, **kwargs): """Initialises the Reference Field. :param dbref: Store the reference as :class:`~pymongo.dbref.DBRef` or as the :class:`~pymongo.objectid.ObjectId`.id . :param reverse_delete_rule: Determines what to do when the referring object is deleted .. note :: A reference to an abstract document type is always stored as a :class:`~pymongo.dbref.DBRef`, regardless of the value of `dbref`. """ if not isinstance(document_type, basestring): if not issubclass(document_type, (Document, basestring)): self.error('Argument to ReferenceField constructor must be a ' 'document class or a string') self.dbref = dbref self.document_type_obj = document_type self.reverse_delete_rule = reverse_delete_rule super(ReferenceField, self).__init__(**kwargs) @property def document_type(self): if isinstance(self.document_type_obj, basestring): if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT: self.document_type_obj = self.owner_document else: self.document_type_obj = get_document(self.document_type_obj) return self.document_type_obj def __get__(self, instance, owner): """Descriptor to allow lazy dereferencing. """ if instance is None: # Document class being used rather than a document object return self # Get value from document instance if available value = instance._data.get(self.name) self._auto_dereference = instance._fields[self.name]._auto_dereference # Dereference DBRefs if self._auto_dereference and isinstance(value, DBRef): if hasattr(value, 'cls'): # Dereference using the class type specified in the reference cls = get_document(value.cls) else: cls = self.document_type value = cls._get_db().dereference(value) if value is not None: instance._data[self.name] = cls._from_son(value) return super(ReferenceField, self).__get__(instance, owner) def to_mongo(self, document): if isinstance(document, DBRef): if not self.dbref: return document.id return document if isinstance(document, Document): # We need the id from the saved object to create the DBRef id_ = document.pk if id_ is None: self.error('You can only reference documents once they have' ' been saved to the database') # Use the attributes from the document instance, so that they # override the attributes of this field's document type cls = document else: id_ = document cls = self.document_type id_field_name = cls._meta['id_field'] id_field = cls._fields[id_field_name] id_ = id_field.to_mongo(id_) if self.document_type._meta.get('abstract'): collection = cls._get_collection_name() return DBRef(collection, id_, cls=cls._class_name) elif self.dbref: collection = cls._get_collection_name() return DBRef(collection, id_) return id_ def to_python(self, value): """Convert a MongoDB-compatible type to a Python type. """ if (not self.dbref and not isinstance(value, (DBRef, Document, EmbeddedDocument))): collection = self.document_type._get_collection_name() value = DBRef(collection, self.document_type.id.to_python(value)) return value def prepare_query_value(self, op, value): if value is None: return None super(ReferenceField, self).prepare_query_value(op, value) return self.to_mongo(value) def validate(self, value): if not isinstance(value, (self.document_type, DBRef)): self.error("A ReferenceField only accepts DBRef or documents") if isinstance(value, Document) and value.id is None: self.error('You can only reference documents once they have been ' 'saved to the database') if self.document_type._meta.get('abstract') and \ not isinstance(value, self.document_type): self.error('%s is not an instance of abstract reference' ' type %s' % (value._class_name, self.document_type._class_name) ) def lookup_member(self, member_name): return self.document_type._fields.get(member_name) class CachedReferenceField(BaseField): """ A referencefield with cache fields to purpose pseudo-joins .. versionadded:: 0.9 """ def __init__(self, document_type, fields=[], auto_sync=True, **kwargs): """Initialises the Cached Reference Field. :param fields: A list of fields to be cached in document :param auto_sync: if True documents are auto updated. """ if not isinstance(document_type, basestring) and \ not issubclass(document_type, (Document, basestring)): self.error('Argument to CachedReferenceField constructor must be a' ' document class or a string') self.auto_sync = auto_sync self.document_type_obj = document_type self.fields = fields super(CachedReferenceField, self).__init__(**kwargs) def start_listener(self): from mongoengine import signals signals.post_save.connect(self.on_document_pre_save, sender=self.document_type) def on_document_pre_save(self, sender, document, created, **kwargs): if not created: update_kwargs = dict( ('set__%s__%s' % (self.name, k), v) for k, v in document._delta()[0].items() if k in self.fields) if update_kwargs: filter_kwargs = {} filter_kwargs[self.name] = document self.owner_document.objects( **filter_kwargs).update(**update_kwargs) def to_python(self, value): if isinstance(value, dict): collection = self.document_type._get_collection_name() value = DBRef( collection, self.document_type.id.to_python(value['_id'])) return self.document_type._from_son(self.document_type._get_db().dereference(value)) return value @property def document_type(self): if isinstance(self.document_type_obj, basestring): if self.document_type_obj == RECURSIVE_REFERENCE_CONSTANT: self.document_type_obj = self.owner_document else: self.document_type_obj = get_document(self.document_type_obj) return self.document_type_obj def __get__(self, instance, owner): if instance is None: # Document class being used rather than a document object return self # Get value from document instance if available value = instance._data.get(self.name) self._auto_dereference = instance._fields[self.name]._auto_dereference # Dereference DBRefs if self._auto_dereference and isinstance(value, DBRef): value = self.document_type._get_db().dereference(value) if value is not None: instance._data[self.name] = self.document_type._from_son(value) return super(CachedReferenceField, self).__get__(instance, owner) def to_mongo(self, document): id_field_name = self.document_type._meta['id_field'] id_field = self.document_type._fields[id_field_name] if isinstance(document, Document): # We need the id from the saved object to create the DBRef id_ = document.pk if id_ is None: self.error('You can only reference documents once they have' ' been saved to the database') else: self.error('Only accept a document object') # TODO: should raise here or will fail next statement value = SON(( ("_id", id_field.to_mongo(id_)), )) value.update(dict(document.to_mongo(fields=self.fields))) return value def prepare_query_value(self, op, value): if value is None: return None if isinstance(value, Document): if value.pk is None: self.error('You can only reference documents once they have' ' been saved to the database') return {'_id': value.pk} raise NotImplementedError def validate(self, value): if not isinstance(value, self.document_type): self.error("A CachedReferenceField only accepts documents") if isinstance(value, Document) and value.id is None: self.error('You can only reference documents once they have been ' 'saved to the database') def lookup_member(self, member_name): return self.document_type._fields.get(member_name) def sync_all(self): """ Sync all cached fields on demand. Caution: this operation may be slower. """ update_key = 'set__%s' % self.name for doc in self.document_type.objects: filter_kwargs = {} filter_kwargs[self.name] = doc update_kwargs = {} update_kwargs[update_key] = doc self.owner_document.objects( **filter_kwargs).update(**update_kwargs) class GenericReferenceField(BaseField): """A reference to *any* :class:`~mongoengine.document.Document` subclass that will be automatically dereferenced on access (lazily). .. note :: * Any documents used as a generic reference must be registered in the document registry. Importing the model will automatically register it. * You can use the choices param to limit the acceptable Document types .. versionadded:: 0.3 """ def __init__(self, *args, **kwargs): choices = kwargs.pop('choices', None) super(GenericReferenceField, self).__init__(*args, **kwargs) self.choices = [] # Keep the choices as a list of allowed Document class names if choices: for choice in choices: if isinstance(choice, basestring): self.choices.append(choice) elif isinstance(choice, type) and issubclass(choice, Document): self.choices.append(choice._class_name) else: self.error('Invalid choices provided: must be a list of' 'Document subclasses and/or basestrings') def _validate_choices(self, value): if isinstance(value, dict): # If the field has not been dereferenced, it is still a dict # of class and DBRef value = value.get('_cls') elif isinstance(value, Document): value = value._class_name super(GenericReferenceField, self)._validate_choices(value) def __get__(self, instance, owner): if instance is None: return self value = instance._data.get(self.name) self._auto_dereference = instance._fields[self.name]._auto_dereference if self._auto_dereference and isinstance(value, (dict, SON)): instance._data[self.name] = self.dereference(value) return super(GenericReferenceField, self).__get__(instance, owner) def validate(self, value): if not isinstance(value, (Document, DBRef, dict, SON)): self.error('GenericReferences can only contain documents') if isinstance(value, (dict, SON)): if '_ref' not in value or '_cls' not in value: self.error('GenericReferences can only contain documents') # We need the id from the saved object to create the DBRef elif isinstance(value, Document) and value.id is None: self.error('You can only reference documents once they have been' ' saved to the database') def dereference(self, value): doc_cls = get_document(value['_cls']) reference = value['_ref'] doc = doc_cls._get_db().dereference(reference) if doc is not None: doc = doc_cls._from_son(doc) return doc def to_mongo(self, document, use_db_field=True): if document is None: return None if isinstance(document, (dict, SON)): return document id_field_name = document.__class__._meta['id_field'] id_field = document.__class__._fields[id_field_name] if isinstance(document, Document): # We need the id from the saved object to create the DBRef id_ = document.id if id_ is None: self.error('You can only reference documents once they have' ' been saved to the database') else: id_ = document id_ = id_field.to_mongo(id_) collection = document._get_collection_name() ref = DBRef(collection, id_) return SON(( ('_cls', document._class_name), ('_ref', ref) )) def prepare_query_value(self, op, value): if value is None: return None return self.to_mongo(value) class BinaryField(BaseField): """A binary data field. """ def __init__(self, max_bytes=None, **kwargs): self.max_bytes = max_bytes super(BinaryField, self).__init__(**kwargs) def __set__(self, instance, value): """Handle bytearrays in python 3.1""" if PY3 and isinstance(value, bytearray): value = bin_type(value) return super(BinaryField, self).__set__(instance, value) def to_mongo(self, value): return Binary(value) def validate(self, value): if not isinstance(value, (bin_type, txt_type, Binary)): self.error("BinaryField only accepts instances of " "(%s, %s, Binary)" % ( bin_type.__name__, txt_type.__name__)) if self.max_bytes is not None and len(value) > self.max_bytes: self.error('Binary value is too long') class GridFSError(Exception): pass class GridFSProxy(object): """Proxy object to handle writing and reading of files to and from GridFS .. versionadded:: 0.4 .. versionchanged:: 0.5 - added optional size param to read .. versionchanged:: 0.6 - added collection name param """ _fs = None def __init__(self, grid_id=None, key=None, instance=None, db_alias=DEFAULT_CONNECTION_NAME, collection_name='fs'): self.grid_id = grid_id # Store GridFS id for file self.key = key self.instance = instance self.db_alias = db_alias self.collection_name = collection_name self.newfile = None # Used for partial writes self.gridout = None def __getattr__(self, name): attrs = ('_fs', 'grid_id', 'key', 'instance', 'db_alias', 'collection_name', 'newfile', 'gridout') if name in attrs: return self.__getattribute__(name) obj = self.get() if hasattr(obj, name): return getattr(obj, name) raise AttributeError def __get__(self, instance, value): return self def __nonzero__(self): return bool(self.grid_id) def __getstate__(self): self_dict = self.__dict__ self_dict['_fs'] = None return self_dict def __copy__(self): copied = GridFSProxy() copied.__dict__.update(self.__getstate__()) return copied def __deepcopy__(self, memo): return self.__copy__() def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self.grid_id) def __str__(self): name = getattr( self.get(), 'filename', self.grid_id) if self.get() else '(no file)' return '<%s: %s>' % (self.__class__.__name__, name) def __eq__(self, other): if isinstance(other, GridFSProxy): return ((self.grid_id == other.grid_id) and (self.collection_name == other.collection_name) and (self.db_alias == other.db_alias)) else: return False @property def fs(self): if not self._fs: self._fs = gridfs.GridFS( get_db(self.db_alias), self.collection_name) return self._fs def get(self, id=None): if id: self.grid_id = id if self.grid_id is None: return None try: if self.gridout is None: self.gridout = self.fs.get(self.grid_id) return self.gridout except: # File has been deleted return None def new_file(self, **kwargs): self.newfile = self.fs.new_file(**kwargs) self.grid_id = self.newfile._id self._mark_as_changed() def put(self, file_obj, **kwargs): if self.grid_id: raise GridFSError('This document already has a file. Either delete ' 'it or call replace to overwrite it') self.grid_id = self.fs.put(file_obj, **kwargs) self._mark_as_changed() def write(self, string): if self.grid_id: if not self.newfile: raise GridFSError('This document already has a file. Either ' 'delete it or call replace to overwrite it') else: self.new_file() self.newfile.write(string) def writelines(self, lines): if not self.newfile: self.new_file() self.grid_id = self.newfile._id self.newfile.writelines(lines) def read(self, size=-1): gridout = self.get() if gridout is None: return None else: try: return gridout.read(size) except: return "" def delete(self): # Delete file from GridFS, FileField still remains self.fs.delete(self.grid_id) self.grid_id = None self.gridout = None self._mark_as_changed() def replace(self, file_obj, **kwargs): self.delete() self.put(file_obj, **kwargs) def close(self): if self.newfile: self.newfile.close() def _mark_as_changed(self): """Inform the instance that `self.key` has been changed""" if self.instance: self.instance._mark_as_changed(self.key) class FileField(BaseField): """A GridFS storage field. .. versionadded:: 0.4 .. versionchanged:: 0.5 added optional size param for read .. versionchanged:: 0.6 added db_alias for multidb support """ proxy_class = GridFSProxy def __init__(self, db_alias=DEFAULT_CONNECTION_NAME, collection_name="fs", **kwargs): super(FileField, self).__init__(**kwargs) self.collection_name = collection_name self.db_alias = db_alias def __get__(self, instance, owner): if instance is None: return self # Check if a file already exists for this model grid_file = instance._data.get(self.name) if not isinstance(grid_file, self.proxy_class): grid_file = self.get_proxy_obj(key=self.name, instance=instance) instance._data[self.name] = grid_file if not grid_file.key: grid_file.key = self.name grid_file.instance = instance return grid_file def __set__(self, instance, value): key = self.name if ((hasattr(value, 'read') and not isinstance(value, GridFSProxy)) or isinstance(value, str_types)): # using "FileField() = file/string" notation grid_file = instance._data.get(self.name) # If a file already exists, delete it if grid_file: try: grid_file.delete() except: pass # Create a new proxy object as we don't already have one instance._data[key] = self.get_proxy_obj( key=key, instance=instance) instance._data[key].put(value) else: instance._data[key] = value instance._mark_as_changed(key) def get_proxy_obj(self, key, instance, db_alias=None, collection_name=None): if db_alias is None: db_alias = self.db_alias if collection_name is None: collection_name = self.collection_name return self.proxy_class(key=key, instance=instance, db_alias=db_alias, collection_name=collection_name) def to_mongo(self, value): # Store the GridFS file id in MongoDB if isinstance(value, self.proxy_class) and value.grid_id is not None: return value.grid_id return None def to_python(self, value): if value is not None: return self.proxy_class(value, collection_name=self.collection_name, db_alias=self.db_alias) def validate(self, value): if value.grid_id is not None: if not isinstance(value, self.proxy_class): self.error('FileField only accepts GridFSProxy values') if not isinstance(value.grid_id, ObjectId): self.error('Invalid GridFSProxy value') class ImageGridFsProxy(GridFSProxy): """ Proxy for ImageField versionadded: 0.6 """ def put(self, file_obj, **kwargs): """ Insert a image in database applying field properties (size, thumbnail_size) """ field = self.instance._fields[self.key] # Handle nested fields if hasattr(field, 'field') and isinstance(field.field, FileField): field = field.field try: img = Image.open(file_obj) img_format = img.format except Exception, e: raise ValidationError('Invalid image: %s' % e) # Progressive JPEG # TODO: fixme, at least unused, at worst bad implementation progressive = img.info.get('progressive') or False if (kwargs.get('progressive') and isinstance(kwargs.get('progressive'), bool) and img_format == 'JPEG'): progressive = True else: progressive = False if (field.size and (img.size[0] > field.size['width'] or img.size[1] > field.size['height'])): size = field.size if size['force']: img = ImageOps.fit(img, (size['width'], size['height']), Image.ANTIALIAS) else: img.thumbnail((size['width'], size['height']), Image.ANTIALIAS) thumbnail = None if field.thumbnail_size: size = field.thumbnail_size if size['force']: thumbnail = ImageOps.fit( img, (size['width'], size['height']), Image.ANTIALIAS) else: thumbnail = img.copy() thumbnail.thumbnail((size['width'], size['height']), Image.ANTIALIAS) if thumbnail: thumb_id = self._put_thumbnail(thumbnail, img_format, progressive) else: thumb_id = None w, h = img.size io = StringIO() img.save(io, img_format, progressive=progressive) io.seek(0) return super(ImageGridFsProxy, self).put(io, width=w, height=h, format=img_format, thumbnail_id=thumb_id, **kwargs) def delete(self, *args, **kwargs): # deletes thumbnail out = self.get() if out and out.thumbnail_id: self.fs.delete(out.thumbnail_id) return super(ImageGridFsProxy, self).delete() def _put_thumbnail(self, thumbnail, format, progressive, **kwargs): w, h = thumbnail.size io = StringIO() thumbnail.save(io, format, progressive=progressive) io.seek(0) return self.fs.put(io, width=w, height=h, format=format, **kwargs) @property def size(self): """ return a width, height of image """ out = self.get() if out: return out.width, out.height @property def format(self): """ return format of image ex: PNG, JPEG, GIF, etc """ out = self.get() if out: return out.format @property def thumbnail(self): """ return a gridfs.grid_file.GridOut representing a thumbnail of Image """ out = self.get() if out and out.thumbnail_id: return self.fs.get(out.thumbnail_id) def write(self, *args, **kwargs): raise RuntimeError("Please use \"put\" method instead") def writelines(self, *args, **kwargs): raise RuntimeError("Please use \"put\" method instead") class ImproperlyConfigured(Exception): pass class ImageField(FileField): """ A Image File storage field. @size (width, height, force): max size to store images, if larger will be automatically resized ex: size=(800, 600, True) @thumbnail (width, height, force): size to generate a thumbnail .. versionadded:: 0.6 """ proxy_class = ImageGridFsProxy def __init__(self, size=None, thumbnail_size=None, collection_name='images', **kwargs): if not Image: raise ImproperlyConfigured("PIL library was not found") params_size = ('width', 'height', 'force') extra_args = dict(size=size, thumbnail_size=thumbnail_size) for att_name, att in extra_args.items(): value = None if isinstance(att, (tuple, list)): if PY3: value = dict(itertools.zip_longest(params_size, att, fillvalue=None)) else: value = dict(map(None, params_size, att)) setattr(self, att_name, value) super(ImageField, self).__init__( collection_name=collection_name, **kwargs) class SequenceField(BaseField): """Provides a sequential counter see: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-SequenceNumbers .. note:: Although traditional databases often use increasing sequence numbers for primary keys. In MongoDB, the preferred approach is to use Object IDs instead. The concept is that in a very large cluster of machines, it is easier to create an object ID than have global, uniformly increasing sequence numbers. :param collection_name: Name of the counter collection (default 'mongoengine.counters') :param sequence_name: Name of the sequence in the collection (default 'ClassName.counter') :param value_decorator: Any callable to use as a counter (default int) Use any callable as `value_decorator` to transform calculated counter into any value suitable for your needs, e.g. string or hexadecimal representation of the default integer counter value. .. note:: In case the counter is defined in the abstract document, it will be common to all inherited documents and the default sequence name will be the class name of the abstract document. .. versionadded:: 0.5 .. versionchanged:: 0.8 added `value_decorator` """ _auto_gen = True COLLECTION_NAME = 'mongoengine.counters' VALUE_DECORATOR = int def __init__(self, collection_name=None, db_alias=None, sequence_name=None, value_decorator=None, *args, **kwargs): self.collection_name = collection_name or self.COLLECTION_NAME self.db_alias = db_alias or DEFAULT_CONNECTION_NAME self.sequence_name = sequence_name self.value_decorator = (callable(value_decorator) and value_decorator or self.VALUE_DECORATOR) super(SequenceField, self).__init__(*args, **kwargs) def generate(self): """ Generate and Increment the counter """ sequence_name = self.get_sequence_name() sequence_id = "%s.%s" % (sequence_name, self.name) collection = get_db(alias=self.db_alias)[self.collection_name] counter = collection.find_and_modify(query={"_id": sequence_id}, update={"$inc": {"next": 1}}, new=True, upsert=True) return self.value_decorator(counter['next']) def set_next_value(self, value): """Helper method to set the next sequence value""" sequence_name = self.get_sequence_name() sequence_id = "%s.%s" % (sequence_name, self.name) collection = get_db(alias=self.db_alias)[self.collection_name] counter = collection.find_and_modify(query={"_id": sequence_id}, update={"$set": {"next": value}}, new=True, upsert=True) return self.value_decorator(counter['next']) def get_next_value(self): """Helper method to get the next value for previewing. .. warning:: There is no guarantee this will be the next value as it is only fixed on set. """ sequence_name = self.get_sequence_name() sequence_id = "%s.%s" % (sequence_name, self.name) collection = get_db(alias=self.db_alias)[self.collection_name] data = collection.find_one({"_id": sequence_id}) if data: return self.value_decorator(data['next'] + 1) return self.value_decorator(1) def get_sequence_name(self): if self.sequence_name: return self.sequence_name owner = self.owner_document if issubclass(owner, Document) and not owner._meta.get('abstract'): return owner._get_collection_name() else: return ''.join('_%s' % c if c.isupper() else c for c in owner._class_name).strip('_').lower() def __get__(self, instance, owner): value = super(SequenceField, self).__get__(instance, owner) if value is None and instance._initialised: value = self.generate() instance._data[self.name] = value instance._mark_as_changed(self.name) return value def __set__(self, instance, value): if value is None and instance._initialised: value = self.generate() return super(SequenceField, self).__set__(instance, value) def prepare_query_value(self, op, value): """ This method is overridden in order to convert the query value into to required type. We need to do this in order to be able to successfully compare query values passed as string, the base implementation returns the value as is. """ return self.value_decorator(value) def to_python(self, value): if value is None: value = self.generate() return value class UUIDField(BaseField): """A UUID field. .. versionadded:: 0.6 """ _binary = None def __init__(self, binary=True, **kwargs): """ Store UUID data in the database :param binary: if False store as a string. .. versionchanged:: 0.8.0 .. versionchanged:: 0.6.19 """ self._binary = binary super(UUIDField, self).__init__(**kwargs) def to_python(self, value): if not self._binary: original_value = value try: if not isinstance(value, basestring): value = unicode(value) return uuid.UUID(value) except: return original_value return value def to_mongo(self, value): if not self._binary: return unicode(value) elif isinstance(value, basestring): return uuid.UUID(value) return value def prepare_query_value(self, op, value): if value is None: return None return self.to_mongo(value) def validate(self, value): if not isinstance(value, uuid.UUID): if not isinstance(value, basestring): value = str(value) try: uuid.UUID(value) except Exception, exc: self.error('Could not convert to UUID: %s' % exc) class GeoPointField(BaseField): """A list storing a longitude and latitude coordinate. .. note:: this represents a generic point in a 2D plane and a legacy way of representing a geo point. It admits 2d indexes but not "2dsphere" indexes in MongoDB > 2.4 which are more natural for modeling geospatial points. See :ref:`geospatial-indexes` .. versionadded:: 0.4 """ _geo_index = pymongo.GEO2D def validate(self, value): """Make sure that a geo-value is of type (x, y) """ if not isinstance(value, (list, tuple)): self.error('GeoPointField can only accept tuples or lists ' 'of (x, y)') if not len(value) == 2: self.error("Value (%s) must be a two-dimensional point" % repr(value)) elif (not isinstance(value[0], (float, int)) or not isinstance(value[1], (float, int))): self.error( "Both values (%s) in point must be float or int" % repr(value)) class PointField(GeoJsonBaseField): """A GeoJSON field storing a longitude and latitude coordinate. The data is represented as: .. code-block:: js { "type" : "Point" , "coordinates" : [x, y]} You can either pass a dict with the full information or a list to set the value. Requires mongodb >= 2.4 .. versionadded:: 0.8 """ _type = "Point" class LineStringField(GeoJsonBaseField): """A GeoJSON field storing a line of longitude and latitude coordinates. The data is represented as: .. code-block:: js { "type" : "LineString" , "coordinates" : [[x1, y1], [x1, y1] ... [xn, yn]]} You can either pass a dict with the full information or a list of points. Requires mongodb >= 2.4 .. versionadded:: 0.8 """ _type = "LineString" class PolygonField(GeoJsonBaseField): """A GeoJSON field storing a polygon of longitude and latitude coordinates. The data is represented as: .. code-block:: js { "type" : "Polygon" , "coordinates" : [[[x1, y1], [x1, y1] ... [xn, yn]], [[x1, y1], [x1, y1] ... [xn, yn]]} You can either pass a dict with the full information or a list of LineStrings. The first LineString being the outside and the rest being holes. Requires mongodb >= 2.4 .. versionadded:: 0.8 """ _type = "Polygon" class MultiPointField(GeoJsonBaseField): """A GeoJSON field storing a list of Points. The data is represented as: .. code-block:: js { "type" : "MultiPoint" , "coordinates" : [[x1, y1], [x2, y2]]} You can either pass a dict with the full information or a list to set the value. Requires mongodb >= 2.6 .. versionadded:: 0.9 """ _type = "MultiPoint" class MultiLineStringField(GeoJsonBaseField): """A GeoJSON field storing a list of LineStrings. The data is represented as: .. code-block:: js { "type" : "MultiLineString" , "coordinates" : [[[x1, y1], [x1, y1] ... [xn, yn]], [[x1, y1], [x1, y1] ... [xn, yn]]]} You can either pass a dict with the full information or a list of points. Requires mongodb >= 2.6 .. versionadded:: 0.9 """ _type = "MultiLineString" class MultiPolygonField(GeoJsonBaseField): """A GeoJSON field storing list of Polygons. The data is represented as: .. code-block:: js { "type" : "MultiPolygon" , "coordinates" : [[ [[x1, y1], [x1, y1] ... [xn, yn]], [[x1, y1], [x1, y1] ... [xn, yn]] ], [ [[x1, y1], [x1, y1] ... [xn, yn]], [[x1, y1], [x1, y1] ... [xn, yn]] ] } You can either pass a dict with the full information or a list of Polygons. Requires mongodb >= 2.6 .. versionadded:: 0.9 """ _type = "MultiPolygon" mongoengine-0.10.6/mongoengine/errors.py0000664000175000017500000000750412651363712017716 0ustar travistravisfrom collections import defaultdict from mongoengine.python_support import txt_type __all__ = ('NotRegistered', 'InvalidDocumentError', 'LookUpError', 'DoesNotExist', 'MultipleObjectsReturned', 'InvalidQueryError', 'OperationError', 'NotUniqueError', 'FieldDoesNotExist', 'ValidationError', 'SaveConditionError') class NotRegistered(Exception): pass class InvalidDocumentError(Exception): pass class LookUpError(AttributeError): pass class DoesNotExist(Exception): pass class MultipleObjectsReturned(Exception): pass class InvalidQueryError(Exception): pass class OperationError(Exception): pass class NotUniqueError(OperationError): pass class SaveConditionError(OperationError): pass class FieldDoesNotExist(Exception): """Raised when trying to set a field not declared in a :class:`~mongoengine.Document` or an :class:`~mongoengine.EmbeddedDocument`. To avoid this behavior on data loading, you should the :attr:`strict` to ``False`` in the :attr:`meta` dictionnary. """ class ValidationError(AssertionError): """Validation exception. May represent an error validating a field or a document containing fields with validation errors. :ivar errors: A dictionary of errors for fields within this document or list, or None if the error is for an individual field. """ errors = {} field_name = None _message = None def __init__(self, message="", **kwargs): self.errors = kwargs.get('errors', {}) self.field_name = kwargs.get('field_name') self.message = message def __str__(self): return txt_type(self.message) def __repr__(self): return '%s(%s,)' % (self.__class__.__name__, self.message) def __getattribute__(self, name): message = super(ValidationError, self).__getattribute__(name) if name == 'message': if self.field_name: message = '%s' % message if self.errors: message = '%s(%s)' % (message, self._format_errors()) return message def _get_message(self): return self._message def _set_message(self, message): self._message = message message = property(_get_message, _set_message) def to_dict(self): """Returns a dictionary of all errors within a document Keys are field names or list indices and values are the validation error messages, or a nested dictionary of errors for an embedded document or list. """ def build_dict(source): errors_dict = {} if not source: return errors_dict if isinstance(source, dict): for field_name, error in source.iteritems(): errors_dict[field_name] = build_dict(error) elif isinstance(source, ValidationError) and source.errors: return build_dict(source.errors) else: return unicode(source) return errors_dict if not self.errors: return {} return build_dict(self.errors) def _format_errors(self): """Returns a string listing all errors within a document""" def generate_key(value, prefix=''): if isinstance(value, list): value = ' '.join([generate_key(k) for k in value]) elif isinstance(value, dict): value = ' '.join( [generate_key(v, k) for k, v in value.iteritems()]) results = "%s.%s" % (prefix, value) if prefix else value return results error_dict = defaultdict(list) for k, v in self.to_dict().iteritems(): error_dict[generate_key(v)].append(k) return ' '.join(["%s: %s" % (k, v) for k, v in error_dict.iteritems()]) mongoengine-0.10.6/mongoengine/base/0000775000175000017500000000000012651364023016730 5ustar travistravismongoengine-0.10.6/mongoengine/base/fields.py0000664000175000017500000005600012651363712020555 0ustar travistravisimport operator import warnings import weakref from bson import DBRef, ObjectId, SON import pymongo from mongoengine.common import _import_class from mongoengine.errors import ValidationError from mongoengine.base.common import ALLOW_INHERITANCE from mongoengine.base.datastructures import ( BaseDict, BaseList, EmbeddedDocumentList ) __all__ = ("BaseField", "ComplexBaseField", "ObjectIdField", "GeoJsonBaseField") UPDATE_OPERATORS = set(['set', 'unset', 'inc', 'dec', 'pop', 'push', 'push_all', 'pull', 'pull_all', 'add_to_set', 'set_on_insert', 'min', 'max']) class BaseField(object): """A base class for fields in a MongoDB document. Instances of this class may be added to subclasses of `Document` to define a document's schema. .. versionchanged:: 0.5 - added verbose and help text """ name = None _geo_index = False _auto_gen = False # Call `generate` to generate a value _auto_dereference = True # These track each time a Field instance is created. Used to retain order. # The auto_creation_counter is used for fields that MongoEngine implicitly # creates, creation_counter is used for all user-specified fields. creation_counter = 0 auto_creation_counter = -1 def __init__(self, db_field=None, name=None, required=False, default=None, unique=False, unique_with=None, primary_key=False, validation=None, choices=None, null=False, sparse=False, **kwargs): """ :param db_field: The database field to store this field in (defaults to the name of the field) :param name: Depreciated - use db_field :param required: If the field is required. Whether it has to have a value or not. Defaults to False. :param default: (optional) The default value for this field if no value has been set (or if the value has been unset). It can be a callable. :param unique: Is the field value unique or not. Defaults to False. :param unique_with: (optional) The other field this field should be unique with. :param primary_key: Mark this field as the primary key. Defaults to False. :param validation: (optional) A callable to validate the value of the field. Generally this is deprecated in favour of the `FIELD.validate` method :param choices: (optional) The valid choices :param null: (optional) Is the field value can be null. If no and there is a default value then the default value is set :param sparse: (optional) `sparse=True` combined with `unique=True` and `required=False` means that uniqueness won't be enforced for `None` values :param **kwargs: (optional) Arbitrary indirection-free metadata for this field can be supplied as additional keyword arguments and accessed as attributes of the field. Must not conflict with any existing attributes. Common metadata includes `verbose_name` and `help_text`. """ self.db_field = (db_field or name) if not primary_key else '_id' if name: msg = "Fields' 'name' attribute deprecated in favour of 'db_field'" warnings.warn(msg, DeprecationWarning) self.required = required or primary_key self.default = default self.unique = bool(unique or unique_with) self.unique_with = unique_with self.primary_key = primary_key self.validation = validation self.choices = choices self.null = null self.sparse = sparse self._owner_document = None # Detect and report conflicts between metadata and base properties. conflicts = set(dir(self)) & set(kwargs) if conflicts: raise TypeError("%s already has attribute(s): %s" % ( self.__class__.__name__, ', '.join(conflicts) )) # Assign metadata to the instance # This efficient method is available because no __slots__ are defined. self.__dict__.update(kwargs) # Adjust the appropriate creation counter, and save our local copy. if self.db_field == '_id': self.creation_counter = BaseField.auto_creation_counter BaseField.auto_creation_counter -= 1 else: self.creation_counter = BaseField.creation_counter BaseField.creation_counter += 1 def __get__(self, instance, owner): """Descriptor for retrieving a value from a field in a document. """ if instance is None: # Document class being used rather than a document object return self # Get value from document instance if available return instance._data.get(self.name) def __set__(self, instance, value): """Descriptor for assigning a value to a field in a document. """ # If setting to None and there is a default # Then set the value to the default value if value is None: if self.null: value = None elif self.default is not None: value = self.default if callable(value): value = value() if instance._initialised: try: if (self.name not in instance._data or instance._data[self.name] != value): instance._mark_as_changed(self.name) except: # Values cant be compared eg: naive and tz datetimes # So mark it as changed instance._mark_as_changed(self.name) EmbeddedDocument = _import_class('EmbeddedDocument') if isinstance(value, EmbeddedDocument): value._instance = weakref.proxy(instance) elif isinstance(value, (list, tuple)): for v in value: if isinstance(v, EmbeddedDocument): v._instance = weakref.proxy(instance) instance._data[self.name] = value def error(self, message="", errors=None, field_name=None): """Raises a ValidationError. """ field_name = field_name if field_name else self.name raise ValidationError(message, errors=errors, field_name=field_name) def to_python(self, value): """Convert a MongoDB-compatible type to a Python type. """ return value def to_mongo(self, value): """Convert a Python type to a MongoDB-compatible type. """ return self.to_python(value) def prepare_query_value(self, op, value): """Prepare a value that is being used in a query for PyMongo. """ if op in UPDATE_OPERATORS: self.validate(value) return value def validate(self, value, clean=True): """Perform validation on a value. """ pass def _validate_choices(self, value): Document = _import_class('Document') EmbeddedDocument = _import_class('EmbeddedDocument') choice_list = self.choices if isinstance(choice_list[0], (list, tuple)): choice_list = [k for k, _ in choice_list] # Choices which are other types of Documents if isinstance(value, (Document, EmbeddedDocument)): if not any(isinstance(value, c) for c in choice_list): self.error( 'Value must be instance of %s' % unicode(choice_list) ) # Choices which are types other than Documents elif value not in choice_list: self.error('Value must be one of %s' % unicode(choice_list)) def _validate(self, value, **kwargs): # Check the Choices Constraint if self.choices: self._validate_choices(value) # check validation argument if self.validation is not None: if callable(self.validation): if not self.validation(value): self.error('Value does not match custom validation method') else: raise ValueError('validation argument for "%s" must be a ' 'callable.' % self.name) self.validate(value, **kwargs) @property def owner_document(self): return self._owner_document def _set_owner_document(self, owner_document): self._owner_document = owner_document @owner_document.setter def owner_document(self, owner_document): self._set_owner_document(owner_document) class ComplexBaseField(BaseField): """Handles complex fields, such as lists / dictionaries. Allows for nesting of embedded documents inside complex types. Handles the lazy dereferencing of a queryset by lazily dereferencing all items in a list / dict rather than one at a time. .. versionadded:: 0.5 """ field = None def __get__(self, instance, owner): """Descriptor to automatically dereference references. """ if instance is None: # Document class being used rather than a document object return self ReferenceField = _import_class('ReferenceField') GenericReferenceField = _import_class('GenericReferenceField') EmbeddedDocumentListField = _import_class('EmbeddedDocumentListField') dereference = (self._auto_dereference and (self.field is None or isinstance(self.field, (GenericReferenceField, ReferenceField)))) _dereference = _import_class("DeReference")() self._auto_dereference = instance._fields[self.name]._auto_dereference if instance._initialised and dereference and instance._data.get(self.name): instance._data[self.name] = _dereference( instance._data.get(self.name), max_depth=1, instance=instance, name=self.name ) value = super(ComplexBaseField, self).__get__(instance, owner) # Convert lists / values so we can watch for any changes on them if isinstance(value, (list, tuple)): if (issubclass(type(self), EmbeddedDocumentListField) and not isinstance(value, EmbeddedDocumentList)): value = EmbeddedDocumentList(value, instance, self.name) elif not isinstance(value, BaseList): value = BaseList(value, instance, self.name) instance._data[self.name] = value elif isinstance(value, dict) and not isinstance(value, BaseDict): value = BaseDict(value, instance, self.name) instance._data[self.name] = value if (self._auto_dereference and instance._initialised and isinstance(value, (BaseList, BaseDict)) and not value._dereferenced): value = _dereference( value, max_depth=1, instance=instance, name=self.name ) value._dereferenced = True instance._data[self.name] = value return value def to_python(self, value): """Convert a MongoDB-compatible type to a Python type. """ Document = _import_class('Document') if isinstance(value, basestring): return value if hasattr(value, 'to_python'): return value.to_python() is_list = False if not hasattr(value, 'items'): try: is_list = True value = dict([(k, v) for k, v in enumerate(value)]) except TypeError: # Not iterable return the value return value if self.field: self.field._auto_dereference = self._auto_dereference value_dict = dict([(key, self.field.to_python(item)) for key, item in value.items()]) else: value_dict = {} for k, v in value.items(): if isinstance(v, Document): # We need the id from the saved object to create the DBRef if v.pk is None: self.error('You can only reference documents once they' ' have been saved to the database') collection = v._get_collection_name() value_dict[k] = DBRef(collection, v.pk) elif hasattr(v, 'to_python'): value_dict[k] = v.to_python() else: value_dict[k] = self.to_python(v) if is_list: # Convert back to a list return [v for _, v in sorted(value_dict.items(), key=operator.itemgetter(0))] return value_dict def to_mongo(self, value): """Convert a Python type to a MongoDB-compatible type. """ Document = _import_class("Document") EmbeddedDocument = _import_class("EmbeddedDocument") GenericReferenceField = _import_class("GenericReferenceField") if isinstance(value, basestring): return value if hasattr(value, 'to_mongo'): if isinstance(value, Document): return GenericReferenceField().to_mongo(value) cls = value.__class__ val = value.to_mongo() # If it's a document that is not inherited add _cls if isinstance(value, EmbeddedDocument): val['_cls'] = cls.__name__ return val is_list = False if not hasattr(value, 'items'): try: is_list = True value = dict([(k, v) for k, v in enumerate(value)]) except TypeError: # Not iterable return the value return value if self.field: value_dict = dict([(key, self.field.to_mongo(item)) for key, item in value.iteritems()]) else: value_dict = {} for k, v in value.iteritems(): if isinstance(v, Document): # We need the id from the saved object to create the DBRef if v.pk is None: self.error('You can only reference documents once they' ' have been saved to the database') # If its a document that is not inheritable it won't have # any _cls data so make it a generic reference allows # us to dereference meta = getattr(v, '_meta', {}) allow_inheritance = ( meta.get('allow_inheritance', ALLOW_INHERITANCE) is True) if not allow_inheritance and not self.field: value_dict[k] = GenericReferenceField().to_mongo(v) else: collection = v._get_collection_name() value_dict[k] = DBRef(collection, v.pk) elif hasattr(v, 'to_mongo'): cls = v.__class__ val = v.to_mongo() # If it's a document that is not inherited add _cls if isinstance(v, (Document, EmbeddedDocument)): val['_cls'] = cls.__name__ value_dict[k] = val else: value_dict[k] = self.to_mongo(v) if is_list: # Convert back to a list return [v for _, v in sorted(value_dict.items(), key=operator.itemgetter(0))] return value_dict def validate(self, value): """If field is provided ensure the value is valid. """ errors = {} if self.field: if hasattr(value, 'iteritems') or hasattr(value, 'items'): sequence = value.iteritems() else: sequence = enumerate(value) for k, v in sequence: try: self.field._validate(v) except ValidationError, error: errors[k] = error.errors or error except (ValueError, AssertionError), error: errors[k] = error if errors: field_class = self.field.__class__.__name__ self.error('Invalid %s item (%s)' % (field_class, value), errors=errors) # Don't allow empty values if required if self.required and not value: self.error('Field is required and cannot be empty') def prepare_query_value(self, op, value): return self.to_mongo(value) def lookup_member(self, member_name): if self.field: return self.field.lookup_member(member_name) return None def _set_owner_document(self, owner_document): if self.field: self.field.owner_document = owner_document self._owner_document = owner_document class ObjectIdField(BaseField): """A field wrapper around MongoDB's ObjectIds. """ def to_python(self, value): try: if not isinstance(value, ObjectId): value = ObjectId(value) except: pass return value def to_mongo(self, value): if not isinstance(value, ObjectId): try: return ObjectId(unicode(value)) except Exception, e: # e.message attribute has been deprecated since Python 2.6 self.error(unicode(e)) return value def prepare_query_value(self, op, value): return self.to_mongo(value) def validate(self, value): try: ObjectId(unicode(value)) except: self.error('Invalid Object ID') class GeoJsonBaseField(BaseField): """A geo json field storing a geojson style object. .. versionadded:: 0.8 """ _geo_index = pymongo.GEOSPHERE _type = "GeoBase" def __init__(self, auto_index=True, *args, **kwargs): """ :param bool auto_index: Automatically create a "2dsphere" index.\ Defaults to `True`. """ self._name = "%sField" % self._type if not auto_index: self._geo_index = False super(GeoJsonBaseField, self).__init__(*args, **kwargs) def validate(self, value): """Validate the GeoJson object based on its type """ if isinstance(value, dict): if set(value.keys()) == set(['type', 'coordinates']): if value['type'] != self._type: self.error('%s type must be "%s"' % (self._name, self._type)) return self.validate(value['coordinates']) else: self.error('%s can only accept a valid GeoJson dictionary' ' or lists of (x, y)' % self._name) return elif not isinstance(value, (list, tuple)): self.error('%s can only accept lists of [x, y]' % self._name) return validate = getattr(self, "_validate_%s" % self._type.lower()) error = validate(value) if error: self.error(error) def _validate_polygon(self, value, top_level=True): if not isinstance(value, (list, tuple)): return 'Polygons must contain list of linestrings' # Quick and dirty validator try: value[0][0][0] except: return "Invalid Polygon must contain at least one valid linestring" errors = [] for val in value: error = self._validate_linestring(val, False) if not error and val[0] != val[-1]: error = 'LineStrings must start and end at the same point' if error and error not in errors: errors.append(error) if errors: if top_level: return "Invalid Polygon:\n%s" % ", ".join(errors) else: return "%s" % ", ".join(errors) def _validate_linestring(self, value, top_level=True): """Validates a linestring""" if not isinstance(value, (list, tuple)): return 'LineStrings must contain list of coordinate pairs' # Quick and dirty validator try: value[0][0] except: return "Invalid LineString must contain at least one valid point" errors = [] for val in value: error = self._validate_point(val) if error and error not in errors: errors.append(error) if errors: if top_level: return "Invalid LineString:\n%s" % ", ".join(errors) else: return "%s" % ", ".join(errors) def _validate_point(self, value): """Validate each set of coords""" if not isinstance(value, (list, tuple)): return 'Points must be a list of coordinate pairs' elif not len(value) == 2: return "Value (%s) must be a two-dimensional point" % repr(value) elif (not isinstance(value[0], (float, int)) or not isinstance(value[1], (float, int))): return "Both values (%s) in point must be float or int" % repr(value) def _validate_multipoint(self, value): if not isinstance(value, (list, tuple)): return 'MultiPoint must be a list of Point' # Quick and dirty validator try: value[0][0] except: return "Invalid MultiPoint must contain at least one valid point" errors = [] for point in value: error = self._validate_point(point) if error and error not in errors: errors.append(error) if errors: return "%s" % ", ".join(errors) def _validate_multilinestring(self, value, top_level=True): if not isinstance(value, (list, tuple)): return 'MultiLineString must be a list of LineString' # Quick and dirty validator try: value[0][0][0] except: return "Invalid MultiLineString must contain at least one valid linestring" errors = [] for linestring in value: error = self._validate_linestring(linestring, False) if error and error not in errors: errors.append(error) if errors: if top_level: return "Invalid MultiLineString:\n%s" % ", ".join(errors) else: return "%s" % ", ".join(errors) def _validate_multipolygon(self, value): if not isinstance(value, (list, tuple)): return 'MultiPolygon must be a list of Polygon' # Quick and dirty validator try: value[0][0][0][0] except: return "Invalid MultiPolygon must contain at least one valid Polygon" errors = [] for polygon in value: error = self._validate_polygon(polygon, False) if error and error not in errors: errors.append(error) if errors: return "Invalid MultiPolygon:\n%s" % ", ".join(errors) def to_mongo(self, value): if isinstance(value, dict): return value return SON([("type", self._type), ("coordinates", value)]) mongoengine-0.10.6/mongoengine/base/datastructures.py0000664000175000017500000003640012651363712022366 0ustar travistravisimport weakref import itertools from mongoengine.common import _import_class from mongoengine.errors import DoesNotExist, MultipleObjectsReturned __all__ = ("BaseDict", "BaseList", "EmbeddedDocumentList") class BaseDict(dict): """A special dict so we can watch any changes""" _dereferenced = False _instance = None _name = None def __init__(self, dict_items, instance, name): Document = _import_class('Document') EmbeddedDocument = _import_class('EmbeddedDocument') if isinstance(instance, (Document, EmbeddedDocument)): self._instance = weakref.proxy(instance) self._name = name super(BaseDict, self).__init__(dict_items) def __getitem__(self, key, *args, **kwargs): value = super(BaseDict, self).__getitem__(key) EmbeddedDocument = _import_class('EmbeddedDocument') if isinstance(value, EmbeddedDocument) and value._instance is None: value._instance = self._instance elif not isinstance(value, BaseDict) and isinstance(value, dict): value = BaseDict(value, None, '%s.%s' % (self._name, key)) super(BaseDict, self).__setitem__(key, value) value._instance = self._instance elif not isinstance(value, BaseList) and isinstance(value, list): value = BaseList(value, None, '%s.%s' % (self._name, key)) super(BaseDict, self).__setitem__(key, value) value._instance = self._instance return value def __setitem__(self, key, value, *args, **kwargs): self._mark_as_changed(key) return super(BaseDict, self).__setitem__(key, value) def __delete__(self, *args, **kwargs): self._mark_as_changed() return super(BaseDict, self).__delete__(*args, **kwargs) def __delitem__(self, key, *args, **kwargs): self._mark_as_changed(key) return super(BaseDict, self).__delitem__(key) def __delattr__(self, key, *args, **kwargs): self._mark_as_changed(key) return super(BaseDict, self).__delattr__(key) def __getstate__(self): self.instance = None self._dereferenced = False return self def __setstate__(self, state): self = state return self def clear(self, *args, **kwargs): self._mark_as_changed() return super(BaseDict, self).clear() def pop(self, *args, **kwargs): self._mark_as_changed() return super(BaseDict, self).pop(*args, **kwargs) def popitem(self, *args, **kwargs): self._mark_as_changed() return super(BaseDict, self).popitem() def setdefault(self, *args, **kwargs): self._mark_as_changed() return super(BaseDict, self).setdefault(*args, **kwargs) def update(self, *args, **kwargs): self._mark_as_changed() return super(BaseDict, self).update(*args, **kwargs) def _mark_as_changed(self, key=None): if hasattr(self._instance, '_mark_as_changed'): if key: self._instance._mark_as_changed('%s.%s' % (self._name, key)) else: self._instance._mark_as_changed(self._name) class BaseList(list): """A special list so we can watch any changes """ _dereferenced = False _instance = None _name = None def __init__(self, list_items, instance, name): Document = _import_class('Document') EmbeddedDocument = _import_class('EmbeddedDocument') if isinstance(instance, (Document, EmbeddedDocument)): self._instance = weakref.proxy(instance) self._name = name super(BaseList, self).__init__(list_items) def __getitem__(self, key, *args, **kwargs): value = super(BaseList, self).__getitem__(key) EmbeddedDocument = _import_class('EmbeddedDocument') if isinstance(value, EmbeddedDocument) and value._instance is None: value._instance = self._instance elif not isinstance(value, BaseDict) and isinstance(value, dict): value = BaseDict(value, None, '%s.%s' % (self._name, key)) super(BaseList, self).__setitem__(key, value) value._instance = self._instance elif not isinstance(value, BaseList) and isinstance(value, list): value = BaseList(value, None, '%s.%s' % (self._name, key)) super(BaseList, self).__setitem__(key, value) value._instance = self._instance return value def __iter__(self): for i in xrange(self.__len__()): yield self[i] def __setitem__(self, key, value, *args, **kwargs): if isinstance(key, slice): self._mark_as_changed() else: self._mark_as_changed(key) return super(BaseList, self).__setitem__(key, value) def __delitem__(self, key, *args, **kwargs): if isinstance(key, slice): self._mark_as_changed() else: self._mark_as_changed(key) return super(BaseList, self).__delitem__(key) def __setslice__(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).__setslice__(*args, **kwargs) def __delslice__(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).__delslice__(*args, **kwargs) def __getstate__(self): self.instance = None self._dereferenced = False return self def __setstate__(self, state): self = state return self def __iadd__(self, other): self._mark_as_changed() return super(BaseList, self).__iadd__(other) def __imul__(self, other): self._mark_as_changed() return super(BaseList, self).__imul__(other) def append(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).append(*args, **kwargs) def extend(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).extend(*args, **kwargs) def insert(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).insert(*args, **kwargs) def pop(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).pop(*args, **kwargs) def remove(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).remove(*args, **kwargs) def reverse(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).reverse() def sort(self, *args, **kwargs): self._mark_as_changed() return super(BaseList, self).sort(*args, **kwargs) def _mark_as_changed(self, key=None): if hasattr(self._instance, '_mark_as_changed'): if key: self._instance._mark_as_changed('%s.%s' % (self._name, key)) else: self._instance._mark_as_changed(self._name) class EmbeddedDocumentList(BaseList): @classmethod def __match_all(cls, i, kwargs): items = kwargs.items() return all([ getattr(i, k) == v or str(getattr(i, k)) == v for k, v in items ]) @classmethod def __only_matches(cls, obj, kwargs): if not kwargs: return obj return filter(lambda i: cls.__match_all(i, kwargs), obj) def __init__(self, list_items, instance, name): super(EmbeddedDocumentList, self).__init__(list_items, instance, name) self._instance = instance def filter(self, **kwargs): """ Filters the list by only including embedded documents with the given keyword arguments. :param kwargs: The keyword arguments corresponding to the fields to filter on. *Multiple arguments are treated as if they are ANDed together.* :return: A new ``EmbeddedDocumentList`` containing the matching embedded documents. Raises ``AttributeError`` if a given keyword is not a valid field for the embedded document class. """ values = self.__only_matches(self, kwargs) return EmbeddedDocumentList(values, self._instance, self._name) def exclude(self, **kwargs): """ Filters the list by excluding embedded documents with the given keyword arguments. :param kwargs: The keyword arguments corresponding to the fields to exclude on. *Multiple arguments are treated as if they are ANDed together.* :return: A new ``EmbeddedDocumentList`` containing the non-matching embedded documents. Raises ``AttributeError`` if a given keyword is not a valid field for the embedded document class. """ exclude = self.__only_matches(self, kwargs) values = [item for item in self if item not in exclude] return EmbeddedDocumentList(values, self._instance, self._name) def count(self): """ The number of embedded documents in the list. :return: The length of the list, equivalent to the result of ``len()``. """ return len(self) def get(self, **kwargs): """ Retrieves an embedded document determined by the given keyword arguments. :param kwargs: The keyword arguments corresponding to the fields to search on. *Multiple arguments are treated as if they are ANDed together.* :return: The embedded document matched by the given keyword arguments. Raises ``DoesNotExist`` if the arguments used to query an embedded document returns no results. ``MultipleObjectsReturned`` if more than one result is returned. """ values = self.__only_matches(self, kwargs) if len(values) == 0: raise DoesNotExist( "%s matching query does not exist." % self._name ) elif len(values) > 1: raise MultipleObjectsReturned( "%d items returned, instead of 1" % len(values) ) return values[0] def first(self): """ Returns the first embedded document in the list, or ``None`` if empty. """ if len(self) > 0: return self[0] def create(self, **values): """ Creates a new embedded document and saves it to the database. .. note:: The embedded document changes are not automatically saved to the database after calling this method. :param values: A dictionary of values for the embedded document. :return: The new embedded document instance. """ name = self._name EmbeddedClass = self._instance._fields[name].field.document_type_obj self._instance[self._name].append(EmbeddedClass(**values)) return self._instance[self._name][-1] def save(self, *args, **kwargs): """ Saves the ancestor document. :param args: Arguments passed up to the ancestor Document's save method. :param kwargs: Keyword arguments passed up to the ancestor Document's save method. """ self._instance.save(*args, **kwargs) def delete(self): """ Deletes the embedded documents from the database. .. note:: The embedded document changes are not automatically saved to the database after calling this method. :return: The number of entries deleted. """ values = list(self) for item in values: self._instance[self._name].remove(item) return len(values) def update(self, **update): """ Updates the embedded documents with the given update values. .. note:: The embedded document changes are not automatically saved to the database after calling this method. :param update: A dictionary of update values to apply to each embedded document. :return: The number of entries updated. """ if len(update) == 0: return 0 values = list(self) for item in values: for k, v in update.items(): setattr(item, k, v) return len(values) class StrictDict(object): __slots__ = () _special_fields = set(['get', 'pop', 'iteritems', 'items', 'keys', 'create']) _classes = {} def __init__(self, **kwargs): for k, v in kwargs.iteritems(): setattr(self, k, v) def __getitem__(self, key): key = '_reserved_' + key if key in self._special_fields else key try: return getattr(self, key) except AttributeError: raise KeyError(key) def __setitem__(self, key, value): key = '_reserved_' + key if key in self._special_fields else key return setattr(self, key, value) def __contains__(self, key): return hasattr(self, key) def get(self, key, default=None): try: return self[key] except KeyError: return default def pop(self, key, default=None): v = self.get(key, default) try: delattr(self, key) except AttributeError: pass return v def iteritems(self): for key in self: yield key, self[key] def items(self): return [(k, self[k]) for k in iter(self)] def iterkeys(self): return iter(self) def keys(self): return list(iter(self)) def __iter__(self): return (key for key in self.__slots__ if hasattr(self, key)) def __len__(self): return len(list(self.iteritems())) def __eq__(self, other): return self.items() == other.items() def __neq__(self, other): return self.items() != other.items() @classmethod def create(cls, allowed_keys): allowed_keys_tuple = tuple(('_reserved_' + k if k in cls._special_fields else k) for k in allowed_keys) allowed_keys = frozenset(allowed_keys_tuple) if allowed_keys not in cls._classes: class SpecificStrictDict(cls): __slots__ = allowed_keys_tuple def __repr__(self): return "{%s}" % ', '.join('"{0!s}": {0!r}'.format(k) for k in self.iterkeys()) cls._classes[allowed_keys] = SpecificStrictDict return cls._classes[allowed_keys] class SemiStrictDict(StrictDict): __slots__ = ('_extras', ) _classes = {} def __getattr__(self, attr): try: super(SemiStrictDict, self).__getattr__(attr) except AttributeError: try: return self.__getattribute__('_extras')[attr] except KeyError as e: raise AttributeError(e) def __setattr__(self, attr, value): try: super(SemiStrictDict, self).__setattr__(attr, value) except AttributeError: try: self._extras[attr] = value except AttributeError: self._extras = {attr: value} def __delattr__(self, attr): try: super(SemiStrictDict, self).__delattr__(attr) except AttributeError: try: del self._extras[attr] except KeyError as e: raise AttributeError(e) def __iter__(self): try: extras_iter = iter(self.__getattribute__('_extras')) except AttributeError: extras_iter = () return itertools.chain(super(SemiStrictDict, self).__iter__(), extras_iter) mongoengine-0.10.6/mongoengine/base/metaclasses.py0000664000175000017500000004410712651363712021620 0ustar travistravisimport warnings from mongoengine.common import _import_class from mongoengine.errors import InvalidDocumentError from mongoengine.python_support import PY3 from mongoengine.queryset import (DO_NOTHING, DoesNotExist, MultipleObjectsReturned, QuerySetManager) from mongoengine.base.common import _document_registry, ALLOW_INHERITANCE from mongoengine.base.fields import BaseField, ComplexBaseField, ObjectIdField __all__ = ('DocumentMetaclass', 'TopLevelDocumentMetaclass') class DocumentMetaclass(type): """Metaclass for all documents. """ def __new__(cls, name, bases, attrs): flattened_bases = cls._get_bases(bases) super_new = super(DocumentMetaclass, cls).__new__ # If a base class just call super metaclass = attrs.get('my_metaclass') if metaclass and issubclass(metaclass, DocumentMetaclass): return super_new(cls, name, bases, attrs) attrs['_is_document'] = attrs.get('_is_document', False) attrs['_cached_reference_fields'] = [] # EmbeddedDocuments could have meta data for inheritance if 'meta' in attrs: attrs['_meta'] = attrs.pop('meta') # EmbeddedDocuments should inherit meta data if '_meta' not in attrs: meta = MetaDict() for base in flattened_bases[::-1]: # Add any mixin metadata from plain objects if hasattr(base, 'meta'): meta.merge(base.meta) elif hasattr(base, '_meta'): meta.merge(base._meta) attrs['_meta'] = meta attrs['_meta']['abstract'] = False # 789: EmbeddedDocument shouldn't inherit abstract if attrs['_meta'].get('allow_inheritance', ALLOW_INHERITANCE): StringField = _import_class('StringField') attrs['_cls'] = StringField() # Handle document Fields # Merge all fields from subclasses doc_fields = {} for base in flattened_bases[::-1]: if hasattr(base, '_fields'): doc_fields.update(base._fields) # Standard object mixin - merge in any Fields if not hasattr(base, '_meta'): base_fields = {} for attr_name, attr_value in base.__dict__.iteritems(): if not isinstance(attr_value, BaseField): continue attr_value.name = attr_name if not attr_value.db_field: attr_value.db_field = attr_name base_fields[attr_name] = attr_value doc_fields.update(base_fields) # Discover any document fields field_names = {} for attr_name, attr_value in attrs.iteritems(): if not isinstance(attr_value, BaseField): continue attr_value.name = attr_name if not attr_value.db_field: attr_value.db_field = attr_name doc_fields[attr_name] = attr_value # Count names to ensure no db_field redefinitions field_names[attr_value.db_field] = field_names.get( attr_value.db_field, 0) + 1 # Ensure no duplicate db_fields duplicate_db_fields = [k for k, v in field_names.items() if v > 1] if duplicate_db_fields: msg = ("Multiple db_fields defined for: %s " % ", ".join(duplicate_db_fields)) raise InvalidDocumentError(msg) # Set _fields and db_field maps attrs['_fields'] = doc_fields attrs['_db_field_map'] = dict([(k, getattr(v, 'db_field', k)) for k, v in doc_fields.iteritems()]) attrs['_reverse_db_field_map'] = dict( (v, k) for k, v in attrs['_db_field_map'].iteritems()) attrs['_fields_ordered'] = tuple(i[1] for i in sorted( (v.creation_counter, v.name) for v in doc_fields.itervalues())) # # Set document hierarchy # superclasses = () class_name = [name] for base in flattened_bases: if (not getattr(base, '_is_base_cls', True) and not getattr(base, '_meta', {}).get('abstract', True)): # Collate hierarchy for _cls and _subclasses class_name.append(base.__name__) if hasattr(base, '_meta'): # Warn if allow_inheritance isn't set and prevent # inheritance of classes where inheritance is set to False allow_inheritance = base._meta.get('allow_inheritance', ALLOW_INHERITANCE) if (allow_inheritance is not True and not base._meta.get('abstract')): raise ValueError('Document %s may not be subclassed' % base.__name__) # Get superclasses from last base superclass document_bases = [b for b in flattened_bases if hasattr(b, '_class_name')] if document_bases: superclasses = document_bases[0]._superclasses superclasses += (document_bases[0]._class_name, ) _cls = '.'.join(reversed(class_name)) attrs['_class_name'] = _cls attrs['_superclasses'] = superclasses attrs['_subclasses'] = (_cls, ) attrs['_types'] = attrs['_subclasses'] # TODO depreciate _types # Create the new_class new_class = super_new(cls, name, bases, attrs) # Set _subclasses for base in document_bases: if _cls not in base._subclasses: base._subclasses += (_cls,) base._types = base._subclasses # TODO depreciate _types (Document, EmbeddedDocument, DictField, CachedReferenceField) = cls._import_classes() if issubclass(new_class, Document): new_class._collection = None # Add class to the _document_registry _document_registry[new_class._class_name] = new_class # In Python 2, User-defined methods objects have special read-only # attributes 'im_func' and 'im_self' which contain the function obj # and class instance object respectively. With Python 3 these special # attributes have been replaced by __func__ and __self__. The Blinker # module continues to use im_func and im_self, so the code below # copies __func__ into im_func and __self__ into im_self for # classmethod objects in Document derived classes. if PY3: for key, val in new_class.__dict__.items(): if isinstance(val, classmethod): f = val.__get__(new_class) if hasattr(f, '__func__') and not hasattr(f, 'im_func'): f.__dict__.update({'im_func': getattr(f, '__func__')}) if hasattr(f, '__self__') and not hasattr(f, 'im_self'): f.__dict__.update({'im_self': getattr(f, '__self__')}) # Handle delete rules for field in new_class._fields.itervalues(): f = field if f.owner_document is None: f.owner_document = new_class delete_rule = getattr(f, 'reverse_delete_rule', DO_NOTHING) if isinstance(f, CachedReferenceField): if issubclass(new_class, EmbeddedDocument): raise InvalidDocumentError( "CachedReferenceFields is not allowed in EmbeddedDocuments") if not f.document_type: raise InvalidDocumentError( "Document is not available to sync") if f.auto_sync: f.start_listener() f.document_type._cached_reference_fields.append(f) if isinstance(f, ComplexBaseField) and hasattr(f, 'field'): delete_rule = getattr(f.field, 'reverse_delete_rule', DO_NOTHING) if isinstance(f, DictField) and delete_rule != DO_NOTHING: msg = ("Reverse delete rules are not supported " "for %s (field: %s)" % (field.__class__.__name__, field.name)) raise InvalidDocumentError(msg) f = field.field if delete_rule != DO_NOTHING: if issubclass(new_class, EmbeddedDocument): msg = ("Reverse delete rules are not supported for " "EmbeddedDocuments (field: %s)" % field.name) raise InvalidDocumentError(msg) f.document_type.register_delete_rule(new_class, field.name, delete_rule) if (field.name and hasattr(Document, field.name) and EmbeddedDocument not in new_class.mro()): msg = ("%s is a document method and not a valid " "field name" % field.name) raise InvalidDocumentError(msg) return new_class def add_to_class(self, name, value): setattr(self, name, value) @classmethod def _get_bases(cls, bases): if isinstance(bases, BasesTuple): return bases seen = [] bases = cls.__get_bases(bases) unique_bases = (b for b in bases if not (b in seen or seen.append(b))) return BasesTuple(unique_bases) @classmethod def __get_bases(cls, bases): for base in bases: if base is object: continue yield base for child_base in cls.__get_bases(base.__bases__): yield child_base @classmethod def _import_classes(cls): Document = _import_class('Document') EmbeddedDocument = _import_class('EmbeddedDocument') DictField = _import_class('DictField') CachedReferenceField = _import_class('CachedReferenceField') return Document, EmbeddedDocument, DictField, CachedReferenceField class TopLevelDocumentMetaclass(DocumentMetaclass): """Metaclass for top-level documents (i.e. documents that have their own collection in the database. """ def __new__(cls, name, bases, attrs): flattened_bases = cls._get_bases(bases) super_new = super(TopLevelDocumentMetaclass, cls).__new__ # Set default _meta data if base class, otherwise get user defined meta if attrs.get('my_metaclass') == TopLevelDocumentMetaclass: # defaults attrs['_meta'] = { 'abstract': True, 'max_documents': None, 'max_size': None, 'ordering': [], # default ordering applied at runtime 'indexes': [], # indexes to be ensured at runtime 'id_field': None, 'index_background': False, 'index_drop_dups': False, 'index_opts': None, 'delete_rules': None, 'allow_inheritance': None, } attrs['_is_base_cls'] = True attrs['_meta'].update(attrs.get('meta', {})) else: attrs['_meta'] = attrs.get('meta', {}) # Explicitly set abstract to false unless set attrs['_meta']['abstract'] = attrs['_meta'].get('abstract', False) attrs['_is_base_cls'] = False # Set flag marking as document class - as opposed to an object mixin attrs['_is_document'] = True # Ensure queryset_class is inherited if 'objects' in attrs: manager = attrs['objects'] if hasattr(manager, 'queryset_class'): attrs['_meta']['queryset_class'] = manager.queryset_class # Clean up top level meta if 'meta' in attrs: del attrs['meta'] # Find the parent document class parent_doc_cls = [b for b in flattened_bases if b.__class__ == TopLevelDocumentMetaclass] parent_doc_cls = None if not parent_doc_cls else parent_doc_cls[0] # Prevent classes setting collection different to their parents # If parent wasn't an abstract class if (parent_doc_cls and 'collection' in attrs.get('_meta', {}) and not parent_doc_cls._meta.get('abstract', True)): msg = "Trying to set a collection on a subclass (%s)" % name warnings.warn(msg, SyntaxWarning) del attrs['_meta']['collection'] # Ensure abstract documents have abstract bases if attrs.get('_is_base_cls') or attrs['_meta'].get('abstract'): if (parent_doc_cls and not parent_doc_cls._meta.get('abstract', False)): msg = "Abstract document cannot have non-abstract base" raise ValueError(msg) return super_new(cls, name, bases, attrs) # Merge base class metas. # Uses a special MetaDict that handles various merging rules meta = MetaDict() for base in flattened_bases[::-1]: # Add any mixin metadata from plain objects if hasattr(base, 'meta'): meta.merge(base.meta) elif hasattr(base, '_meta'): meta.merge(base._meta) # Set collection in the meta if its callable if (getattr(base, '_is_document', False) and not base._meta.get('abstract')): collection = meta.get('collection', None) if callable(collection): meta['collection'] = collection(base) meta.merge(attrs.get('_meta', {})) # Top level meta # Only simple classes (direct subclasses of Document) # may set allow_inheritance to False simple_class = all([b._meta.get('abstract') for b in flattened_bases if hasattr(b, '_meta')]) if (not simple_class and meta['allow_inheritance'] is False and not meta['abstract']): raise ValueError('Only direct subclasses of Document may set ' '"allow_inheritance" to False') # Set default collection name if 'collection' not in meta: meta['collection'] = ''.join('_%s' % c if c.isupper() else c for c in name).strip('_').lower() attrs['_meta'] = meta # Call super and get the new class new_class = super_new(cls, name, bases, attrs) meta = new_class._meta # Set index specifications meta['index_specs'] = new_class._build_index_specs(meta['indexes']) # If collection is a callable - call it and set the value collection = meta.get('collection') if callable(collection): new_class._meta['collection'] = collection(new_class) # Provide a default queryset unless exists or one has been set if 'objects' not in dir(new_class): new_class.objects = QuerySetManager() # Validate the fields and set primary key if needed for field_name, field in new_class._fields.iteritems(): if field.primary_key: # Ensure only one primary key is set current_pk = new_class._meta.get('id_field') if current_pk and current_pk != field_name: raise ValueError('Cannot override primary key field') # Set primary key if not current_pk: new_class._meta['id_field'] = field_name new_class.id = field # Set primary key if not defined by the document new_class._auto_id_field = getattr(parent_doc_cls, '_auto_id_field', False) if not new_class._meta.get('id_field'): # After 0.10, find not existing names, instead of overwriting id_name, id_db_name = cls.get_auto_id_names(new_class) new_class._auto_id_field = True new_class._meta['id_field'] = id_name new_class._fields[id_name] = ObjectIdField(db_field=id_db_name) new_class._fields[id_name].name = id_name new_class.id = new_class._fields[id_name] new_class._db_field_map[id_name] = id_db_name new_class._reverse_db_field_map[id_db_name] = id_name # Prepend id field to _fields_ordered new_class._fields_ordered = (id_name, ) + new_class._fields_ordered # Merge in exceptions with parent hierarchy exceptions_to_merge = (DoesNotExist, MultipleObjectsReturned) module = attrs.get('__module__') for exc in exceptions_to_merge: name = exc.__name__ parents = tuple(getattr(base, name) for base in flattened_bases if hasattr(base, name)) or (exc,) # Create new exception and set to new_class exception = type(name, parents, {'__module__': module}) setattr(new_class, name, exception) return new_class @classmethod def get_auto_id_names(cls, new_class): id_name, id_db_name = ('id', '_id') if id_name not in new_class._fields and \ id_db_name not in (v.db_field for v in new_class._fields.values()): return id_name, id_db_name id_basename, id_db_basename, i = 'auto_id', '_auto_id', 0 while id_name in new_class._fields or \ id_db_name in (v.db_field for v in new_class._fields.values()): id_name = '{0}_{1}'.format(id_basename, i) id_db_name = '{0}_{1}'.format(id_db_basename, i) i += 1 return id_name, id_db_name class MetaDict(dict): """Custom dictionary for meta classes. Handles the merging of set indexes """ _merge_options = ('indexes',) def merge(self, new_options): for k, v in new_options.iteritems(): if k in self._merge_options: self[k] = self.get(k, []) + v else: self[k] = v class BasesTuple(tuple): """Special class to handle introspection of bases tuple in __new__""" pass mongoengine-0.10.6/mongoengine/base/__init__.py0000664000175000017500000000042312651363712021044 0ustar travistravisfrom mongoengine.base.common import * from mongoengine.base.datastructures import * from mongoengine.base.document import * from mongoengine.base.fields import * from mongoengine.base.metaclasses import * # Help with backwards compatibility from mongoengine.errors import * mongoengine-0.10.6/mongoengine/base/document.py0000664000175000017500000011556212651363712021136 0ustar travistravisimport copy import operator import numbers from collections import Hashable from functools import partial import pymongo from bson import json_util, ObjectId from bson.dbref import DBRef from bson.son import SON from mongoengine import signals from mongoengine.common import _import_class from mongoengine.errors import (ValidationError, InvalidDocumentError, LookUpError, FieldDoesNotExist) from mongoengine.python_support import PY3, txt_type from mongoengine.base.common import get_document, ALLOW_INHERITANCE from mongoengine.base.datastructures import ( BaseDict, BaseList, EmbeddedDocumentList, StrictDict, SemiStrictDict ) from mongoengine.base.fields import ComplexBaseField __all__ = ('BaseDocument', 'NON_FIELD_ERRORS') NON_FIELD_ERRORS = '__all__' class BaseDocument(object): __slots__ = ('_changed_fields', '_initialised', '_created', '_data', '_dynamic_fields', '_auto_id_field', '_db_field_map', '__weakref__') _dynamic = False _dynamic_lock = True STRICT = False def __init__(self, *args, **values): """ Initialise a document or embedded document :param __auto_convert: Try and will cast python objects to Object types :param values: A dictionary of values for the document """ self._initialised = False self._created = True if args: # Combine positional arguments with named arguments. # We only want named arguments. field = iter(self._fields_ordered) # If its an automatic id field then skip to the first defined field if self._auto_id_field: next(field) for value in args: name = next(field) if name in values: raise TypeError( "Multiple values for keyword argument '" + name + "'") values[name] = value __auto_convert = values.pop("__auto_convert", True) # 399: set default values only to fields loaded from DB __only_fields = set(values.pop("__only_fields", values)) _created = values.pop("_created", True) signals.pre_init.send(self.__class__, document=self, values=values) # Check if there are undefined fields supplied to the constructor, # if so raise an Exception. if not self._dynamic and (self._meta.get('strict', True) or _created): for var in values.keys(): if var not in self._fields.keys() + ['id', 'pk', '_cls', '_text_score']: msg = ( "The field '{0}' does not exist on the document '{1}'" ).format(var, self._class_name) raise FieldDoesNotExist(msg) if self.STRICT and not self._dynamic: self._data = StrictDict.create(allowed_keys=self._fields_ordered)() else: self._data = SemiStrictDict.create( allowed_keys=self._fields_ordered)() self._dynamic_fields = SON() # Assign default values to instance for key, field in self._fields.iteritems(): if self._db_field_map.get(key, key) in __only_fields: continue value = getattr(self, key, None) setattr(self, key, value) if "_cls" not in values: self._cls = self._class_name # Set passed values after initialisation if self._dynamic: dynamic_data = {} for key, value in values.iteritems(): if key in self._fields or key == '_id': setattr(self, key, value) elif self._dynamic: dynamic_data[key] = value else: FileField = _import_class('FileField') for key, value in values.iteritems(): if key == '__auto_convert': continue key = self._reverse_db_field_map.get(key, key) if key in self._fields or key in ('id', 'pk', '_cls'): if __auto_convert and value is not None: field = self._fields.get(key) if field and not isinstance(field, FileField): value = field.to_python(value) setattr(self, key, value) else: self._data[key] = value # Set any get_fieldname_display methods self.__set_field_display() if self._dynamic: self._dynamic_lock = False for key, value in dynamic_data.iteritems(): setattr(self, key, value) # Flag initialised self._initialised = True self._created = _created signals.post_init.send(self.__class__, document=self) def __delattr__(self, *args, **kwargs): """Handle deletions of fields""" field_name = args[0] if field_name in self._fields: default = self._fields[field_name].default if callable(default): default = default() setattr(self, field_name, default) else: super(BaseDocument, self).__delattr__(*args, **kwargs) def __setattr__(self, name, value): # Handle dynamic data only if an initialised dynamic document if self._dynamic and not self._dynamic_lock: if not hasattr(self, name) and not name.startswith('_'): DynamicField = _import_class("DynamicField") field = DynamicField(db_field=name) field.name = name self._dynamic_fields[name] = field self._fields_ordered += (name,) if not name.startswith('_'): value = self.__expand_dynamic_values(name, value) # Handle marking data as changed if name in self._dynamic_fields: self._data[name] = value if hasattr(self, '_changed_fields'): self._mark_as_changed(name) try: self__created = self._created except AttributeError: self__created = True if (self._is_document and not self__created and name in self._meta.get('shard_key', tuple()) and self._data.get(name) != value): OperationError = _import_class('OperationError') msg = "Shard Keys are immutable. Tried to update %s" % name raise OperationError(msg) try: self__initialised = self._initialised except AttributeError: self__initialised = False # Check if the user has created a new instance of a class if (self._is_document and self__initialised and self__created and name == self._meta.get('id_field')): super(BaseDocument, self).__setattr__('_created', False) super(BaseDocument, self).__setattr__(name, value) def __getstate__(self): data = {} for k in ('_changed_fields', '_initialised', '_created', '_dynamic_fields', '_fields_ordered'): if hasattr(self, k): data[k] = getattr(self, k) data['_data'] = self.to_mongo() return data def __setstate__(self, data): if isinstance(data["_data"], SON): data["_data"] = self.__class__._from_son(data["_data"])._data for k in ('_changed_fields', '_initialised', '_created', '_data', '_dynamic_fields'): if k in data: setattr(self, k, data[k]) if '_fields_ordered' in data: if self._dynamic: setattr(self, '_fields_ordered', data['_fields_ordered']) else: _super_fields_ordered = type(self)._fields_ordered setattr(self, '_fields_ordered', _super_fields_ordered) dynamic_fields = data.get('_dynamic_fields') or SON() for k in dynamic_fields.keys(): setattr(self, k, data["_data"].get(k)) def __iter__(self): return iter(self._fields_ordered) def __getitem__(self, name): """Dictionary-style field access, return a field's value if present. """ try: if name in self._fields_ordered: return getattr(self, name) except AttributeError: pass raise KeyError(name) def __setitem__(self, name, value): """Dictionary-style field access, set a field's value. """ # Ensure that the field exists before settings its value if not self._dynamic and name not in self._fields: raise KeyError(name) return setattr(self, name, value) def __contains__(self, name): try: val = getattr(self, name) return val is not None except AttributeError: return False def __len__(self): return len(self._data) def __repr__(self): try: u = self.__str__() except (UnicodeEncodeError, UnicodeDecodeError): u = '[Bad Unicode data]' repr_type = str if u is None else type(u) return repr_type('<%s: %s>' % (self.__class__.__name__, u)) def __str__(self): if hasattr(self, '__unicode__'): if PY3: return self.__unicode__() else: return unicode(self).encode('utf-8') return txt_type('%s object' % self.__class__.__name__) def __eq__(self, other): if isinstance(other, self.__class__) and hasattr(other, 'id') and other.id is not None: return self.id == other.id if isinstance(other, DBRef): return self._get_collection_name() == other.collection and self.id == other.id if self.id is None: return self is other return False def __ne__(self, other): return not self.__eq__(other) def __hash__(self): if getattr(self, 'pk', None) is None: # For new object return super(BaseDocument, self).__hash__() else: return hash(self.pk) def clean(self): """ Hook for doing document level data cleaning before validation is run. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS. """ pass def get_text_score(self): """ Get text score from text query """ if '_text_score' not in self._data: raise InvalidDocumentError('This document is not originally built from a text query') return self._data['_text_score'] def to_mongo(self, use_db_field=True, fields=None): """ Return as SON data ready for use with MongoDB. """ if not fields: fields = [] data = SON() data["_id"] = None data['_cls'] = self._class_name EmbeddedDocumentField = _import_class("EmbeddedDocumentField") # only root fields ['test1.a', 'test2'] => ['test1', 'test2'] root_fields = set([f.split('.')[0] for f in fields]) for field_name in self: if root_fields and field_name not in root_fields: continue value = self._data.get(field_name, None) field = self._fields.get(field_name) if field is None and self._dynamic: field = self._dynamic_fields.get(field_name) if value is not None: if isinstance(field, EmbeddedDocumentField): if fields: key = '%s.' % field_name embedded_fields = [ i.replace(key, '') for i in fields if i.startswith(key)] else: embedded_fields = [] value = field.to_mongo(value, use_db_field=use_db_field, fields=embedded_fields) else: value = field.to_mongo(value) # Handle self generating fields if value is None and field._auto_gen: value = field.generate() self._data[field_name] = value if value is not None: if use_db_field: data[field.db_field] = value else: data[field.name] = value # If "_id" has not been set, then try and set it Document = _import_class("Document") if isinstance(self, Document): if data["_id"] is None: data["_id"] = self._data.get("id", None) if data['_id'] is None: data.pop('_id') # Only add _cls if allow_inheritance is True if (not hasattr(self, '_meta') or not self._meta.get('allow_inheritance', ALLOW_INHERITANCE)): data.pop('_cls') return data def validate(self, clean=True): """Ensure that all fields' values are valid and that required fields are present. """ # Ensure that each field is matched to a valid value errors = {} if clean: try: self.clean() except ValidationError, error: errors[NON_FIELD_ERRORS] = error # Get a list of tuples of field names and their current values fields = [(self._fields.get(name, self._dynamic_fields.get(name)), self._data.get(name)) for name in self._fields_ordered] EmbeddedDocumentField = _import_class("EmbeddedDocumentField") GenericEmbeddedDocumentField = _import_class( "GenericEmbeddedDocumentField") for field, value in fields: if value is not None: try: if isinstance(field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): field._validate(value, clean=clean) else: field._validate(value) except ValidationError, error: errors[field.name] = error.errors or error except (ValueError, AttributeError, AssertionError), error: errors[field.name] = error elif field.required and not getattr(field, '_auto_gen', False): errors[field.name] = ValidationError('Field is required', field_name=field.name) if errors: pk = "None" if hasattr(self, 'pk'): pk = self.pk elif self._instance and hasattr(self._instance, 'pk'): pk = self._instance.pk message = "ValidationError (%s:%s) " % (self._class_name, pk) raise ValidationError(message, errors=errors) def to_json(self, *args, **kwargs): """Converts a document to JSON. :param use_db_field: Set to True by default but enables the output of the json structure with the field names and not the mongodb store db_names in case of set to False """ use_db_field = kwargs.pop('use_db_field', True) return json_util.dumps(self.to_mongo(use_db_field), *args, **kwargs) @classmethod def from_json(cls, json_data, created=False): """Converts json data to an unsaved document instance""" return cls._from_son(json_util.loads(json_data), created=created) def __expand_dynamic_values(self, name, value): """expand any dynamic values to their correct types / values""" if not isinstance(value, (dict, list, tuple)): return value EmbeddedDocumentListField = _import_class('EmbeddedDocumentListField') is_list = False if not hasattr(value, 'items'): is_list = True value = dict([(k, v) for k, v in enumerate(value)]) if not is_list and '_cls' in value: cls = get_document(value['_cls']) return cls(**value) data = {} for k, v in value.items(): key = name if is_list else k data[k] = self.__expand_dynamic_values(key, v) if is_list: # Convert back to a list data_items = sorted(data.items(), key=operator.itemgetter(0)) value = [v for k, v in data_items] else: value = data # Convert lists / values so we can watch for any changes on them if (isinstance(value, (list, tuple)) and not isinstance(value, BaseList)): if issubclass(type(self), EmbeddedDocumentListField): value = EmbeddedDocumentList(value, self, name) else: value = BaseList(value, self, name) elif isinstance(value, dict) and not isinstance(value, BaseDict): value = BaseDict(value, self, name) return value def _mark_as_changed(self, key): """Marks a key as explicitly changed by the user """ if not key: return if not hasattr(self, '_changed_fields'): return if '.' in key: key, rest = key.split('.', 1) key = self._db_field_map.get(key, key) key = '%s.%s' % (key, rest) else: key = self._db_field_map.get(key, key) if key not in self._changed_fields: levels, idx = key.split('.'), 1 while idx <= len(levels): if '.'.join(levels[:idx]) in self._changed_fields: break idx += 1 else: self._changed_fields.append(key) # remove lower level changed fields level = '.'.join(levels[:idx]) + '.' remove = self._changed_fields.remove for field in self._changed_fields: if field.startswith(level): remove(field) def _clear_changed_fields(self): """Using get_changed_fields iterate and remove any fields that are marked as changed""" for changed in self._get_changed_fields(): parts = changed.split(".") data = self for part in parts: if isinstance(data, list): try: data = data[int(part)] except IndexError: data = None elif isinstance(data, dict): data = data.get(part, None) else: data = getattr(data, part, None) if hasattr(data, "_changed_fields"): if hasattr(data, "_is_document") and data._is_document: continue data._changed_fields = [] self._changed_fields = [] def _nestable_types_changed_fields(self, changed_fields, key, data, inspected): # Loop list / dict fields as they contain documents # Determine the iterator to use if not hasattr(data, 'items'): iterator = enumerate(data) else: iterator = data.iteritems() for index, value in iterator: list_key = "%s%s." % (key, index) # don't check anything lower if this key is already marked # as changed. if list_key[:-1] in changed_fields: continue if hasattr(value, '_get_changed_fields'): changed = value._get_changed_fields(inspected) changed_fields += ["%s%s" % (list_key, k) for k in changed if k] elif isinstance(value, (list, tuple, dict)): self._nestable_types_changed_fields( changed_fields, list_key, value, inspected) def _get_changed_fields(self, inspected=None): """Returns a list of all fields that have explicitly been changed. """ EmbeddedDocument = _import_class("EmbeddedDocument") DynamicEmbeddedDocument = _import_class("DynamicEmbeddedDocument") ReferenceField = _import_class("ReferenceField") SortedListField = _import_class("SortedListField") changed_fields = [] changed_fields += getattr(self, '_changed_fields', []) inspected = inspected or set() if hasattr(self, 'id') and isinstance(self.id, Hashable): if self.id in inspected: return changed_fields inspected.add(self.id) for field_name in self._fields_ordered: db_field_name = self._db_field_map.get(field_name, field_name) key = '%s.' % db_field_name data = self._data.get(field_name, None) field = self._fields.get(field_name) if hasattr(data, 'id'): if data.id in inspected: continue if isinstance(field, ReferenceField): continue elif (isinstance(data, (EmbeddedDocument, DynamicEmbeddedDocument)) and db_field_name not in changed_fields): # Find all embedded fields that have been changed changed = data._get_changed_fields(inspected) changed_fields += ["%s%s" % (key, k) for k in changed if k] elif (isinstance(data, (list, tuple, dict)) and db_field_name not in changed_fields): if (hasattr(field, 'field') and isinstance(field.field, ReferenceField)): continue elif isinstance(field, SortedListField) and field._ordering: # if ordering is affected whole list is changed if any(map(lambda d: field._ordering in d._changed_fields, data)): changed_fields.append(db_field_name) continue self._nestable_types_changed_fields( changed_fields, key, data, inspected) return changed_fields def _delta(self): """Returns the delta (set, unset) of the changes for a document. Gets any values that have been explicitly changed. """ # Handles cases where not loaded from_son but has _id doc = self.to_mongo() set_fields = self._get_changed_fields() unset_data = {} parts = [] if hasattr(self, '_changed_fields'): set_data = {} # Fetch each set item from its path for path in set_fields: parts = path.split('.') d = doc new_path = [] for p in parts: if isinstance(d, (ObjectId, DBRef)): break elif isinstance(d, list) and p.isdigit(): try: d = d[int(p)] except IndexError: d = None elif hasattr(d, 'get'): d = d.get(p) new_path.append(p) path = '.'.join(new_path) set_data[path] = d else: set_data = doc if '_id' in set_data: del set_data['_id'] # Determine if any changed items were actually unset. for path, value in set_data.items(): if value or isinstance(value, (numbers.Number, bool)): continue # If we've set a value that ain't the default value don't unset it. default = None if (self._dynamic and len(parts) and parts[0] in self._dynamic_fields): del set_data[path] unset_data[path] = 1 continue elif path in self._fields: default = self._fields[path].default else: # Perform a full lookup for lists / embedded lookups d = self parts = path.split('.') db_field_name = parts.pop() for p in parts: if isinstance(d, list) and p.isdigit(): d = d[int(p)] elif (hasattr(d, '__getattribute__') and not isinstance(d, dict)): real_path = d._reverse_db_field_map.get(p, p) d = getattr(d, real_path) else: d = d.get(p) if hasattr(d, '_fields'): field_name = d._reverse_db_field_map.get(db_field_name, db_field_name) if field_name in d._fields: default = d._fields.get(field_name).default else: default = None if default is not None: if callable(default): default = default() if default != value: continue del set_data[path] unset_data[path] = 1 return set_data, unset_data @classmethod def _get_collection_name(cls): """Returns the collection name for this class. None for abstract class """ return cls._meta.get('collection', None) @classmethod def _from_son(cls, son, _auto_dereference=True, only_fields=None, created=False): """Create an instance of a Document (subclass) from a PyMongo SON. """ if not only_fields: only_fields = [] # get the class name from the document, falling back to the given # class if unavailable class_name = son.get('_cls', cls._class_name) data = dict(("%s" % key, value) for key, value in son.iteritems()) # Return correct subclass for document type if class_name != cls._class_name: cls = get_document(class_name) changed_fields = [] errors_dict = {} fields = cls._fields if not _auto_dereference: fields = copy.copy(fields) for field_name, field in fields.iteritems(): field._auto_dereference = _auto_dereference if field.db_field in data: value = data[field.db_field] try: data[field_name] = (value if value is None else field.to_python(value)) if field_name != field.db_field: del data[field.db_field] except (AttributeError, ValueError), e: errors_dict[field_name] = e elif field.default: default = field.default if callable(default): default = default() if isinstance(default, BaseDocument): changed_fields.append(field_name) elif not only_fields or field_name in only_fields: changed_fields.append(field_name) if errors_dict: errors = "\n".join(["%s - %s" % (k, v) for k, v in errors_dict.items()]) msg = ("Invalid data to create a `%s` instance.\n%s" % (cls._class_name, errors)) raise InvalidDocumentError(msg) if cls.STRICT: data = dict((k, v) for k, v in data.iteritems() if k in cls._fields) obj = cls(__auto_convert=False, _created=created, __only_fields=only_fields, **data) obj._changed_fields = changed_fields if not _auto_dereference: obj._fields = fields return obj @classmethod def _build_index_specs(cls, meta_indexes): """Generate and merge the full index specs """ geo_indices = cls._geo_indices() unique_indices = cls._unique_with_indexes() index_specs = [cls._build_index_spec(spec) for spec in meta_indexes] def merge_index_specs(index_specs, indices): if not indices: return index_specs spec_fields = [v['fields'] for k, v in enumerate(index_specs)] # Merge unique_indexes with existing specs for k, v in enumerate(indices): if v['fields'] in spec_fields: index_specs[spec_fields.index(v['fields'])].update(v) else: index_specs.append(v) return index_specs index_specs = merge_index_specs(index_specs, geo_indices) index_specs = merge_index_specs(index_specs, unique_indices) return index_specs @classmethod def _build_index_spec(cls, spec): """Build a PyMongo index spec from a MongoEngine index spec. """ if isinstance(spec, basestring): spec = {'fields': [spec]} elif isinstance(spec, (list, tuple)): spec = {'fields': list(spec)} elif isinstance(spec, dict): spec = dict(spec) index_list = [] direction = None # Check to see if we need to include _cls allow_inheritance = cls._meta.get('allow_inheritance', ALLOW_INHERITANCE) include_cls = (allow_inheritance and not spec.get('sparse', False) and spec.get('cls', True) and '_cls' not in spec['fields']) # 733: don't include cls if index_cls is False unless there is an explicit cls with the index include_cls = include_cls and (spec.get('cls', False) or cls._meta.get('index_cls', True)) if "cls" in spec: spec.pop('cls') for key in spec['fields']: # If inherited spec continue if isinstance(key, (list, tuple)): continue # ASCENDING from + # DESCENDING from - # TEXT from $ # HASHED from # # GEOSPHERE from ( # GEOHAYSTACK from ) # GEO2D from * direction = pymongo.ASCENDING if key.startswith("-"): direction = pymongo.DESCENDING elif key.startswith("$"): direction = pymongo.TEXT elif key.startswith("#"): direction = pymongo.HASHED elif key.startswith("("): direction = pymongo.GEOSPHERE elif key.startswith(")"): direction = pymongo.GEOHAYSTACK elif key.startswith("*"): direction = pymongo.GEO2D if key.startswith(("+", "-", "*", "$", "#", "(", ")")): key = key[1:] # Use real field name, do it manually because we need field # objects for the next part (list field checking) parts = key.split('.') if parts in (['pk'], ['id'], ['_id']): key = '_id' else: fields = cls._lookup_field(parts) parts = [] for field in fields: try: if field != "_id": field = field.db_field except AttributeError: pass parts.append(field) key = '.'.join(parts) index_list.append((key, direction)) # Don't add cls to a geo index if include_cls and direction not in ( pymongo.GEO2D, pymongo.GEOHAYSTACK, pymongo.GEOSPHERE): index_list.insert(0, ('_cls', 1)) if index_list: spec['fields'] = index_list return spec @classmethod def _unique_with_indexes(cls, namespace=""): """ Find and set unique indexes """ unique_indexes = [] for field_name, field in cls._fields.items(): sparse = field.sparse # Generate a list of indexes needed by uniqueness constraints if field.unique: unique_fields = [field.db_field] # Add any unique_with fields to the back of the index spec if field.unique_with: if isinstance(field.unique_with, basestring): field.unique_with = [field.unique_with] # Convert unique_with field names to real field names unique_with = [] for other_name in field.unique_with: parts = other_name.split('.') # Lookup real name parts = cls._lookup_field(parts) name_parts = [part.db_field for part in parts] unique_with.append('.'.join(name_parts)) # Unique field should be required parts[-1].required = True sparse = (not sparse and parts[-1].name not in cls.__dict__) unique_fields += unique_with # Add the new index to the list fields = [("%s%s" % (namespace, f), pymongo.ASCENDING) for f in unique_fields] index = {'fields': fields, 'unique': True, 'sparse': sparse} unique_indexes.append(index) if field.__class__.__name__ == "ListField": field = field.field # Grab any embedded document field unique indexes if (field.__class__.__name__ == "EmbeddedDocumentField" and field.document_type != cls): field_namespace = "%s." % field_name doc_cls = field.document_type unique_indexes += doc_cls._unique_with_indexes(field_namespace) return unique_indexes @classmethod def _geo_indices(cls, inspected=None, parent_field=None): inspected = inspected or [] geo_indices = [] inspected.append(cls) geo_field_type_names = ["EmbeddedDocumentField", "GeoPointField", "PointField", "LineStringField", "PolygonField"] geo_field_types = tuple([_import_class(field) for field in geo_field_type_names]) for field in cls._fields.values(): if not isinstance(field, geo_field_types): continue if hasattr(field, 'document_type'): field_cls = field.document_type if field_cls in inspected: continue if hasattr(field_cls, '_geo_indices'): geo_indices += field_cls._geo_indices( inspected, parent_field=field.db_field) elif field._geo_index: field_name = field.db_field if parent_field: field_name = "%s.%s" % (parent_field, field_name) geo_indices.append({'fields': [(field_name, field._geo_index)]}) return geo_indices @classmethod def _lookup_field(cls, parts): """Lookup a field based on its attribute and return a list containing the field's parents and the field. """ ListField = _import_class("ListField") DynamicField = _import_class('DynamicField') if not isinstance(parts, (list, tuple)): parts = [parts] fields = [] field = None for field_name in parts: # Handle ListField indexing: if field_name.isdigit() and isinstance(field, ListField): fields.append(field_name) continue if field is None: # Look up first field from the document if field_name == 'pk': # Deal with "primary key" alias field_name = cls._meta['id_field'] if field_name in cls._fields: field = cls._fields[field_name] elif cls._dynamic: field = DynamicField(db_field=field_name) elif cls._meta.get("allow_inheritance", False) or cls._meta.get("abstract", False): # 744: in case the field is defined in a subclass for subcls in cls.__subclasses__(): try: field = subcls._lookup_field([field_name])[0] except LookUpError: continue if field is not None: break else: raise LookUpError('Cannot resolve field "%s"' % field_name) else: raise LookUpError('Cannot resolve field "%s"' % field_name) else: ReferenceField = _import_class('ReferenceField') GenericReferenceField = _import_class('GenericReferenceField') if isinstance(field, (ReferenceField, GenericReferenceField)): raise LookUpError('Cannot perform join in mongoDB: %s' % '__'.join(parts)) if hasattr(getattr(field, 'field', None), 'lookup_member'): new_field = field.field.lookup_member(field_name) elif cls._dynamic and (isinstance(field, DynamicField) or getattr(getattr(field, 'document_type'), '_dynamic')): new_field = DynamicField(db_field=field_name) else: # Look up subfield on the previous field or raise try: new_field = field.lookup_member(field_name) except AttributeError: raise LookUpError('Cannot resolve subfield or operator {} ' 'on the field {}'.format( field_name, field.name)) if not new_field and isinstance(field, ComplexBaseField): fields.append(field_name) continue elif not new_field: raise LookUpError('Cannot resolve field "%s"' % field_name) field = new_field # update field to the new field type fields.append(field) return fields @classmethod def _translate_field_name(cls, field, sep='.'): """Translate a field attribute name to a database field name. """ parts = field.split(sep) parts = [f.db_field for f in cls._lookup_field(parts)] return '.'.join(parts) def __set_field_display(self): """Dynamically set the display value for a field with choices""" for attr_name, field in self._fields.items(): if field.choices: if self._dynamic: obj = self else: obj = type(self) setattr(obj, 'get_%s_display' % attr_name, partial(self.__get_field_display, field=field)) def __get_field_display(self, field): """Returns the display value for a choice field""" value = getattr(self, field.name) if field.choices and isinstance(field.choices[0], (list, tuple)): return dict(field.choices).get(value, value) return value mongoengine-0.10.6/mongoengine/base/common.py0000664000175000017500000000155412651363712020603 0ustar travistravisfrom mongoengine.errors import NotRegistered __all__ = ('ALLOW_INHERITANCE', 'get_document', '_document_registry') ALLOW_INHERITANCE = False _document_registry = {} def get_document(name): doc = _document_registry.get(name, None) if not doc: # Possible old style name single_end = name.split('.')[-1] compound_end = '.%s' % single_end possible_match = [k for k in _document_registry.keys() if k.endswith(compound_end) or k == single_end] if len(possible_match) == 1: doc = _document_registry.get(possible_match.pop(), None) if not doc: raise NotRegistered(""" `%s` has not been registered in the document registry. Importing the document class automatically registers it, has it been imported? """.strip() % name) return doc mongoengine-0.10.6/mongoengine/__init__.py0000664000175000017500000000112412651363712020131 0ustar travistravisimport document from document import * import fields from fields import * import connection from connection import * import queryset from queryset import * import signals from signals import * from errors import * import errors __all__ = (list(document.__all__) + fields.__all__ + connection.__all__ + list(queryset.__all__) + signals.__all__ + list(errors.__all__)) VERSION = (0, 10, 6) def get_version(): if isinstance(VERSION[-1], basestring): return '.'.join(map(str, VERSION[:-1])) + VERSION[-1] return '.'.join(map(str, VERSION)) __version__ = get_version() mongoengine-0.10.6/mongoengine/dereference.py0000664000175000017500000002530112651363712020644 0ustar travistravisfrom bson import DBRef, SON from base import ( BaseDict, BaseList, EmbeddedDocumentList, TopLevelDocumentMetaclass, get_document ) from fields import (ReferenceField, ListField, DictField, MapField) from connection import get_db from queryset import QuerySet from document import Document, EmbeddedDocument class DeReference(object): def __call__(self, items, max_depth=1, instance=None, name=None): """ Cheaply dereferences the items to a set depth. Also handles the conversion of complex data types. :param items: The iterable (dict, list, queryset) to be dereferenced. :param max_depth: The maximum depth to recurse to :param instance: The owning instance used for tracking changes by :class:`~mongoengine.base.ComplexBaseField` :param name: The name of the field, used for tracking changes by :class:`~mongoengine.base.ComplexBaseField` :param get: A boolean determining if being called by __get__ """ if items is None or isinstance(items, basestring): return items # cheapest way to convert a queryset to a list # list(queryset) uses a count() query to determine length if isinstance(items, QuerySet): items = [i for i in items] self.max_depth = max_depth doc_type = None if instance and isinstance(instance, (Document, EmbeddedDocument, TopLevelDocumentMetaclass)): doc_type = instance._fields.get(name) while hasattr(doc_type, 'field'): doc_type = doc_type.field if isinstance(doc_type, ReferenceField): field = doc_type doc_type = doc_type.document_type is_list = not hasattr(items, 'items') if is_list and all([i.__class__ == doc_type for i in items]): return items elif not is_list and all( [i.__class__ == doc_type for i in items.values()]): return items elif not field.dbref: if not hasattr(items, 'items'): def _get_items(items): new_items = [] for v in items: if isinstance(v, list): new_items.append(_get_items(v)) elif not isinstance(v, (DBRef, Document)): new_items.append(field.to_python(v)) else: new_items.append(v) return new_items items = _get_items(items) else: items = dict([ (k, field.to_python(v)) if not isinstance(v, (DBRef, Document)) else (k, v) for k, v in items.iteritems()] ) self.reference_map = self._find_references(items) self.object_map = self._fetch_objects(doc_type=doc_type) return self._attach_objects(items, 0, instance, name) def _find_references(self, items, depth=0): """ Recursively finds all db references to be dereferenced :param items: The iterable (dict, list, queryset) :param depth: The current depth of recursion """ reference_map = {} if not items or depth >= self.max_depth: return reference_map # Determine the iterator to use if not hasattr(items, 'items'): iterator = enumerate(items) else: iterator = items.iteritems() # Recursively find dbreferences depth += 1 for k, item in iterator: if isinstance(item, (Document, EmbeddedDocument)): for field_name, field in item._fields.iteritems(): v = item._data.get(field_name, None) if isinstance(v, DBRef): reference_map.setdefault(field.document_type, set()).add(v.id) elif isinstance(v, (dict, SON)) and '_ref' in v: reference_map.setdefault(get_document(v['_cls']), set()).add(v['_ref'].id) elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth: field_cls = getattr(getattr(field, 'field', None), 'document_type', None) references = self._find_references(v, depth) for key, refs in references.iteritems(): if isinstance(field_cls, (Document, TopLevelDocumentMetaclass)): key = field_cls reference_map.setdefault(key, set()).update(refs) elif isinstance(item, DBRef): reference_map.setdefault(item.collection, set()).add(item.id) elif isinstance(item, (dict, SON)) and '_ref' in item: reference_map.setdefault(get_document(item['_cls']), set()).add(item['_ref'].id) elif isinstance(item, (dict, list, tuple)) and depth - 1 <= self.max_depth: references = self._find_references(item, depth - 1) for key, refs in references.iteritems(): reference_map.setdefault(key, set()).update(refs) return reference_map def _fetch_objects(self, doc_type=None): """Fetch all references and convert to their document objects """ object_map = {} for collection, dbrefs in self.reference_map.iteritems(): if hasattr(collection, 'objects'): # We have a document class for the refs col_name = collection._get_collection_name() refs = [dbref for dbref in dbrefs if (col_name, dbref) not in object_map] references = collection.objects.in_bulk(refs) for key, doc in references.iteritems(): object_map[(col_name, key)] = doc else: # Generic reference: use the refs data to convert to document if isinstance(doc_type, (ListField, DictField, MapField,)): continue refs = [dbref for dbref in dbrefs if (collection, dbref) not in object_map] if doc_type: references = doc_type._get_db()[collection].find({'_id': {'$in': refs}}) for ref in references: doc = doc_type._from_son(ref) object_map[(collection, doc.id)] = doc else: references = get_db()[collection].find({'_id': {'$in': refs}}) for ref in references: if '_cls' in ref: doc = get_document(ref["_cls"])._from_son(ref) elif doc_type is None: doc = get_document( ''.join(x.capitalize() for x in collection.split('_')))._from_son(ref) else: doc = doc_type._from_son(ref) object_map[(collection, doc.id)] = doc return object_map def _attach_objects(self, items, depth=0, instance=None, name=None): """ Recursively finds all db references to be dereferenced :param items: The iterable (dict, list, queryset) :param depth: The current depth of recursion :param instance: The owning instance used for tracking changes by :class:`~mongoengine.base.ComplexBaseField` :param name: The name of the field, used for tracking changes by :class:`~mongoengine.base.ComplexBaseField` """ if not items: if isinstance(items, (BaseDict, BaseList)): return items if instance: if isinstance(items, dict): return BaseDict(items, instance, name) else: return BaseList(items, instance, name) if isinstance(items, (dict, SON)): if '_ref' in items: return self.object_map.get( (items['_ref'].collection, items['_ref'].id), items) elif '_cls' in items: doc = get_document(items['_cls'])._from_son(items) _cls = doc._data.pop('_cls', None) del items['_cls'] doc._data = self._attach_objects(doc._data, depth, doc, None) if _cls is not None: doc._data['_cls'] = _cls return doc if not hasattr(items, 'items'): is_list = True list_type = BaseList if isinstance(items, EmbeddedDocumentList): list_type = EmbeddedDocumentList as_tuple = isinstance(items, tuple) iterator = enumerate(items) data = [] else: is_list = False iterator = items.iteritems() data = {} depth += 1 for k, v in iterator: if is_list: data.append(v) else: data[k] = v if k in self.object_map and not is_list: data[k] = self.object_map[k] elif isinstance(v, (Document, EmbeddedDocument)): for field_name, field in v._fields.iteritems(): v = data[k]._data.get(field_name, None) if isinstance(v, DBRef): data[k]._data[field_name] = self.object_map.get( (v.collection, v.id), v) elif isinstance(v, (dict, SON)) and '_ref' in v: data[k]._data[field_name] = self.object_map.get( (v['_ref'].collection, v['_ref'].id), v) elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth: item_name = "{0}.{1}.{2}".format(name, k, field_name) data[k]._data[field_name] = self._attach_objects(v, depth, instance=instance, name=item_name) elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth: item_name = '%s.%s' % (name, k) if name else name data[k] = self._attach_objects(v, depth - 1, instance=instance, name=item_name) elif hasattr(v, 'id'): data[k] = self.object_map.get((v.collection, v.id), v) if instance and name: if is_list: return tuple(data) if as_tuple else list_type(data, instance, name) return BaseDict(data, instance, name) depth += 1 return data mongoengine-0.10.6/mongoengine/signals.py0000664000175000017500000000337612651363712020045 0ustar travistravis# -*- coding: utf-8 -*- __all__ = ['pre_init', 'post_init', 'pre_save', 'pre_save_post_validation', 'post_save', 'pre_delete', 'post_delete'] signals_available = False try: from blinker import Namespace signals_available = True except ImportError: class Namespace(object): def signal(self, name, doc=None): return _FakeSignal(name, doc) class _FakeSignal(object): """If blinker is unavailable, create a fake class with the same interface that allows sending of signals but will fail with an error on anything else. Instead of doing anything on send, it will just ignore the arguments and do nothing instead. """ def __init__(self, name, doc=None): self.name = name self.__doc__ = doc def _fail(self, *args, **kwargs): raise RuntimeError('signalling support is unavailable ' 'because the blinker library is ' 'not installed.') send = lambda *a, **kw: None connect = disconnect = has_receivers_for = receivers_for = \ temporarily_connected_to = _fail del _fail # the namespace for code signals. If you are not mongoengine code, do # not put signals in here. Create your own namespace instead. _signals = Namespace() pre_init = _signals.signal('pre_init') post_init = _signals.signal('post_init') pre_save = _signals.signal('pre_save') pre_save_post_validation = _signals.signal('pre_save_post_validation') post_save = _signals.signal('post_save') pre_delete = _signals.signal('pre_delete') post_delete = _signals.signal('post_delete') pre_bulk_insert = _signals.signal('pre_bulk_insert') post_bulk_insert = _signals.signal('post_bulk_insert') mongoengine-0.10.6/mongoengine/connection.py0000664000175000017500000001634612651363712020545 0ustar travistravisfrom pymongo import MongoClient, ReadPreference, uri_parser from mongoengine.python_support import IS_PYMONGO_3 __all__ = ['ConnectionError', 'connect', 'register_connection', 'DEFAULT_CONNECTION_NAME'] DEFAULT_CONNECTION_NAME = 'default' if IS_PYMONGO_3: READ_PREFERENCE = ReadPreference.PRIMARY else: from pymongo import MongoReplicaSetClient READ_PREFERENCE = False class ConnectionError(Exception): pass _connection_settings = {} _connections = {} _dbs = {} def register_connection(alias, name=None, host=None, port=None, read_preference=READ_PREFERENCE, username=None, password=None, authentication_source=None, **kwargs): """Add a connection. :param alias: the name that will be used to refer to this connection throughout MongoEngine :param name: the name of the specific database to use :param host: the host name of the :program:`mongod` instance to connect to :param port: the port that the :program:`mongod` instance is running on :param read_preference: The read preference for the collection ** Added pymongo 2.1 :param username: username to authenticate with :param password: password to authenticate with :param authentication_source: database to authenticate against :param is_mock: explicitly use mongomock for this connection (can also be done by using `mongomock://` as db host prefix) :param kwargs: allow ad-hoc parameters to be passed into the pymongo driver .. versionchanged:: 0.10.6 - added mongomock support """ global _connection_settings conn_settings = { 'name': name or 'test', 'host': host or 'localhost', 'port': port or 27017, 'read_preference': read_preference, 'username': username, 'password': password, 'authentication_source': authentication_source } # Handle uri style connections conn_host = conn_settings['host'] if conn_host.startswith('mongomock://'): conn_settings['is_mock'] = True # `mongomock://` is not a valid url prefix and must be replaced by `mongodb://` conn_settings['host'] = conn_host.replace('mongomock://', 'mongodb://', 1) elif '://' in conn_host: uri_dict = uri_parser.parse_uri(conn_host) conn_settings.update({ 'name': uri_dict.get('database') or name, 'username': uri_dict.get('username'), 'password': uri_dict.get('password'), 'read_preference': read_preference, }) uri_options = uri_dict['options'] if 'replicaset' in uri_options: conn_settings['replicaSet'] = True if 'authsource' in uri_options: conn_settings['authentication_source'] = uri_options['authsource'] # Deprecated parameters that should not be passed on kwargs.pop('slaves', None) kwargs.pop('is_slave', None) conn_settings.update(kwargs) _connection_settings[alias] = conn_settings def disconnect(alias=DEFAULT_CONNECTION_NAME): global _connections global _dbs if alias in _connections: get_connection(alias=alias).close() del _connections[alias] if alias in _dbs: del _dbs[alias] def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False): global _connections # Connect to the database if not already connected if reconnect: disconnect(alias) if alias not in _connections: if alias not in _connection_settings: msg = 'Connection with alias "%s" has not been defined' % alias if alias == DEFAULT_CONNECTION_NAME: msg = 'You have not defined a default connection' raise ConnectionError(msg) conn_settings = _connection_settings[alias].copy() conn_settings.pop('name', None) conn_settings.pop('username', None) conn_settings.pop('password', None) conn_settings.pop('authentication_source', None) is_mock = conn_settings.pop('is_mock', None) if is_mock: # Use MongoClient from mongomock try: import mongomock except ImportError: raise RuntimeError('You need mongomock installed ' 'to mock MongoEngine.') connection_class = mongomock.MongoClient else: # Use MongoClient from pymongo connection_class = MongoClient if 'replicaSet' in conn_settings: # Discard port since it can't be used on MongoReplicaSetClient conn_settings.pop('port', None) # Discard replicaSet if not base string if not isinstance(conn_settings['replicaSet'], basestring): conn_settings.pop('replicaSet', None) if not IS_PYMONGO_3: connection_class = MongoReplicaSetClient conn_settings['hosts_or_uri'] = conn_settings.pop('host', None) try: connection = None # check for shared connections connection_settings_iterator = ( (db_alias, settings.copy()) for db_alias, settings in _connection_settings.iteritems()) for db_alias, connection_settings in connection_settings_iterator: connection_settings.pop('name', None) connection_settings.pop('username', None) connection_settings.pop('password', None) connection_settings.pop('authentication_source', None) if conn_settings == connection_settings and _connections.get(db_alias, None): connection = _connections[db_alias] break _connections[alias] = connection if connection else connection_class(**conn_settings) except Exception, e: raise ConnectionError("Cannot connect to database %s :\n%s" % (alias, e)) return _connections[alias] def get_db(alias=DEFAULT_CONNECTION_NAME, reconnect=False): global _dbs if reconnect: disconnect(alias) if alias not in _dbs: conn = get_connection(alias) conn_settings = _connection_settings[alias] db = conn[conn_settings['name']] # Authenticate if necessary if conn_settings['username'] and conn_settings['password']: db.authenticate(conn_settings['username'], conn_settings['password'], source=conn_settings['authentication_source']) _dbs[alias] = db return _dbs[alias] def connect(db=None, alias=DEFAULT_CONNECTION_NAME, **kwargs): """Connect to the database specified by the 'db' argument. Connection settings may be provided here as well if the database is not running on the default port on localhost. If authentication is needed, provide username and password arguments as well. Multiple databases are supported by using aliases. Provide a separate `alias` to connect to a different instance of :program:`mongod`. .. versionchanged:: 0.6 - added multiple database support. """ global _connections if alias not in _connections: register_connection(alias, db, **kwargs) return get_connection(alias) # Support old naming convention _get_connection = get_connection _get_db = get_db mongoengine-0.10.6/mongoengine/document.py0000664000175000017500000011666012651363712020224 0ustar travistravisimport warnings import pymongo import re from pymongo.read_preferences import ReadPreference from bson.dbref import DBRef from mongoengine import signals from mongoengine.common import _import_class from mongoengine.base import ( DocumentMetaclass, TopLevelDocumentMetaclass, BaseDocument, BaseDict, BaseList, EmbeddedDocumentList, ALLOW_INHERITANCE, get_document ) from mongoengine.errors import (InvalidQueryError, InvalidDocumentError, SaveConditionError) from mongoengine.python_support import IS_PYMONGO_3 from mongoengine.queryset import (OperationError, NotUniqueError, QuerySet, transform) from mongoengine.connection import get_db, DEFAULT_CONNECTION_NAME from mongoengine.context_managers import switch_db, switch_collection __all__ = ('Document', 'EmbeddedDocument', 'DynamicDocument', 'DynamicEmbeddedDocument', 'OperationError', 'InvalidCollectionError', 'NotUniqueError', 'MapReduceDocument') def includes_cls(fields): """ Helper function used for ensuring and comparing indexes """ first_field = None if len(fields): if isinstance(fields[0], basestring): first_field = fields[0] elif isinstance(fields[0], (list, tuple)) and len(fields[0]): first_field = fields[0][0] return first_field == '_cls' class InvalidCollectionError(Exception): pass class EmbeddedDocument(BaseDocument): """A :class:`~mongoengine.Document` that isn't stored in its own collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as fields on :class:`~mongoengine.Document`\ s through the :class:`~mongoengine.EmbeddedDocumentField` field type. A :class:`~mongoengine.EmbeddedDocument` subclass may be itself subclassed, to create a specialised version of the embedded document that will be stored in the same collection. To facilitate this behaviour a `_cls` field is added to documents (hidden though the MongoEngine interface). To disable this behaviour and remove the dependence on the presence of `_cls` set :attr:`allow_inheritance` to ``False`` in the :attr:`meta` dictionary. """ __slots__ = ('_instance', ) # The __metaclass__ attribute is removed by 2to3 when running with Python3 # my_metaclass is defined so that metaclass can be queried in Python 2 & 3 my_metaclass = DocumentMetaclass __metaclass__ = DocumentMetaclass def __init__(self, *args, **kwargs): super(EmbeddedDocument, self).__init__(*args, **kwargs) self._instance = None self._changed_fields = [] def __eq__(self, other): if isinstance(other, self.__class__): return self._data == other._data return False def __ne__(self, other): return not self.__eq__(other) def save(self, *args, **kwargs): self._instance.save(*args, **kwargs) def reload(self, *args, **kwargs): self._instance.reload(*args, **kwargs) class Document(BaseDocument): """The base class used for defining the structure and properties of collections of documents stored in MongoDB. Inherit from this class, and add fields as class attributes to define a document's structure. Individual documents may then be created by making instances of the :class:`~mongoengine.Document` subclass. By default, the MongoDB collection used to store documents created using a :class:`~mongoengine.Document` subclass will be the name of the subclass converted to lowercase. A different collection may be specified by providing :attr:`collection` to the :attr:`meta` dictionary in the class definition. A :class:`~mongoengine.Document` subclass may be itself subclassed, to create a specialised version of the document that will be stored in the same collection. To facilitate this behaviour a `_cls` field is added to documents (hidden though the MongoEngine interface). To disable this behaviour and remove the dependence on the presence of `_cls` set :attr:`allow_inheritance` to ``False`` in the :attr:`meta` dictionary. A :class:`~mongoengine.Document` may use a **Capped Collection** by specifying :attr:`max_documents` and :attr:`max_size` in the :attr:`meta` dictionary. :attr:`max_documents` is the maximum number of documents that is allowed to be stored in the collection, and :attr:`max_size` is the maximum size of the collection in bytes. :attr:`max_size` is rounded up to the next multiple of 256 by MongoDB internally and mongoengine before. Use also a multiple of 256 to avoid confusions. If :attr:`max_size` is not specified and :attr:`max_documents` is, :attr:`max_size` defaults to 10485760 bytes (10MB). Indexes may be created by specifying :attr:`indexes` in the :attr:`meta` dictionary. The value should be a list of field names or tuples of field names. Index direction may be specified by prefixing the field names with a **+** or **-** sign. Automatic index creation can be disabled by specifying :attr:`auto_create_index` in the :attr:`meta` dictionary. If this is set to False then indexes will not be created by MongoEngine. This is useful in production systems where index creation is performed as part of a deployment system. By default, _cls will be added to the start of every index (that doesn't contain a list) if allow_inheritance is True. This can be disabled by either setting cls to False on the specific index or by setting index_cls to False on the meta dictionary for the document. By default, any extra attribute existing in stored data but not declared in your model will raise a :class:`~mongoengine.FieldDoesNotExist` error. This can be disabled by setting :attr:`strict` to ``False`` in the :attr:`meta` dictionary. """ # The __metaclass__ attribute is removed by 2to3 when running with Python3 # my_metaclass is defined so that metaclass can be queried in Python 2 & 3 my_metaclass = TopLevelDocumentMetaclass __metaclass__ = TopLevelDocumentMetaclass __slots__ = ('__objects',) def pk(): """Primary key alias """ def fget(self): if 'id_field' not in self._meta: return None return getattr(self, self._meta['id_field']) def fset(self, value): return setattr(self, self._meta['id_field'], value) return property(fget, fset) pk = pk() @classmethod def _get_db(cls): """Some Model using other db_alias""" return get_db(cls._meta.get("db_alias", DEFAULT_CONNECTION_NAME)) @classmethod def _get_collection(cls): """Returns the collection for the document.""" # TODO: use new get_collection() with PyMongo3 ? if not hasattr(cls, '_collection') or cls._collection is None: db = cls._get_db() collection_name = cls._get_collection_name() # Create collection as a capped collection if specified if cls._meta.get('max_size') or cls._meta.get('max_documents'): # Get max document limit and max byte size from meta max_size = cls._meta.get('max_size') or 10 * 2 ** 20 # 10MB default max_documents = cls._meta.get('max_documents') # Round up to next 256 bytes as MongoDB would do it to avoid exception if max_size % 256: max_size = (max_size // 256 + 1) * 256 if collection_name in db.collection_names(): cls._collection = db[collection_name] # The collection already exists, check if its capped # options match the specified capped options options = cls._collection.options() if options.get('max') != max_documents or \ options.get('size') != max_size: msg = (('Cannot create collection "%s" as a capped ' 'collection as it already exists') % cls._collection) raise InvalidCollectionError(msg) else: # Create the collection as a capped collection opts = {'capped': True, 'size': max_size} if max_documents: opts['max'] = max_documents cls._collection = db.create_collection( collection_name, **opts ) else: cls._collection = db[collection_name] if cls._meta.get('auto_create_index', True): cls.ensure_indexes() return cls._collection def modify(self, query={}, **update): """Perform an atomic update of the document in the database and reload the document object using updated version. Returns True if the document has been updated or False if the document in the database doesn't match the query. .. note:: All unsaved changes that have been made to the document are rejected if the method returns True. :param query: the update will be performed only if the document in the database matches the query :param update: Django-style update keyword arguments """ if self.pk is None: raise InvalidDocumentError("The document does not have a primary key.") id_field = self._meta["id_field"] query = query.copy() if isinstance(query, dict) else query.to_query(self) if id_field not in query: query[id_field] = self.pk elif query[id_field] != self.pk: raise InvalidQueryError("Invalid document modify query: it must modify only this document.") updated = self._qs(**query).modify(new=True, **update) if updated is None: return False for field in self._fields_ordered: setattr(self, field, self._reload(field, updated[field])) self._changed_fields = updated._changed_fields self._created = False return True def save(self, force_insert=False, validate=True, clean=True, write_concern=None, cascade=None, cascade_kwargs=None, _refs=None, save_condition=None, **kwargs): """Save the :class:`~mongoengine.Document` to the database. If the document already exists, it will be updated, otherwise it will be created. :param force_insert: only try to create a new document, don't allow updates of existing documents :param validate: validates the document; set to ``False`` to skip. :param clean: call the document clean method, requires `validate` to be True. :param write_concern: Extra keyword arguments are passed down to :meth:`~pymongo.collection.Collection.save` OR :meth:`~pymongo.collection.Collection.insert` which will be used as options for the resultant ``getLastError`` command. For example, ``save(..., write_concern={w: 2, fsync: True}, ...)`` will wait until at least two servers have recorded the write and will force an fsync on the primary server. :param cascade: Sets the flag for cascading saves. You can set a default by setting "cascade" in the document __meta__ :param cascade_kwargs: (optional) kwargs dictionary to be passed throw to cascading saves. Implies ``cascade=True``. :param _refs: A list of processed references used in cascading saves :param save_condition: only perform save if matching record in db satisfies condition(s) (e.g. version number). Raises :class:`OperationError` if the conditions are not satisfied .. versionchanged:: 0.5 In existing documents it only saves changed fields using set / unset. Saves are cascaded and any :class:`~bson.dbref.DBRef` objects that have changes are saved as well. .. versionchanged:: 0.6 Added cascading saves .. versionchanged:: 0.8 Cascade saves are optional and default to False. If you want fine grain control then you can turn off using document meta['cascade'] = True. Also you can pass different kwargs to the cascade save using cascade_kwargs which overwrites the existing kwargs with custom values. .. versionchanged:: 0.8.5 Optional save_condition that only overwrites existing documents if the condition is satisfied in the current db record. .. versionchanged:: 0.10 :class:`OperationError` exception raised if save_condition fails. .. versionchanged:: 0.10.1 :class: save_condition failure now raises a `SaveConditionError` """ signals.pre_save.send(self.__class__, document=self) if validate: self.validate(clean=clean) if write_concern is None: write_concern = {"w": 1} doc = self.to_mongo() created = ('_id' not in doc or self._created or force_insert) signals.pre_save_post_validation.send(self.__class__, document=self, created=created) try: collection = self._get_collection() if self._meta.get('auto_create_index', True): self.ensure_indexes() if created: if force_insert: object_id = collection.insert(doc, **write_concern) else: object_id = collection.save(doc, **write_concern) # In PyMongo 3.0, the save() call calls internally the _update() call # but they forget to return the _id value passed back, therefore getting it back here # Correct behaviour in 2.X and in 3.0.1+ versions if not object_id and pymongo.version_tuple == (3, 0): pk_as_mongo_obj = self._fields.get(self._meta['id_field']).to_mongo(self.pk) object_id = self._qs.filter(pk=pk_as_mongo_obj).first() and \ self._qs.filter(pk=pk_as_mongo_obj).first().pk else: object_id = doc['_id'] updates, removals = self._delta() # Need to add shard key to query, or you get an error if save_condition is not None: select_dict = transform.query(self.__class__, **save_condition) else: select_dict = {} select_dict['_id'] = object_id shard_key = self.__class__._meta.get('shard_key', tuple()) for k in shard_key: path = self._lookup_field(k.split('.')) actual_key = [p.db_field for p in path] val = doc for ak in actual_key: val = val[ak] select_dict['.'.join(actual_key)] = val def is_new_object(last_error): if last_error is not None: updated = last_error.get("updatedExisting") if updated is not None: return not updated return created update_query = {} if updates: update_query["$set"] = updates if removals: update_query["$unset"] = removals if updates or removals: upsert = save_condition is None last_error = collection.update(select_dict, update_query, upsert=upsert, **write_concern) if not upsert and last_error["n"] == 0: raise SaveConditionError('Race condition preventing' ' document update detected') created = is_new_object(last_error) if cascade is None: cascade = self._meta.get( 'cascade', False) or cascade_kwargs is not None if cascade: kwargs = { "force_insert": force_insert, "validate": validate, "write_concern": write_concern, "cascade": cascade } if cascade_kwargs: # Allow granular control over cascades kwargs.update(cascade_kwargs) kwargs['_refs'] = _refs self.cascade_save(**kwargs) except pymongo.errors.DuplicateKeyError, err: message = u'Tried to save duplicate unique keys (%s)' raise NotUniqueError(message % unicode(err)) except pymongo.errors.OperationFailure, err: message = 'Could not save document (%s)' if re.match('^E1100[01] duplicate key', unicode(err)): # E11000 - duplicate key error index # E11001 - duplicate key on update message = u'Tried to save duplicate unique keys (%s)' raise NotUniqueError(message % unicode(err)) raise OperationError(message % unicode(err)) id_field = self._meta['id_field'] if created or id_field not in self._meta.get('shard_key', []): self[id_field] = self._fields[id_field].to_python(object_id) signals.post_save.send(self.__class__, document=self, created=created) self._clear_changed_fields() self._created = False return self def cascade_save(self, *args, **kwargs): """Recursively saves any references / generic references on the document""" _refs = kwargs.get('_refs', []) or [] ReferenceField = _import_class('ReferenceField') GenericReferenceField = _import_class('GenericReferenceField') for name, cls in self._fields.items(): if not isinstance(cls, (ReferenceField, GenericReferenceField)): continue ref = self._data.get(name) if not ref or isinstance(ref, DBRef): continue if not getattr(ref, '_changed_fields', True): continue ref_id = "%s,%s" % (ref.__class__.__name__, str(ref._data)) if ref and ref_id not in _refs: _refs.append(ref_id) kwargs["_refs"] = _refs ref.save(**kwargs) ref._changed_fields = [] @property def _qs(self): """ Returns the queryset to use for updating / reloading / deletions """ if not hasattr(self, '__objects'): self.__objects = QuerySet(self, self._get_collection()) return self.__objects @property def _object_key(self): """Dict to identify object in collection """ select_dict = {'pk': self.pk} shard_key = self.__class__._meta.get('shard_key', tuple()) for k in shard_key: path = self._lookup_field(k.split('.')) actual_key = [p.db_field for p in path] val = self for ak in actual_key: val = getattr(val, ak) select_dict['__'.join(actual_key)] = val return select_dict def update(self, **kwargs): """Performs an update on the :class:`~mongoengine.Document` A convenience wrapper to :meth:`~mongoengine.QuerySet.update`. Raises :class:`OperationError` if called on an object that has not yet been saved. """ if not self.pk: if kwargs.get('upsert', False): query = self.to_mongo() if "_cls" in query: del query["_cls"] return self._qs.filter(**query).update_one(**kwargs) else: raise OperationError( 'attempt to update a document not yet saved') # Need to add shard key to query, or you get an error return self._qs.filter(**self._object_key).update_one(**kwargs) def delete(self, **write_concern): """Delete the :class:`~mongoengine.Document` from the database. This will only take effect if the document has been previously saved. :param write_concern: Extra keyword arguments are passed down which will be used as options for the resultant ``getLastError`` command. For example, ``save(..., write_concern={w: 2, fsync: True}, ...)`` will wait until at least two servers have recorded the write and will force an fsync on the primary server. """ signals.pre_delete.send(self.__class__, document=self) # Delete FileFields separately FileField = _import_class('FileField') for name, field in self._fields.iteritems(): if isinstance(field, FileField): getattr(self, name).delete() try: self._qs.filter( **self._object_key).delete(write_concern=write_concern, _from_doc_delete=True) except pymongo.errors.OperationFailure, err: message = u'Could not delete document (%s)' % err.message raise OperationError(message) signals.post_delete.send(self.__class__, document=self) def switch_db(self, db_alias, keep_created=True): """ Temporarily switch the database for a document instance. Only really useful for archiving off data and calling `save()`:: user = User.objects.get(id=user_id) user.switch_db('archive-db') user.save() :param str db_alias: The database alias to use for saving the document :param bool keep_created: keep self._created value after switching db, else is reset to True .. seealso:: Use :class:`~mongoengine.context_managers.switch_collection` if you need to read from another collection """ with switch_db(self.__class__, db_alias) as cls: collection = cls._get_collection() db = cls._get_db() self._get_collection = lambda: collection self._get_db = lambda: db self._collection = collection self._created = True if not keep_created else self._created self.__objects = self._qs self.__objects._collection_obj = collection return self def switch_collection(self, collection_name, keep_created=True): """ Temporarily switch the collection for a document instance. Only really useful for archiving off data and calling `save()`:: user = User.objects.get(id=user_id) user.switch_collection('old-users') user.save() :param str collection_name: The database alias to use for saving the document :param bool keep_created: keep self._created value after switching collection, else is reset to True .. seealso:: Use :class:`~mongoengine.context_managers.switch_db` if you need to read from another database """ with switch_collection(self.__class__, collection_name) as cls: collection = cls._get_collection() self._get_collection = lambda: collection self._collection = collection self._created = True if not keep_created else self._created self.__objects = self._qs self.__objects._collection_obj = collection return self def select_related(self, max_depth=1): """Handles dereferencing of :class:`~bson.dbref.DBRef` objects to a maximum depth in order to cut down the number queries to mongodb. .. versionadded:: 0.5 """ DeReference = _import_class('DeReference') DeReference()([self], max_depth + 1) return self def reload(self, *fields, **kwargs): """Reloads all attributes from the database. :param fields: (optional) args list of fields to reload :param max_depth: (optional) depth of dereferencing to follow .. versionadded:: 0.1.2 .. versionchanged:: 0.6 Now chainable .. versionchanged:: 0.9 Can provide specific fields to reload """ max_depth = 1 if fields and isinstance(fields[0], int): max_depth = fields[0] fields = fields[1:] elif "max_depth" in kwargs: max_depth = kwargs["max_depth"] if not self.pk: raise self.DoesNotExist("Document does not exist") obj = self._qs.read_preference(ReadPreference.PRIMARY).filter( **self._object_key).only(*fields).limit( 1).select_related(max_depth=max_depth) if obj: obj = obj[0] else: raise self.DoesNotExist("Document does not exist") for field in obj._data: if not fields or field in fields: try: setattr(self, field, self._reload(field, obj[field])) except (KeyError, AttributeError): try: # If field is a special field, e.g. items is stored as _reserved_items, # an KeyError is thrown. So try to retrieve the field from _data setattr(self, field, self._reload(field, obj._data.get(field))) except KeyError: # If field is removed from the database while the object # is in memory, a reload would cause a KeyError # i.e. obj.update(unset__field=1) followed by obj.reload() delattr(self, field) self._changed_fields = obj._changed_fields self._created = False return self def _reload(self, key, value): """Used by :meth:`~mongoengine.Document.reload` to ensure the correct instance is linked to self. """ if isinstance(value, BaseDict): value = [(k, self._reload(k, v)) for k, v in value.items()] value = BaseDict(value, self, key) elif isinstance(value, EmbeddedDocumentList): value = [self._reload(key, v) for v in value] value = EmbeddedDocumentList(value, self, key) elif isinstance(value, BaseList): value = [self._reload(key, v) for v in value] value = BaseList(value, self, key) elif isinstance(value, (EmbeddedDocument, DynamicEmbeddedDocument)): value._instance = None value._changed_fields = [] return value def to_dbref(self): """Returns an instance of :class:`~bson.dbref.DBRef` useful in `__raw__` queries.""" if not self.pk: msg = "Only saved documents can have a valid dbref" raise OperationError(msg) return DBRef(self.__class__._get_collection_name(), self.pk) @classmethod def register_delete_rule(cls, document_cls, field_name, rule): """This method registers the delete rules to apply when removing this object. """ classes = [get_document(class_name) for class_name in cls._subclasses if class_name != cls.__name__] + [cls] documents = [get_document(class_name) for class_name in document_cls._subclasses if class_name != document_cls.__name__] + [document_cls] for klass in classes: for document_cls in documents: delete_rules = klass._meta.get('delete_rules') or {} delete_rules[(document_cls, field_name)] = rule klass._meta['delete_rules'] = delete_rules @classmethod def drop_collection(cls): """Drops the entire collection associated with this :class:`~mongoengine.Document` type from the database. """ cls._collection = None db = cls._get_db() db.drop_collection(cls._get_collection_name()) @classmethod def create_index(cls, keys, background=False, **kwargs): """Creates the given indexes if required. :param keys: a single index key or a list of index keys (to construct a multi-field index); keys may be prefixed with a **+** or a **-** to determine the index ordering :param background: Allows index creation in the background """ index_spec = cls._build_index_spec(keys) index_spec = index_spec.copy() fields = index_spec.pop('fields') drop_dups = kwargs.get('drop_dups', False) if IS_PYMONGO_3 and drop_dups: msg = "drop_dups is deprecated and is removed when using PyMongo 3+." warnings.warn(msg, DeprecationWarning) elif not IS_PYMONGO_3: index_spec['drop_dups'] = drop_dups index_spec['background'] = background index_spec.update(kwargs) if IS_PYMONGO_3: return cls._get_collection().create_index(fields, **index_spec) else: return cls._get_collection().ensure_index(fields, **index_spec) @classmethod def ensure_index(cls, key_or_list, drop_dups=False, background=False, **kwargs): """Ensure that the given indexes are in place. Deprecated in favour of create_index. :param key_or_list: a single index key or a list of index keys (to construct a multi-field index); keys may be prefixed with a **+** or a **-** to determine the index ordering :param background: Allows index creation in the background :param drop_dups: Was removed/ignored with MongoDB >2.7.5. The value will be removed if PyMongo3+ is used """ if IS_PYMONGO_3 and drop_dups: msg = "drop_dups is deprecated and is removed when using PyMongo 3+." warnings.warn(msg, DeprecationWarning) elif not IS_PYMONGO_3: kwargs.update({'drop_dups': drop_dups}) return cls.create_index(key_or_list, background=background, **kwargs) @classmethod def ensure_indexes(cls): """Checks the document meta data and ensures all the indexes exist. Global defaults can be set in the meta - see :doc:`guide/defining-documents` .. note:: You can disable automatic index creation by setting `auto_create_index` to False in the documents meta data """ background = cls._meta.get('index_background', False) drop_dups = cls._meta.get('index_drop_dups', False) index_opts = cls._meta.get('index_opts') or {} index_cls = cls._meta.get('index_cls', True) if IS_PYMONGO_3 and drop_dups: msg = "drop_dups is deprecated and is removed when using PyMongo 3+." warnings.warn(msg, DeprecationWarning) collection = cls._get_collection() # 746: when connection is via mongos, the read preference is not necessarily an indication that # this code runs on a secondary if not collection.is_mongos and collection.read_preference > 1: return # determine if an index which we are creating includes # _cls as its first field; if so, we can avoid creating # an extra index on _cls, as mongodb will use the existing # index to service queries against _cls cls_indexed = False # Ensure document-defined indexes are created if cls._meta['index_specs']: index_spec = cls._meta['index_specs'] for spec in index_spec: spec = spec.copy() fields = spec.pop('fields') cls_indexed = cls_indexed or includes_cls(fields) opts = index_opts.copy() opts.update(spec) # we shouldn't pass 'cls' to the collection.ensureIndex options # because of https://jira.mongodb.org/browse/SERVER-769 if 'cls' in opts: del opts['cls'] if IS_PYMONGO_3: collection.create_index(fields, background=background, **opts) else: collection.ensure_index(fields, background=background, drop_dups=drop_dups, **opts) # If _cls is being used (for polymorphism), it needs an index, # only if another index doesn't begin with _cls if (index_cls and not cls_indexed and cls._meta.get('allow_inheritance', ALLOW_INHERITANCE) is True): # we shouldn't pass 'cls' to the collection.ensureIndex options # because of https://jira.mongodb.org/browse/SERVER-769 if 'cls' in index_opts: del index_opts['cls'] if IS_PYMONGO_3: collection.create_index('_cls', background=background, **index_opts) else: collection.ensure_index('_cls', background=background, **index_opts) @classmethod def list_indexes(cls): """ Lists all of the indexes that should be created for given collection. It includes all the indexes from super- and sub-classes. """ if cls._meta.get('abstract'): return [] # get all the base classes, subclasses and siblings classes = [] def get_classes(cls): if (cls not in classes and isinstance(cls, TopLevelDocumentMetaclass)): classes.append(cls) for base_cls in cls.__bases__: if (isinstance(base_cls, TopLevelDocumentMetaclass) and base_cls != Document and not base_cls._meta.get('abstract') and base_cls._get_collection().full_name == cls._get_collection().full_name and base_cls not in classes): classes.append(base_cls) get_classes(base_cls) for subclass in cls.__subclasses__(): if (isinstance(base_cls, TopLevelDocumentMetaclass) and subclass._get_collection().full_name == cls._get_collection().full_name and subclass not in classes): classes.append(subclass) get_classes(subclass) get_classes(cls) # get the indexes spec for all of the gathered classes def get_indexes_spec(cls): indexes = [] if cls._meta['index_specs']: index_spec = cls._meta['index_specs'] for spec in index_spec: spec = spec.copy() fields = spec.pop('fields') indexes.append(fields) return indexes indexes = [] for klass in classes: for index in get_indexes_spec(klass): if index not in indexes: indexes.append(index) # finish up by appending { '_id': 1 } and { '_cls': 1 }, if needed if [(u'_id', 1)] not in indexes: indexes.append([(u'_id', 1)]) if (cls._meta.get('index_cls', True) and cls._meta.get('allow_inheritance', ALLOW_INHERITANCE) is True): indexes.append([(u'_cls', 1)]) return indexes @classmethod def compare_indexes(cls): """ Compares the indexes defined in MongoEngine with the ones existing in the database. Returns any missing/extra indexes. """ required = cls.list_indexes() existing = [info['key'] for info in cls._get_collection().index_information().values()] missing = [index for index in required if index not in existing] extra = [index for index in existing if index not in required] # if { _cls: 1 } is missing, make sure it's *really* necessary if [(u'_cls', 1)] in missing: cls_obsolete = False for index in existing: if includes_cls(index) and index not in extra: cls_obsolete = True break if cls_obsolete: missing.remove([(u'_cls', 1)]) return {'missing': missing, 'extra': extra} class DynamicDocument(Document): """A Dynamic Document class allowing flexible, expandable and uncontrolled schemas. As a :class:`~mongoengine.Document` subclass, acts in the same way as an ordinary document but has expando style properties. Any data passed or set against the :class:`~mongoengine.DynamicDocument` that is not a field is automatically converted into a :class:`~mongoengine.fields.DynamicField` and data can be attributed to that field. .. note:: There is one caveat on Dynamic Documents: fields cannot start with `_` """ # The __metaclass__ attribute is removed by 2to3 when running with Python3 # my_metaclass is defined so that metaclass can be queried in Python 2 & 3 my_metaclass = TopLevelDocumentMetaclass __metaclass__ = TopLevelDocumentMetaclass _dynamic = True def __delattr__(self, *args, **kwargs): """Deletes the attribute by setting to None and allowing _delta to unset it""" field_name = args[0] if field_name in self._dynamic_fields: setattr(self, field_name, None) else: super(DynamicDocument, self).__delattr__(*args, **kwargs) class DynamicEmbeddedDocument(EmbeddedDocument): """A Dynamic Embedded Document class allowing flexible, expandable and uncontrolled schemas. See :class:`~mongoengine.DynamicDocument` for more information about dynamic documents. """ # The __metaclass__ attribute is removed by 2to3 when running with Python3 # my_metaclass is defined so that metaclass can be queried in Python 2 & 3 my_metaclass = DocumentMetaclass __metaclass__ = DocumentMetaclass _dynamic = True def __delattr__(self, *args, **kwargs): """Deletes the attribute by setting to None and allowing _delta to unset it""" field_name = args[0] if field_name in self._fields: default = self._fields[field_name].default if callable(default): default = default() setattr(self, field_name, default) else: setattr(self, field_name, None) class MapReduceDocument(object): """A document returned from a map/reduce query. :param collection: An instance of :class:`~pymongo.Collection` :param key: Document/result key, often an instance of :class:`~bson.objectid.ObjectId`. If supplied as an ``ObjectId`` found in the given ``collection``, the object can be accessed via the ``object`` property. :param value: The result(s) for this key. .. versionadded:: 0.3 """ def __init__(self, document, collection, key, value): self._document = document self._collection = collection self.key = key self.value = value @property def object(self): """Lazy-load the object referenced by ``self.key``. ``self.key`` should be the ``primary_key``. """ id_field = self._document()._meta['id_field'] id_field_type = type(id_field) if not isinstance(self.key, id_field_type): try: self.key = id_field_type(self.key) except: raise Exception("Could not cast key as %s" % id_field_type.__name__) if not hasattr(self, "_key_object"): self._key_object = self._document.objects.with_id(self.key) return self._key_object return self._key_object mongoengine-0.10.6/mongoengine/queryset/0000775000175000017500000000000012651364023017677 5ustar travistravismongoengine-0.10.6/mongoengine/queryset/visitor.py0000664000175000017500000001052212651363712021754 0ustar travistravisimport copy from mongoengine.errors import InvalidQueryError from mongoengine.queryset import transform __all__ = ('Q',) class QNodeVisitor(object): """Base visitor class for visiting Q-object nodes in a query tree. """ def visit_combination(self, combination): """Called by QCombination objects. """ return combination def visit_query(self, query): """Called by (New)Q objects. """ return query class DuplicateQueryConditionsError(InvalidQueryError): pass class SimplificationVisitor(QNodeVisitor): """Simplifies query trees by combining unnecessary 'and' connection nodes into a single Q-object. """ def visit_combination(self, combination): if combination.operation == combination.AND: # The simplification only applies to 'simple' queries if all(isinstance(node, Q) for node in combination.children): queries = [n.query for n in combination.children] try: return Q(**self._query_conjunction(queries)) except DuplicateQueryConditionsError: # Cannot be simplified pass return combination def _query_conjunction(self, queries): """Merges query dicts - effectively &ing them together. """ query_ops = set() combined_query = {} for query in queries: ops = set(query.keys()) # Make sure that the same operation isn't applied more than once # to a single field intersection = ops.intersection(query_ops) if intersection: raise DuplicateQueryConditionsError() query_ops.update(ops) combined_query.update(copy.deepcopy(query)) return combined_query class QueryCompilerVisitor(QNodeVisitor): """Compiles the nodes in a query tree to a PyMongo-compatible query dictionary. """ def __init__(self, document): self.document = document def visit_combination(self, combination): operator = "$and" if combination.operation == combination.OR: operator = "$or" return {operator: combination.children} def visit_query(self, query): return transform.query(self.document, **query.query) class QNode(object): """Base class for nodes in query trees. """ AND = 0 OR = 1 def to_query(self, document): query = self.accept(SimplificationVisitor()) query = query.accept(QueryCompilerVisitor(document)) return query def accept(self, visitor): raise NotImplementedError def _combine(self, other, operation): """Combine this node with another node into a QCombination object. """ if getattr(other, 'empty', True): return self if self.empty: return other return QCombination(operation, [self, other]) @property def empty(self): return False def __or__(self, other): return self._combine(other, self.OR) def __and__(self, other): return self._combine(other, self.AND) class QCombination(QNode): """Represents the combination of several conditions by a given logical operator. """ def __init__(self, operation, children): self.operation = operation self.children = [] for node in children: # If the child is a combination of the same type, we can merge its # children directly into this combinations children if isinstance(node, QCombination) and node.operation == operation: self.children += node.children else: self.children.append(node) def accept(self, visitor): for i in range(len(self.children)): if isinstance(self.children[i], QNode): self.children[i] = self.children[i].accept(visitor) return visitor.visit_combination(self) @property def empty(self): return not bool(self.children) class Q(QNode): """A simple query object, used in a query tree to build up more complex query structures. """ def __init__(self, **query): self.query = query def accept(self, visitor): return visitor.visit_query(self) @property def empty(self): return not bool(self.query) mongoengine-0.10.6/mongoengine/queryset/__init__.py0000664000175000017500000000101512651363712022011 0ustar travistravisfrom mongoengine.errors import (DoesNotExist, MultipleObjectsReturned, InvalidQueryError, OperationError, NotUniqueError) from mongoengine.queryset.field_list import * from mongoengine.queryset.manager import * from mongoengine.queryset.queryset import * from mongoengine.queryset.transform import * from mongoengine.queryset.visitor import * __all__ = (field_list.__all__ + manager.__all__ + queryset.__all__ + transform.__all__ + visitor.__all__) mongoengine-0.10.6/mongoengine/queryset/field_list.py0000664000175000017500000000563012651363712022377 0ustar travistravis__all__ = ('QueryFieldList',) class QueryFieldList(object): """Object that handles combinations of .only() and .exclude() calls""" ONLY = 1 EXCLUDE = 0 def __init__(self, fields=None, value=ONLY, always_include=None, _only_called=False): """The QueryFieldList builder :param fields: A list of fields used in `.only()` or `.exclude()` :param value: How to handle the fields; either `ONLY` or `EXCLUDE` :param always_include: Any fields to always_include eg `_cls` :param _only_called: Has `.only()` been called? If so its a set of fields otherwise it performs a union. """ self.value = value self.fields = set(fields or []) self.always_include = set(always_include or []) self._id = None self._only_called = _only_called self.slice = {} def __add__(self, f): if isinstance(f.value, dict): for field in f.fields: self.slice[field] = f.value if not self.fields: self.fields = f.fields elif not self.fields: self.fields = f.fields self.value = f.value self.slice = {} elif self.value is self.ONLY and f.value is self.ONLY: self._clean_slice() if self._only_called: self.fields = self.fields.union(f.fields) else: self.fields = f.fields elif self.value is self.EXCLUDE and f.value is self.EXCLUDE: self.fields = self.fields.union(f.fields) self._clean_slice() elif self.value is self.ONLY and f.value is self.EXCLUDE: self.fields -= f.fields self._clean_slice() elif self.value is self.EXCLUDE and f.value is self.ONLY: self.value = self.ONLY self.fields = f.fields - self.fields self._clean_slice() if '_id' in f.fields: self._id = f.value if self.always_include: if self.value is self.ONLY and self.fields: if sorted(self.slice.keys()) != sorted(self.fields): self.fields = self.fields.union(self.always_include) else: self.fields -= self.always_include if getattr(f, '_only_called', False): self._only_called = True return self def __nonzero__(self): return bool(self.fields) def as_dict(self): field_list = dict((field, self.value) for field in self.fields) if self.slice: field_list.update(self.slice) if self._id is not None: field_list['_id'] = self._id return field_list def reset(self): self.fields = set([]) self.slice = {} self.value = self.ONLY def _clean_slice(self): if self.slice: for field in set(self.slice.keys()) - self.fields: del self.slice[field] mongoengine-0.10.6/mongoengine/queryset/base.py0000664000175000017500000020670612651363712021202 0ustar travistravisfrom __future__ import absolute_import import copy import itertools import operator import pprint import re import warnings from bson import SON from bson.code import Code from bson import json_util import pymongo import pymongo.errors from pymongo.common import validate_read_preference from mongoengine import signals from mongoengine.connection import get_db from mongoengine.context_managers import switch_db from mongoengine.common import _import_class from mongoengine.base.common import get_document from mongoengine.errors import (OperationError, NotUniqueError, InvalidQueryError, LookUpError) from mongoengine.python_support import IS_PYMONGO_3 from mongoengine.queryset import transform from mongoengine.queryset.field_list import QueryFieldList from mongoengine.queryset.visitor import Q, QNode if IS_PYMONGO_3: from pymongo.collection import ReturnDocument __all__ = ('BaseQuerySet', 'DO_NOTHING', 'NULLIFY', 'CASCADE', 'DENY', 'PULL') # Delete rules DO_NOTHING = 0 NULLIFY = 1 CASCADE = 2 DENY = 3 PULL = 4 RE_TYPE = type(re.compile('')) class BaseQuerySet(object): """A set of results returned from a query. Wraps a MongoDB cursor, providing :class:`~mongoengine.Document` objects as the results. """ __dereference = False _auto_dereference = True def __init__(self, document, collection): self._document = document self._collection_obj = collection self._mongo_query = None self._query_obj = Q() self._initial_query = {} self._where_clause = None self._loaded_fields = QueryFieldList() self._ordering = None self._snapshot = False self._timeout = True self._class_check = True self._slave_okay = False self._read_preference = None self._iter = False self._scalar = [] self._none = False self._as_pymongo = False self._as_pymongo_coerce = False self._search_text = None # If inheritance is allowed, only return instances and instances of # subclasses of the class being used if document._meta.get('allow_inheritance') is True: if len(self._document._subclasses) == 1: self._initial_query = {"_cls": self._document._subclasses[0]} else: self._initial_query = { "_cls": {"$in": self._document._subclasses}} self._loaded_fields = QueryFieldList(always_include=['_cls']) self._cursor_obj = None self._limit = None self._skip = None self._hint = -1 # Using -1 as None is a valid value for hint self.only_fields = [] self._max_time_ms = None def __call__(self, q_obj=None, class_check=True, read_preference=None, **query): """Filter the selected documents by calling the :class:`~mongoengine.queryset.QuerySet` with a query. :param q_obj: a :class:`~mongoengine.queryset.Q` object to be used in the query; the :class:`~mongoengine.queryset.QuerySet` is filtered multiple times with different :class:`~mongoengine.queryset.Q` objects, only the last one will be used :param class_check: If set to False bypass class name check when querying collection :param read_preference: if set, overrides connection-level read_preference from `ReplicaSetConnection`. :param query: Django-style query keyword arguments """ query = Q(**query) if q_obj: # make sure proper query object is passed if not isinstance(q_obj, QNode): msg = ("Not a query object: %s. " "Did you intend to use key=value?" % q_obj) raise InvalidQueryError(msg) query &= q_obj if read_preference is None: queryset = self.clone() else: # Use the clone provided when setting read_preference queryset = self.read_preference(read_preference) queryset._query_obj &= query queryset._mongo_query = None queryset._cursor_obj = None queryset._class_check = class_check return queryset def __getitem__(self, key): """Support skip and limit using getitem and slicing syntax. """ queryset = self.clone() # Slice provided if isinstance(key, slice): try: queryset._cursor_obj = queryset._cursor[key] queryset._skip, queryset._limit = key.start, key.stop if key.start and key.stop: queryset._limit = key.stop - key.start except IndexError, err: # PyMongo raises an error if key.start == key.stop, catch it, # bin it, kill it. start = key.start or 0 if start >= 0 and key.stop >= 0 and key.step is None: if start == key.stop: queryset.limit(0) queryset._skip = key.start queryset._limit = key.stop - start return queryset raise err # Allow further QuerySet modifications to be performed return queryset # Integer index provided elif isinstance(key, int): if queryset._scalar: return queryset._get_scalar( queryset._document._from_son(queryset._cursor[key], _auto_dereference=self._auto_dereference, only_fields=self.only_fields)) if queryset._as_pymongo: return queryset._get_as_pymongo(queryset._cursor[key]) return queryset._document._from_son(queryset._cursor[key], _auto_dereference=self._auto_dereference, only_fields=self.only_fields) raise AttributeError def __iter__(self): raise NotImplementedError def _has_data(self): """ Retrieves whether cursor has any data. """ queryset = self.order_by() return False if queryset.first() is None else True def __nonzero__(self): """ Avoid to open all records in an if stmt in Py2. """ return self._has_data() def __bool__(self): """ Avoid to open all records in an if stmt in Py3. """ return self._has_data() # Core functions def all(self): """Returns all documents.""" return self.__call__() def filter(self, *q_objs, **query): """An alias of :meth:`~mongoengine.queryset.QuerySet.__call__` """ return self.__call__(*q_objs, **query) def search_text(self, text, language=None): """ Start a text search, using text indexes. Require: MongoDB server version 2.6+. :param language: The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer. If not specified, the search uses the default language of the index. For supported languages, see `Text Search Languages `. """ queryset = self.clone() if queryset._search_text: raise OperationError( "It is not possible to use search_text two times.") query_kwargs = SON({'$search': text}) if language: query_kwargs['$language'] = language queryset._query_obj &= Q(__raw__={'$text': query_kwargs}) queryset._mongo_query = None queryset._cursor_obj = None queryset._search_text = text return queryset def get(self, *q_objs, **query): """Retrieve the the matching object raising :class:`~mongoengine.queryset.MultipleObjectsReturned` or `DocumentName.MultipleObjectsReturned` exception if multiple results and :class:`~mongoengine.queryset.DoesNotExist` or `DocumentName.DoesNotExist` if no results are found. .. versionadded:: 0.3 """ queryset = self.clone() queryset = queryset.order_by().limit(2) queryset = queryset.filter(*q_objs, **query) try: result = queryset.next() except StopIteration: msg = ("%s matching query does not exist." % queryset._document._class_name) raise queryset._document.DoesNotExist(msg) try: queryset.next() except StopIteration: return result queryset.rewind() message = u'%d items returned, instead of 1' % queryset.count() raise queryset._document.MultipleObjectsReturned(message) def create(self, **kwargs): """Create new object. Returns the saved object instance. .. versionadded:: 0.4 """ return self._document(**kwargs).save() def first(self): """Retrieve the first object matching the query. """ queryset = self.clone() try: result = queryset[0] except IndexError: result = None return result def insert(self, doc_or_docs, load_bulk=True, write_concern=None): """bulk insert documents :param doc_or_docs: a document or list of documents to be inserted :param load_bulk (optional): If True returns the list of document instances :param write_concern: Extra keyword arguments are passed down to :meth:`~pymongo.collection.Collection.insert` which will be used as options for the resultant ``getLastError`` command. For example, ``insert(..., {w: 2, fsync: True})`` will wait until at least two servers have recorded the write and will force an fsync on each server being written to. By default returns document instances, set ``load_bulk`` to False to return just ``ObjectIds`` .. versionadded:: 0.5 """ Document = _import_class('Document') if write_concern is None: write_concern = {} docs = doc_or_docs return_one = False if isinstance(docs, Document) or issubclass(docs.__class__, Document): return_one = True docs = [docs] raw = [] for doc in docs: if not isinstance(doc, self._document): msg = ("Some documents inserted aren't instances of %s" % str(self._document)) raise OperationError(msg) if doc.pk and not doc._created: msg = "Some documents have ObjectIds use doc.update() instead" raise OperationError(msg) raw.append(doc.to_mongo()) signals.pre_bulk_insert.send(self._document, documents=docs) try: ids = self._collection.insert(raw, **write_concern) except pymongo.errors.DuplicateKeyError, err: message = 'Could not save document (%s)' raise NotUniqueError(message % unicode(err)) except pymongo.errors.OperationFailure, err: message = 'Could not save document (%s)' if re.match('^E1100[01] duplicate key', unicode(err)): # E11000 - duplicate key error index # E11001 - duplicate key on update message = u'Tried to save duplicate unique keys (%s)' raise NotUniqueError(message % unicode(err)) raise OperationError(message % unicode(err)) if not load_bulk: signals.post_bulk_insert.send( self._document, documents=docs, loaded=False) return return_one and ids[0] or ids documents = self.in_bulk(ids) results = [] for obj_id in ids: results.append(documents.get(obj_id)) signals.post_bulk_insert.send( self._document, documents=results, loaded=True) return return_one and results[0] or results def count(self, with_limit_and_skip=False): """Count the selected elements in the query. :param with_limit_and_skip (optional): take any :meth:`limit` or :meth:`skip` that has been applied to this cursor into account when getting the count """ if self._limit == 0 and with_limit_and_skip or self._none: return 0 return self._cursor.count(with_limit_and_skip=with_limit_and_skip) def delete(self, write_concern=None, _from_doc_delete=False, cascade_refs=None): """Delete the documents matched by the query. :param write_concern: Extra keyword arguments are passed down which will be used as options for the resultant ``getLastError`` command. For example, ``save(..., write_concern={w: 2, fsync: True}, ...)`` will wait until at least two servers have recorded the write and will force an fsync on the primary server. :param _from_doc_delete: True when called from document delete therefore signals will have been triggered so don't loop. :returns number of deleted documents """ queryset = self.clone() doc = queryset._document if write_concern is None: write_concern = {} # Handle deletes where skips or limits have been applied or # there is an untriggered delete signal has_delete_signal = signals.signals_available and ( signals.pre_delete.has_receivers_for(self._document) or signals.post_delete.has_receivers_for(self._document)) call_document_delete = (queryset._skip or queryset._limit or has_delete_signal) and not _from_doc_delete if call_document_delete: cnt = 0 for doc in queryset: doc.delete(**write_concern) cnt += 1 return cnt delete_rules = doc._meta.get('delete_rules') or {} # Check for DENY rules before actually deleting/nullifying any other # references for rule_entry in delete_rules: document_cls, field_name = rule_entry if document_cls._meta.get('abstract'): continue rule = doc._meta['delete_rules'][rule_entry] if rule == DENY and document_cls.objects( **{field_name + '__in': self}).count() > 0: msg = ("Could not delete document (%s.%s refers to it)" % (document_cls.__name__, field_name)) raise OperationError(msg) for rule_entry in delete_rules: document_cls, field_name = rule_entry if document_cls._meta.get('abstract'): continue rule = doc._meta['delete_rules'][rule_entry] if rule == CASCADE: cascade_refs = set() if cascade_refs is None else cascade_refs for ref in queryset: cascade_refs.add(ref.id) ref_q = document_cls.objects(**{field_name + '__in': self, 'id__nin': cascade_refs}) ref_q_count = ref_q.count() if ref_q_count > 0: ref_q.delete(write_concern=write_concern, cascade_refs=cascade_refs) elif rule == NULLIFY: document_cls.objects(**{field_name + '__in': self}).update( write_concern=write_concern, **{'unset__%s' % field_name: 1}) elif rule == PULL: document_cls.objects(**{field_name + '__in': self}).update( write_concern=write_concern, **{'pull_all__%s' % field_name: self}) result = queryset._collection.remove(queryset._query, **write_concern) if result: return result.get("n") def update(self, upsert=False, multi=True, write_concern=None, full_result=False, **update): """Perform an atomic update on the fields matched by the query. :param upsert: Any existing document with that "_id" is overwritten. :param multi: Update multiple documents. :param write_concern: Extra keyword arguments are passed down which will be used as options for the resultant ``getLastError`` command. For example, ``save(..., write_concern={w: 2, fsync: True}, ...)`` will wait until at least two servers have recorded the write and will force an fsync on the primary server. :param full_result: Return the full result rather than just the number updated. :param update: Django-style update keyword arguments .. versionadded:: 0.2 """ if not update and not upsert: raise OperationError("No update parameters, would remove data") if write_concern is None: write_concern = {} queryset = self.clone() query = queryset._query update = transform.update(queryset._document, **update) # If doing an atomic upsert on an inheritable class # then ensure we add _cls to the update operation if upsert and '_cls' in query: if '$set' in update: update["$set"]["_cls"] = queryset._document._class_name else: update["$set"] = {"_cls": queryset._document._class_name} try: result = queryset._collection.update(query, update, multi=multi, upsert=upsert, **write_concern) if full_result: return result elif result: return result['n'] except pymongo.errors.DuplicateKeyError, err: raise NotUniqueError(u'Update failed (%s)' % unicode(err)) except pymongo.errors.OperationFailure, err: if unicode(err) == u'multi not coded yet': message = u'update() method requires MongoDB 1.1.3+' raise OperationError(message) raise OperationError(u'Update failed (%s)' % unicode(err)) def upsert_one(self, write_concern=None, **update): """Overwrite or add the first document matched by the query. :param write_concern: Extra keyword arguments are passed down which will be used as options for the resultant ``getLastError`` command. For example, ``save(..., write_concern={w: 2, fsync: True}, ...)`` will wait until at least two servers have recorded the write and will force an fsync on the primary server. :param update: Django-style update keyword arguments :returns the new or overwritten document .. versionadded:: 0.10.2 """ atomic_update = self.update(multi=False, upsert=True, write_concern=write_concern, full_result=True,**update) if atomic_update['updatedExisting']: document = self.get() else: document = self._document.objects.with_id(atomic_update['upserted']) return document def update_one(self, upsert=False, write_concern=None, **update): """Perform an atomic update on the fields of the first document matched by the query. :param upsert: Any existing document with that "_id" is overwritten. :param write_concern: Extra keyword arguments are passed down which will be used as options for the resultant ``getLastError`` command. For example, ``save(..., write_concern={w: 2, fsync: True}, ...)`` will wait until at least two servers have recorded the write and will force an fsync on the primary server. :param update: Django-style update keyword arguments .. versionadded:: 0.2 """ return self.update( upsert=upsert, multi=False, write_concern=write_concern, **update) def modify(self, upsert=False, full_response=False, remove=False, new=False, **update): """Update and return the updated document. Returns either the document before or after modification based on `new` parameter. If no documents match the query and `upsert` is false, returns ``None``. If upserting and `new` is false, returns ``None``. If the full_response parameter is ``True``, the return value will be the entire response object from the server, including the 'ok' and 'lastErrorObject' fields, rather than just the modified document. This is useful mainly because the 'lastErrorObject' document holds information about the command's execution. :param upsert: insert if document doesn't exist (default ``False``) :param full_response: return the entire response object from the server (default ``False``, not available for PyMongo 3+) :param remove: remove rather than updating (default ``False``) :param new: return updated rather than original document (default ``False``) :param update: Django-style update keyword arguments .. versionadded:: 0.9 """ if remove and new: raise OperationError("Conflicting parameters: remove and new") if not update and not upsert and not remove: raise OperationError( "No update parameters, must either update or remove") queryset = self.clone() query = queryset._query if not IS_PYMONGO_3 or not remove: update = transform.update(queryset._document, **update) sort = queryset._ordering try: if IS_PYMONGO_3: if full_response: msg = "With PyMongo 3+, it is not possible anymore to get the full response." warnings.warn(msg, DeprecationWarning) if remove: result = queryset._collection.find_one_and_delete( query, sort=sort, **self._cursor_args) else: if new: return_doc = ReturnDocument.AFTER else: return_doc = ReturnDocument.BEFORE result = queryset._collection.find_one_and_update( query, update, upsert=upsert, sort=sort, return_document=return_doc, **self._cursor_args) else: result = queryset._collection.find_and_modify( query, update, upsert=upsert, sort=sort, remove=remove, new=new, full_response=full_response, **self._cursor_args) except pymongo.errors.DuplicateKeyError, err: raise NotUniqueError(u"Update failed (%s)" % err) except pymongo.errors.OperationFailure, err: raise OperationError(u"Update failed (%s)" % err) if full_response: if result["value"] is not None: result["value"] = self._document._from_son(result["value"], only_fields=self.only_fields) else: if result is not None: result = self._document._from_son(result, only_fields=self.only_fields) return result def with_id(self, object_id): """Retrieve the object matching the id provided. Uses `object_id` only and raises InvalidQueryError if a filter has been applied. Returns `None` if no document exists with that id. :param object_id: the value for the id of the document to look up .. versionchanged:: 0.6 Raises InvalidQueryError if filter has been set """ queryset = self.clone() if not queryset._query_obj.empty: msg = "Cannot use a filter whilst using `with_id`" raise InvalidQueryError(msg) return queryset.filter(pk=object_id).first() def in_bulk(self, object_ids): """Retrieve a set of documents by their ids. :param object_ids: a list or tuple of ``ObjectId``\ s :rtype: dict of ObjectIds as keys and collection-specific Document subclasses as values. .. versionadded:: 0.3 """ doc_map = {} docs = self._collection.find({'_id': {'$in': object_ids}}, **self._cursor_args) if self._scalar: for doc in docs: doc_map[doc['_id']] = self._get_scalar( self._document._from_son(doc, only_fields=self.only_fields)) elif self._as_pymongo: for doc in docs: doc_map[doc['_id']] = self._get_as_pymongo(doc) else: for doc in docs: doc_map[doc['_id']] = self._document._from_son( doc, only_fields=self.only_fields, _auto_dereference=self._auto_dereference) return doc_map def none(self): """Helper that just returns a list""" queryset = self.clone() queryset._none = True return queryset def no_sub_classes(self): """ Only return instances of this document and not any inherited documents """ if self._document._meta.get('allow_inheritance') is True: self._initial_query = {"_cls": self._document._class_name} return self def using(self, alias): """This method is for controlling which database the QuerySet will be evaluated against if you are using more than one database. :param alias: The database alias .. versionadded:: 0.9 """ with switch_db(self._document, alias) as cls: collection = cls._get_collection() return self.clone_into(self.__class__(self._document, collection)) def clone(self): """Creates a copy of the current :class:`~mongoengine.queryset.QuerySet` .. versionadded:: 0.5 """ return self.clone_into(self.__class__(self._document, self._collection_obj)) def clone_into(self, cls): """Creates a copy of the current :class:`~mongoengine.queryset.base.BaseQuerySet` into another child class """ if not isinstance(cls, BaseQuerySet): raise OperationError( '%s is not a subclass of BaseQuerySet' % cls.__name__) copy_props = ('_mongo_query', '_initial_query', '_none', '_query_obj', '_where_clause', '_loaded_fields', '_ordering', '_snapshot', '_timeout', '_class_check', '_slave_okay', '_read_preference', '_iter', '_scalar', '_as_pymongo', '_as_pymongo_coerce', '_limit', '_skip', '_hint', '_auto_dereference', '_search_text', 'only_fields', '_max_time_ms') for prop in copy_props: val = getattr(self, prop) setattr(cls, prop, copy.copy(val)) if self._cursor_obj: cls._cursor_obj = self._cursor_obj.clone() return cls def select_related(self, max_depth=1): """Handles dereferencing of :class:`~bson.dbref.DBRef` objects or :class:`~bson.object_id.ObjectId` a maximum depth in order to cut down the number queries to mongodb. .. versionadded:: 0.5 """ # Make select related work the same for querysets max_depth += 1 queryset = self.clone() return queryset._dereference(queryset, max_depth=max_depth) def limit(self, n): """Limit the number of returned documents to `n`. This may also be achieved using array-slicing syntax (e.g. ``User.objects[:5]``). :param n: the maximum number of objects to return """ queryset = self.clone() queryset._limit = n if n != 0 else 1 # Return self to allow chaining return queryset def skip(self, n): """Skip `n` documents before returning the results. This may also be achieved using array-slicing syntax (e.g. ``User.objects[5:]``). :param n: the number of objects to skip before returning results """ queryset = self.clone() queryset._skip = n return queryset def hint(self, index=None): """Added 'hint' support, telling Mongo the proper index to use for the query. Judicious use of hints can greatly improve query performance. When doing a query on multiple fields (at least one of which is indexed) pass the indexed field as a hint to the query. Hinting will not do anything if the corresponding index does not exist. The last hint applied to this cursor takes precedence over all others. .. versionadded:: 0.5 """ queryset = self.clone() queryset._hint = index return queryset def distinct(self, field): """Return a list of distinct values for a given field. :param field: the field to select distinct values from .. note:: This is a command and won't take ordering or limit into account. .. versionadded:: 0.4 .. versionchanged:: 0.5 - Fixed handling references .. versionchanged:: 0.6 - Improved db_field refrence handling """ queryset = self.clone() try: field = self._fields_to_dbfields([field]).pop() finally: distinct = self._dereference(queryset._cursor.distinct(field), 1, name=field, instance=self._document) doc_field = self._document._fields.get(field.split('.', 1)[0]) instance = False # We may need to cast to the correct type eg. ListField(EmbeddedDocumentField) EmbeddedDocumentField = _import_class('EmbeddedDocumentField') ListField = _import_class('ListField') GenericEmbeddedDocumentField = _import_class('GenericEmbeddedDocumentField') if isinstance(doc_field, ListField): doc_field = getattr(doc_field, "field", doc_field) if isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): instance = getattr(doc_field, "document_type", False) # handle distinct on subdocuments if '.' in field: for field_part in field.split('.')[1:]: # if looping on embedded document, get the document type instance if instance and isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): doc_field = instance # now get the subdocument doc_field = getattr(doc_field, field_part, doc_field) # We may need to cast to the correct type eg. ListField(EmbeddedDocumentField) if isinstance(doc_field, ListField): doc_field = getattr(doc_field, "field", doc_field) if isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): instance = getattr(doc_field, "document_type", False) if instance and isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): distinct = [instance(**doc) for doc in distinct] return distinct def only(self, *fields): """Load only a subset of this document's fields. :: post = BlogPost.objects(...).only("title", "author.name") .. note :: `only()` is chainable and will perform a union :: So with the following it will fetch both: `title` and `author.name`:: post = BlogPost.objects.only("title").only("author.name") :func:`~mongoengine.queryset.QuerySet.all_fields` will reset any field filters. :param fields: fields to include .. versionadded:: 0.3 .. versionchanged:: 0.5 - Added subfield support """ fields = dict([(f, QueryFieldList.ONLY) for f in fields]) self.only_fields = fields.keys() return self.fields(True, **fields) def exclude(self, *fields): """Opposite to .only(), exclude some document's fields. :: post = BlogPost.objects(...).exclude("comments") .. note :: `exclude()` is chainable and will perform a union :: So with the following it will exclude both: `title` and `author.name`:: post = BlogPost.objects.exclude("title").exclude("author.name") :func:`~mongoengine.queryset.QuerySet.all_fields` will reset any field filters. :param fields: fields to exclude .. versionadded:: 0.5 """ fields = dict([(f, QueryFieldList.EXCLUDE) for f in fields]) return self.fields(**fields) def fields(self, _only_called=False, **kwargs): """Manipulate how you load this document's fields. Used by `.only()` and `.exclude()` to manipulate which fields to retrieve. Fields also allows for a greater level of control for example: Retrieving a Subrange of Array Elements: You can use the $slice operator to retrieve a subrange of elements in an array. For example to get the first 5 comments:: post = BlogPost.objects(...).fields(slice__comments=5) :param kwargs: A dictionary identifying what to include .. versionadded:: 0.5 """ # Check for an operator and transform to mongo-style if there is operators = ["slice"] cleaned_fields = [] for key, value in kwargs.items(): parts = key.split('__') if parts[0] in operators: op = parts.pop(0) value = {'$' + op: value} key = '.'.join(parts) cleaned_fields.append((key, value)) fields = sorted(cleaned_fields, key=operator.itemgetter(1)) queryset = self.clone() for value, group in itertools.groupby(fields, lambda x: x[1]): fields = [field for field, value in group] fields = queryset._fields_to_dbfields(fields) queryset._loaded_fields += QueryFieldList( fields, value=value, _only_called=_only_called) return queryset def all_fields(self): """Include all fields. Reset all previously calls of .only() or .exclude(). :: post = BlogPost.objects.exclude("comments").all_fields() .. versionadded:: 0.5 """ queryset = self.clone() queryset._loaded_fields = QueryFieldList( always_include=queryset._loaded_fields.always_include) return queryset def order_by(self, *keys): """Order the :class:`~mongoengine.queryset.QuerySet` by the keys. The order may be specified by prepending each of the keys by a + or a -. Ascending order is assumed. :param keys: fields to order the query results by; keys may be prefixed with **+** or **-** to determine the ordering direction """ queryset = self.clone() queryset._ordering = queryset._get_order_by(keys) return queryset def explain(self, format=False): """Return an explain plan record for the :class:`~mongoengine.queryset.QuerySet`\ 's cursor. :param format: format the plan before returning it """ plan = self._cursor.explain() if format: plan = pprint.pformat(plan) return plan # DEPRECATED. Has no more impact on PyMongo 3+ def snapshot(self, enabled): """Enable or disable snapshot mode when querying. :param enabled: whether or not snapshot mode is enabled ..versionchanged:: 0.5 - made chainable .. deprecated:: Ignored with PyMongo 3+ """ if IS_PYMONGO_3: msg = "snapshot is deprecated as it has no impact when using PyMongo 3+." warnings.warn(msg, DeprecationWarning) queryset = self.clone() queryset._snapshot = enabled return queryset def timeout(self, enabled): """Enable or disable the default mongod timeout when querying. :param enabled: whether or not the timeout is used ..versionchanged:: 0.5 - made chainable """ queryset = self.clone() queryset._timeout = enabled return queryset # DEPRECATED. Has no more impact on PyMongo 3+ def slave_okay(self, enabled): """Enable or disable the slave_okay when querying. :param enabled: whether or not the slave_okay is enabled .. deprecated:: Ignored with PyMongo 3+ """ if IS_PYMONGO_3: msg = "slave_okay is deprecated as it has no impact when using PyMongo 3+." warnings.warn(msg, DeprecationWarning) queryset = self.clone() queryset._slave_okay = enabled return queryset def read_preference(self, read_preference): """Change the read_preference when querying. :param read_preference: override ReplicaSetConnection-level preference. """ validate_read_preference('read_preference', read_preference) queryset = self.clone() queryset._read_preference = read_preference queryset._cursor_obj = None # we need to re-create the cursor object whenever we apply read_preference return queryset def scalar(self, *fields): """Instead of returning Document instances, return either a specific value or a tuple of values in order. Can be used along with :func:`~mongoengine.queryset.QuerySet.no_dereference` to turn off dereferencing. .. note:: This effects all results and can be unset by calling ``scalar`` without arguments. Calls ``only`` automatically. :param fields: One or more fields to return instead of a Document. """ queryset = self.clone() queryset._scalar = list(fields) if fields: queryset = queryset.only(*fields) else: queryset = queryset.all_fields() return queryset def values_list(self, *fields): """An alias for scalar""" return self.scalar(*fields) def as_pymongo(self, coerce_types=False): """Instead of returning Document instances, return raw values from pymongo. :param coerce_types: Field types (if applicable) would be use to coerce types. """ queryset = self.clone() queryset._as_pymongo = True queryset._as_pymongo_coerce = coerce_types return queryset def max_time_ms(self, ms): """Wait `ms` milliseconds before killing the query on the server :param ms: the number of milliseconds before killing the query on the server """ return self._chainable_method("max_time_ms", ms) # JSON Helpers def to_json(self, *args, **kwargs): """Converts a queryset to JSON""" return json_util.dumps(self.as_pymongo(), *args, **kwargs) def from_json(self, json_data): """Converts json data to unsaved objects""" son_data = json_util.loads(json_data) return [self._document._from_son(data, only_fields=self.only_fields) for data in son_data] def aggregate(self, *pipeline, **kwargs): """ Perform a aggregate function based in your queryset params :param pipeline: list of aggregation commands,\ see: http://docs.mongodb.org/manual/core/aggregation-pipeline/ .. versionadded:: 0.9 """ initial_pipeline = [] if self._query: initial_pipeline.append({'$match': self._query}) if self._ordering: initial_pipeline.append({'$sort': dict(self._ordering)}) if self._limit is not None: initial_pipeline.append({'$limit': self._limit}) if self._skip is not None: initial_pipeline.append({'$skip': self._skip}) pipeline = initial_pipeline + list(pipeline) return self._collection.aggregate(pipeline, cursor={}, **kwargs) # JS functionality def map_reduce(self, map_f, reduce_f, output, finalize_f=None, limit=None, scope=None): """Perform a map/reduce query using the current query spec and ordering. While ``map_reduce`` respects ``QuerySet`` chaining, it must be the last call made, as it does not return a maleable ``QuerySet``. See the :meth:`~mongoengine.tests.QuerySetTest.test_map_reduce` and :meth:`~mongoengine.tests.QuerySetTest.test_map_advanced` tests in ``tests.queryset.QuerySetTest`` for usage examples. :param map_f: map function, as :class:`~bson.code.Code` or string :param reduce_f: reduce function, as :class:`~bson.code.Code` or string :param output: output collection name, if set to 'inline' will try to use :class:`~pymongo.collection.Collection.inline_map_reduce` This can also be a dictionary containing output options see: http://docs.mongodb.org/manual/reference/command/mapReduce/#dbcmd.mapReduce :param finalize_f: finalize function, an optional function that performs any post-reduction processing. :param scope: values to insert into map/reduce global scope. Optional. :param limit: number of objects from current query to provide to map/reduce method Returns an iterator yielding :class:`~mongoengine.document.MapReduceDocument`. .. note:: Map/Reduce changed in server version **>= 1.7.4**. The PyMongo :meth:`~pymongo.collection.Collection.map_reduce` helper requires PyMongo version **>= 1.11**. .. versionchanged:: 0.5 - removed ``keep_temp`` keyword argument, which was only relevant for MongoDB server versions older than 1.7.4 .. versionadded:: 0.3 """ queryset = self.clone() MapReduceDocument = _import_class('MapReduceDocument') if not hasattr(self._collection, "map_reduce"): raise NotImplementedError("Requires MongoDB >= 1.7.1") map_f_scope = {} if isinstance(map_f, Code): map_f_scope = map_f.scope map_f = unicode(map_f) map_f = Code(queryset._sub_js_fields(map_f), map_f_scope) reduce_f_scope = {} if isinstance(reduce_f, Code): reduce_f_scope = reduce_f.scope reduce_f = unicode(reduce_f) reduce_f_code = queryset._sub_js_fields(reduce_f) reduce_f = Code(reduce_f_code, reduce_f_scope) mr_args = {'query': queryset._query} if finalize_f: finalize_f_scope = {} if isinstance(finalize_f, Code): finalize_f_scope = finalize_f.scope finalize_f = unicode(finalize_f) finalize_f_code = queryset._sub_js_fields(finalize_f) finalize_f = Code(finalize_f_code, finalize_f_scope) mr_args['finalize'] = finalize_f if scope: mr_args['scope'] = scope if limit: mr_args['limit'] = limit if output == 'inline' and not queryset._ordering: map_reduce_function = 'inline_map_reduce' else: map_reduce_function = 'map_reduce' if isinstance(output, basestring): mr_args['out'] = output elif isinstance(output, dict): ordered_output = [] for part in ('replace', 'merge', 'reduce'): value = output.get(part) if value: ordered_output.append((part, value)) break else: raise OperationError("actionData not specified for output") db_alias = output.get('db_alias') remaing_args = ['db', 'sharded', 'nonAtomic'] if db_alias: ordered_output.append(('db', get_db(db_alias).name)) del remaing_args[0] for part in remaing_args: value = output.get(part) if value: ordered_output.append((part, value)) mr_args['out'] = SON(ordered_output) results = getattr(queryset._collection, map_reduce_function)( map_f, reduce_f, **mr_args) if map_reduce_function == 'map_reduce': results = results.find() if queryset._ordering: results = results.sort(queryset._ordering) for doc in results: yield MapReduceDocument(queryset._document, queryset._collection, doc['_id'], doc['value']) def exec_js(self, code, *fields, **options): """Execute a Javascript function on the server. A list of fields may be provided, which will be translated to their correct names and supplied as the arguments to the function. A few extra variables are added to the function's scope: ``collection``, which is the name of the collection in use; ``query``, which is an object representing the current query; and ``options``, which is an object containing any options specified as keyword arguments. As fields in MongoEngine may use different names in the database (set using the :attr:`db_field` keyword argument to a :class:`Field` constructor), a mechanism exists for replacing MongoEngine field names with the database field names in Javascript code. When accessing a field, use square-bracket notation, and prefix the MongoEngine field name with a tilde (~). :param code: a string of Javascript code to execute :param fields: fields that you will be using in your function, which will be passed in to your function as arguments :param options: options that you want available to the function (accessed in Javascript through the ``options`` object) """ queryset = self.clone() code = queryset._sub_js_fields(code) fields = [queryset._document._translate_field_name(f) for f in fields] collection = queryset._document._get_collection_name() scope = { 'collection': collection, 'options': options or {}, } query = queryset._query if queryset._where_clause: query['$where'] = queryset._where_clause scope['query'] = query code = Code(code, scope=scope) db = queryset._document._get_db() return db.eval(code, *fields) def where(self, where_clause): """Filter ``QuerySet`` results with a ``$where`` clause (a Javascript expression). Performs automatic field name substitution like :meth:`mongoengine.queryset.Queryset.exec_js`. .. note:: When using this mode of query, the database will call your function, or evaluate your predicate clause, for each object in the collection. .. versionadded:: 0.5 """ queryset = self.clone() where_clause = queryset._sub_js_fields(where_clause) queryset._where_clause = where_clause return queryset def sum(self, field): """Sum over the values of the specified field. :param field: the field to sum over; use dot-notation to refer to embedded document fields .. versionchanged:: 0.5 - updated to map_reduce as db.eval doesnt work with sharding. """ map_func = """ function() { var path = '{{~%(field)s}}'.split('.'), field = this; for (p in path) { if (typeof field != 'undefined') field = field[path[p]]; else break; } if (field && field.constructor == Array) { field.forEach(function(item) { emit(1, item||0); }); } else if (typeof field != 'undefined') { emit(1, field||0); } } """ % dict(field=field) reduce_func = Code(""" function(key, values) { var sum = 0; for (var i in values) { sum += values[i]; } return sum; } """) for result in self.map_reduce(map_func, reduce_func, output='inline'): return result.value else: return 0 def aggregate_sum(self, field): """Sum over the values of the specified field. :param field: the field to sum over; use dot-notation to refer to embedded document fields This method is more performant than the regular `sum`, because it uses the aggregation framework instead of map-reduce. """ result = self._document._get_collection().aggregate([ {'$match': self._query}, {'$group': {'_id': 'sum', 'total': {'$sum': '$' + field}}} ]) if IS_PYMONGO_3: result = list(result) else: result = result.get('result') if result: return result[0]['total'] return 0 def average(self, field): """Average over the values of the specified field. :param field: the field to average over; use dot-notation to refer to embedded document fields .. versionchanged:: 0.5 - updated to map_reduce as db.eval doesnt work with sharding. """ map_func = """ function() { var path = '{{~%(field)s}}'.split('.'), field = this; for (p in path) { if (typeof field != 'undefined') field = field[path[p]]; else break; } if (field && field.constructor == Array) { field.forEach(function(item) { emit(1, {t: item||0, c: 1}); }); } else if (typeof field != 'undefined') { emit(1, {t: field||0, c: 1}); } } """ % dict(field=field) reduce_func = Code(""" function(key, values) { var out = {t: 0, c: 0}; for (var i in values) { var value = values[i]; out.t += value.t; out.c += value.c; } return out; } """) finalize_func = Code(""" function(key, value) { return value.t / value.c; } """) for result in self.map_reduce(map_func, reduce_func, finalize_f=finalize_func, output='inline'): return result.value else: return 0 def aggregate_average(self, field): """Average over the values of the specified field. :param field: the field to average over; use dot-notation to refer to embedded document fields This method is more performant than the regular `average`, because it uses the aggregation framework instead of map-reduce. """ result = self._document._get_collection().aggregate([ {'$match': self._query}, {'$group': {'_id': 'avg', 'total': {'$avg': '$' + field}}} ]) if IS_PYMONGO_3: result = list(result) else: result = result.get('result') if result: return result[0]['total'] return 0 def item_frequencies(self, field, normalize=False, map_reduce=True): """Returns a dictionary of all items present in a field across the whole queried set of documents, and their corresponding frequency. This is useful for generating tag clouds, or searching documents. .. note:: Can only do direct simple mappings and cannot map across :class:`~mongoengine.fields.ReferenceField` or :class:`~mongoengine.fields.GenericReferenceField` for more complex counting a manual map reduce call would is required. If the field is a :class:`~mongoengine.fields.ListField`, the items within each list will be counted individually. :param field: the field to use :param normalize: normalize the results so they add to 1.0 :param map_reduce: Use map_reduce over exec_js .. versionchanged:: 0.5 defaults to map_reduce and can handle embedded document lookups """ if map_reduce: return self._item_frequencies_map_reduce(field, normalize=normalize) return self._item_frequencies_exec_js(field, normalize=normalize) # Iterator helpers def next(self): """Wrap the result in a :class:`~mongoengine.Document` object. """ if self._limit == 0 or self._none: raise StopIteration raw_doc = self._cursor.next() if self._as_pymongo: return self._get_as_pymongo(raw_doc) doc = self._document._from_son(raw_doc, _auto_dereference=self._auto_dereference, only_fields=self.only_fields) if self._scalar: return self._get_scalar(doc) return doc def rewind(self): """Rewind the cursor to its unevaluated state. .. versionadded:: 0.3 """ self._iter = False self._cursor.rewind() # Properties @property def _collection(self): """Property that returns the collection object. This allows us to perform operations only if the collection is accessed. """ return self._collection_obj @property def _cursor_args(self): if not IS_PYMONGO_3: fields_name = 'fields' cursor_args = { 'timeout': self._timeout, 'snapshot': self._snapshot } if self._read_preference is not None: cursor_args['read_preference'] = self._read_preference else: cursor_args['slave_okay'] = self._slave_okay else: fields_name = 'projection' # snapshot is not handled at all by PyMongo 3+ # TODO: evaluate similar possibilities using modifiers if self._snapshot: msg = "The snapshot option is not anymore available with PyMongo 3+" warnings.warn(msg, DeprecationWarning) cursor_args = { 'no_cursor_timeout': self._timeout } if self._loaded_fields: cursor_args[fields_name] = self._loaded_fields.as_dict() if self._search_text: if fields_name not in cursor_args: cursor_args[fields_name] = {} cursor_args[fields_name]['_text_score'] = {'$meta': "textScore"} return cursor_args @property def _cursor(self): if self._cursor_obj is None: # In PyMongo 3+, we define the read preference on a collection # level, not a cursor level. Thus, we need to get a cloned # collection object using `with_options` first. if IS_PYMONGO_3 and self._read_preference is not None: self._cursor_obj = self._collection\ .with_options(read_preference=self._read_preference)\ .find(self._query, **self._cursor_args) else: self._cursor_obj = self._collection.find(self._query, **self._cursor_args) # Apply where clauses to cursor if self._where_clause: where_clause = self._sub_js_fields(self._where_clause) self._cursor_obj.where(where_clause) if self._ordering: # Apply query ordering self._cursor_obj.sort(self._ordering) elif self._ordering is None and self._document._meta['ordering']: # Otherwise, apply the ordering from the document model, unless # it's been explicitly cleared via order_by with no arguments order = self._get_order_by(self._document._meta['ordering']) self._cursor_obj.sort(order) if self._limit is not None: self._cursor_obj.limit(self._limit) if self._skip is not None: self._cursor_obj.skip(self._skip) if self._hint != -1: self._cursor_obj.hint(self._hint) return self._cursor_obj def __deepcopy__(self, memo): """Essential for chained queries with ReferenceFields involved""" return self.clone() @property def _query(self): if self._mongo_query is None: self._mongo_query = self._query_obj.to_query(self._document) if self._class_check and self._initial_query: if "_cls" in self._mongo_query: self._mongo_query = {"$and": [self._initial_query, self._mongo_query]} else: self._mongo_query.update(self._initial_query) return self._mongo_query @property def _dereference(self): if not self.__dereference: self.__dereference = _import_class('DeReference')() return self.__dereference def no_dereference(self): """Turn off any dereferencing for the results of this queryset. """ queryset = self.clone() queryset._auto_dereference = False return queryset # Helper Functions def _item_frequencies_map_reduce(self, field, normalize=False): map_func = """ function() { var path = '{{~%(field)s}}'.split('.'); var field = this; for (p in path) { if (typeof field != 'undefined') field = field[path[p]]; else break; } if (field && field.constructor == Array) { field.forEach(function(item) { emit(item, 1); }); } else if (typeof field != 'undefined') { emit(field, 1); } else { emit(null, 1); } } """ % dict(field=field) reduce_func = """ function(key, values) { var total = 0; var valuesSize = values.length; for (var i=0; i < valuesSize; i++) { total += parseInt(values[i], 10); } return total; } """ values = self.map_reduce(map_func, reduce_func, 'inline') frequencies = {} for f in values: key = f.key if isinstance(key, float): if int(key) == key: key = int(key) frequencies[key] = int(f.value) if normalize: count = sum(frequencies.values()) frequencies = dict([(k, float(v) / count) for k, v in frequencies.items()]) return frequencies def _item_frequencies_exec_js(self, field, normalize=False): """Uses exec_js to execute""" freq_func = """ function(path) { var path = path.split('.'); var total = 0.0; db[collection].find(query).forEach(function(doc) { var field = doc; for (p in path) { if (field) field = field[path[p]]; else break; } if (field && field.constructor == Array) { total += field.length; } else { total++; } }); var frequencies = {}; var types = {}; var inc = 1.0; db[collection].find(query).forEach(function(doc) { field = doc; for (p in path) { if (field) field = field[path[p]]; else break; } if (field && field.constructor == Array) { field.forEach(function(item) { frequencies[item] = inc + (isNaN(frequencies[item]) ? 0: frequencies[item]); }); } else { var item = field; types[item] = item; frequencies[item] = inc + (isNaN(frequencies[item]) ? 0: frequencies[item]); } }); return [total, frequencies, types]; } """ total, data, types = self.exec_js(freq_func, field) values = dict([(types.get(k), int(v)) for k, v in data.iteritems()]) if normalize: values = dict([(k, float(v) / total) for k, v in values.items()]) frequencies = {} for k, v in values.iteritems(): if isinstance(k, float): if int(k) == k: k = int(k) frequencies[k] = v return frequencies def _fields_to_dbfields(self, fields): """Translate fields paths to its db equivalents""" ret = [] subclasses = [] document = self._document if document._meta['allow_inheritance']: subclasses = [get_document(x) for x in document._subclasses][1:] for field in fields: try: field = ".".join(f.db_field for f in document._lookup_field(field.split('.'))) ret.append(field) except LookUpError, err: found = False for subdoc in subclasses: try: subfield = ".".join(f.db_field for f in subdoc._lookup_field(field.split('.'))) ret.append(subfield) found = True break except LookUpError: pass if not found: raise err return ret def _get_order_by(self, keys): """Creates a list of order by fields """ key_list = [] for key in keys: if not key: continue if key == '$text_score': key_list.append(('_text_score', {'$meta': "textScore"})) continue direction = pymongo.ASCENDING if key[0] == '-': direction = pymongo.DESCENDING if key[0] in ('-', '+'): key = key[1:] key = key.replace('__', '.') try: key = self._document._translate_field_name(key) except: pass key_list.append((key, direction)) if self._cursor_obj and key_list: self._cursor_obj.sort(key_list) return key_list def _get_scalar(self, doc): def lookup(obj, name): chunks = name.split('__') for chunk in chunks: obj = getattr(obj, chunk) return obj data = [lookup(doc, n) for n in self._scalar] if len(data) == 1: return data[0] return tuple(data) def _get_as_pymongo(self, row): # Extract which fields paths we should follow if .fields(...) was # used. If not, handle all fields. if not getattr(self, '__as_pymongo_fields', None): self.__as_pymongo_fields = [] for field in self._loaded_fields.fields - set(['_cls']): self.__as_pymongo_fields.append(field) while '.' in field: field, _ = field.rsplit('.', 1) self.__as_pymongo_fields.append(field) all_fields = not self.__as_pymongo_fields def clean(data, path=None): path = path or '' if isinstance(data, dict): new_data = {} for key, value in data.iteritems(): new_path = '%s.%s' % (path, key) if path else key if all_fields: include_field = True elif self._loaded_fields.value == QueryFieldList.ONLY: include_field = new_path in self.__as_pymongo_fields else: include_field = new_path not in self.__as_pymongo_fields if include_field: new_data[key] = clean(value, path=new_path) data = new_data elif isinstance(data, list): data = [clean(d, path=path) for d in data] else: if self._as_pymongo_coerce: # If we need to coerce types, we need to determine the # type of this field and use the corresponding # .to_python(...) from mongoengine.fields import EmbeddedDocumentField obj = self._document for chunk in path.split('.'): obj = getattr(obj, chunk, None) if obj is None: break elif isinstance(obj, EmbeddedDocumentField): obj = obj.document_type if obj and data is not None: data = obj.to_python(data) return data return clean(row) def _sub_js_fields(self, code): """When fields are specified with [~fieldname] syntax, where *fieldname* is the Python name of a field, *fieldname* will be substituted for the MongoDB name of the field (specified using the :attr:`name` keyword argument in a field's constructor). """ def field_sub(match): # Extract just the field name, and look up the field objects field_name = match.group(1).split('.') fields = self._document._lookup_field(field_name) # Substitute the correct name for the field into the javascript return u'["%s"]' % fields[-1].db_field def field_path_sub(match): # Extract just the field name, and look up the field objects field_name = match.group(1).split('.') fields = self._document._lookup_field(field_name) # Substitute the correct name for the field into the javascript return ".".join([f.db_field for f in fields]) code = re.sub(u'\[\s*~([A-z_][A-z_0-9.]+?)\s*\]', field_sub, code) code = re.sub(u'\{\{\s*~([A-z_][A-z_0-9.]+?)\s*\}\}', field_path_sub, code) return code def _chainable_method(self, method_name, val): queryset = self.clone() method = getattr(queryset._cursor, method_name) method(val) setattr(queryset, "_" + method_name, val) return queryset # Deprecated def ensure_index(self, **kwargs): """Deprecated use :func:`Document.ensure_index`""" msg = ("Doc.objects()._ensure_index() is deprecated. " "Use Doc.ensure_index() instead.") warnings.warn(msg, DeprecationWarning) self._document.__class__.ensure_index(**kwargs) return self def _ensure_indexes(self): """Deprecated use :func:`~Document.ensure_indexes`""" msg = ("Doc.objects()._ensure_indexes() is deprecated. " "Use Doc.ensure_indexes() instead.") warnings.warn(msg, DeprecationWarning) self._document.__class__.ensure_indexes() mongoengine-0.10.6/mongoengine/queryset/queryset.py0000664000175000017500000001200412651363712022133 0ustar travistravisfrom mongoengine.errors import OperationError from mongoengine.queryset.base import (BaseQuerySet, DO_NOTHING, NULLIFY, CASCADE, DENY, PULL) __all__ = ('QuerySet', 'QuerySetNoCache', 'DO_NOTHING', 'NULLIFY', 'CASCADE', 'DENY', 'PULL') # The maximum number of items to display in a QuerySet.__repr__ REPR_OUTPUT_SIZE = 20 ITER_CHUNK_SIZE = 100 class QuerySet(BaseQuerySet): """The default queryset, that builds queries and handles a set of results returned from a query. Wraps a MongoDB cursor, providing :class:`~mongoengine.Document` objects as the results. """ _has_more = True _len = None _result_cache = None def __iter__(self): """Iteration utilises a results cache which iterates the cursor in batches of ``ITER_CHUNK_SIZE``. If ``self._has_more`` the cursor hasn't been exhausted so cache then batch. Otherwise iterate the result_cache. """ self._iter = True if self._has_more: return self._iter_results() # iterating over the cache. return iter(self._result_cache) def __len__(self): """Since __len__ is called quite frequently (for example, as part of list(qs) we populate the result cache and cache the length. """ if self._len is not None: return self._len if self._has_more: # populate the cache list(self._iter_results()) self._len = len(self._result_cache) return self._len def __repr__(self): """Provides the string representation of the QuerySet """ if self._iter: return '.. queryset mid-iteration ..' self._populate_cache() data = self._result_cache[:REPR_OUTPUT_SIZE + 1] if len(data) > REPR_OUTPUT_SIZE: data[-1] = "...(remaining elements truncated)..." return repr(data) def _iter_results(self): """A generator for iterating over the result cache. Also populates the cache if there are more possible results to yield. Raises StopIteration when there are no more results""" if self._result_cache is None: self._result_cache = [] pos = 0 while True: upper = len(self._result_cache) while pos < upper: yield self._result_cache[pos] pos += 1 if not self._has_more: raise StopIteration if len(self._result_cache) <= pos: self._populate_cache() def _populate_cache(self): """ Populates the result cache with ``ITER_CHUNK_SIZE`` more entries (until the cursor is exhausted). """ if self._result_cache is None: self._result_cache = [] if self._has_more: try: for i in xrange(ITER_CHUNK_SIZE): self._result_cache.append(self.next()) except StopIteration: self._has_more = False def count(self, with_limit_and_skip=False): """Count the selected elements in the query. :param with_limit_and_skip (optional): take any :meth:`limit` or :meth:`skip` that has been applied to this cursor into account when getting the count """ if with_limit_and_skip is False: return super(QuerySet, self).count(with_limit_and_skip) if self._len is None: self._len = super(QuerySet, self).count(with_limit_and_skip) return self._len def no_cache(self): """Convert to a non_caching queryset .. versionadded:: 0.8.3 Convert to non caching queryset """ if self._result_cache is not None: raise OperationError("QuerySet already cached") return self.clone_into(QuerySetNoCache(self._document, self._collection)) class QuerySetNoCache(BaseQuerySet): """A non caching QuerySet""" def cache(self): """Convert to a caching queryset .. versionadded:: 0.8.3 Convert to caching queryset """ return self.clone_into(QuerySet(self._document, self._collection)) def __repr__(self): """Provides the string representation of the QuerySet .. versionchanged:: 0.6.13 Now doesnt modify the cursor """ if self._iter: return '.. queryset mid-iteration ..' data = [] for i in xrange(REPR_OUTPUT_SIZE + 1): try: data.append(self.next()) except StopIteration: break if len(data) > REPR_OUTPUT_SIZE: data[-1] = "...(remaining elements truncated)..." self.rewind() return repr(data) def __iter__(self): queryset = self if queryset._iter: queryset = self.clone() queryset.rewind() return queryset class QuerySetNoDeRef(QuerySet): """Special no_dereference QuerySet""" def __dereference(items, max_depth=1, instance=None, name=None): return items mongoengine-0.10.6/mongoengine/queryset/transform.py0000664000175000017500000003650612651363712022302 0ustar travistravisfrom collections import defaultdict import pymongo from bson import SON from mongoengine.base.fields import UPDATE_OPERATORS from mongoengine.connection import get_connection from mongoengine.common import _import_class from mongoengine.errors import InvalidQueryError from mongoengine.python_support import IS_PYMONGO_3 __all__ = ('query', 'update') COMPARISON_OPERATORS = ('ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'mod', 'all', 'size', 'exists', 'not', 'elemMatch', 'type') GEO_OPERATORS = ('within_distance', 'within_spherical_distance', 'within_box', 'within_polygon', 'near', 'near_sphere', 'max_distance', 'min_distance', 'geo_within', 'geo_within_box', 'geo_within_polygon', 'geo_within_center', 'geo_within_sphere', 'geo_intersects') STRING_OPERATORS = ('contains', 'icontains', 'startswith', 'istartswith', 'endswith', 'iendswith', 'exact', 'iexact') CUSTOM_OPERATORS = ('match',) MATCH_OPERATORS = (COMPARISON_OPERATORS + GEO_OPERATORS + STRING_OPERATORS + CUSTOM_OPERATORS) def query(_doc_cls=None, **kwargs): """Transform a query from Django-style format to Mongo format. """ mongo_query = {} merge_query = defaultdict(list) for key, value in sorted(kwargs.items()): if key == "__raw__": mongo_query.update(value) continue parts = key.rsplit('__') indices = [(i, p) for i, p in enumerate(parts) if p.isdigit()] parts = [part for part in parts if not part.isdigit()] # Check for an operator and transform to mongo-style if there is op = None if len(parts) > 1 and parts[-1] in MATCH_OPERATORS: op = parts.pop() # Allw to escape operator-like field name by __ if len(parts) > 1 and parts[-1] == "": parts.pop() negate = False if len(parts) > 1 and parts[-1] == 'not': parts.pop() negate = True if _doc_cls: # Switch field names to proper names [set in Field(name='foo')] try: fields = _doc_cls._lookup_field(parts) except Exception, e: raise InvalidQueryError(e) parts = [] CachedReferenceField = _import_class('CachedReferenceField') cleaned_fields = [] for field in fields: append_field = True if isinstance(field, basestring): parts.append(field) append_field = False # is last and CachedReferenceField elif isinstance(field, CachedReferenceField) and fields[-1] == field: parts.append('%s._id' % field.db_field) else: parts.append(field.db_field) if append_field: cleaned_fields.append(field) # Convert value to proper value field = cleaned_fields[-1] singular_ops = [None, 'ne', 'gt', 'gte', 'lt', 'lte', 'not'] singular_ops += STRING_OPERATORS if op in singular_ops: if isinstance(field, basestring): if (op in STRING_OPERATORS and isinstance(value, basestring)): StringField = _import_class('StringField') value = StringField.prepare_query_value(op, value) else: value = field else: value = field.prepare_query_value(op, value) if isinstance(field, CachedReferenceField) and value: value = value['_id'] elif op in ('in', 'nin', 'all', 'near') and not isinstance(value, dict): # 'in', 'nin' and 'all' require a list of values value = [field.prepare_query_value(op, v) for v in value] # if op and op not in COMPARISON_OPERATORS: if op: if op in GEO_OPERATORS: value = _geo_operator(field, op, value) elif op in ('match', 'elemMatch'): ListField = _import_class('ListField') EmbeddedDocumentField = _import_class('EmbeddedDocumentField') if (isinstance(value, dict) and isinstance(field, ListField) and isinstance(field.field, EmbeddedDocumentField)): value = query(field.field.document_type, **value) else: value = field.prepare_query_value(op, value) value = {"$elemMatch": value} elif op in CUSTOM_OPERATORS: NotImplementedError("Custom method '%s' has not " "been implemented" % op) elif op not in STRING_OPERATORS: value = {'$' + op: value} if negate: value = {'$not': value} for i, part in indices: parts.insert(i, part) key = '.'.join(parts) if op is None or key not in mongo_query: mongo_query[key] = value elif key in mongo_query: if key in mongo_query and isinstance(mongo_query[key], dict): mongo_query[key].update(value) # $max/minDistance needs to come last - convert to SON value_dict = mongo_query[key] if ('$maxDistance' in value_dict or '$minDistance' in value_dict) and \ ('$near' in value_dict or '$nearSphere' in value_dict): value_son = SON() for k, v in value_dict.iteritems(): if k == '$maxDistance' or k == '$minDistance': continue value_son[k] = v # Required for MongoDB >= 2.6, may fail when combining # PyMongo 3+ and MongoDB < 2.6 near_embedded = False for near_op in ('$near', '$nearSphere'): if isinstance(value_dict.get(near_op), dict) and ( IS_PYMONGO_3 or get_connection().max_wire_version > 1): value_son[near_op] = SON(value_son[near_op]) if '$maxDistance' in value_dict: value_son[near_op][ '$maxDistance'] = value_dict['$maxDistance'] if '$minDistance' in value_dict: value_son[near_op][ '$minDistance'] = value_dict['$minDistance'] near_embedded = True if not near_embedded: if '$maxDistance' in value_dict: value_son['$maxDistance'] = value_dict['$maxDistance'] if '$minDistance' in value_dict: value_son['$minDistance'] = value_dict['$minDistance'] mongo_query[key] = value_son else: # Store for manually merging later merge_query[key].append(value) # The queryset has been filter in such a way we must manually merge for k, v in merge_query.items(): merge_query[k].append(mongo_query[k]) del mongo_query[k] if isinstance(v, list): value = [{k: val} for val in v] if '$and' in mongo_query.keys(): mongo_query['$and'].extend(value) else: mongo_query['$and'] = value return mongo_query def update(_doc_cls=None, **update): """Transform an update spec from Django-style format to Mongo format. """ mongo_update = {} for key, value in update.items(): if key == "__raw__": mongo_update.update(value) continue parts = key.split('__') # if there is no operator, default to "set" if len(parts) < 3 and parts[0] not in UPDATE_OPERATORS: parts.insert(0, 'set') # Check for an operator and transform to mongo-style if there is op = None if parts[0] in UPDATE_OPERATORS: op = parts.pop(0) # Convert Pythonic names to Mongo equivalents if op in ('push_all', 'pull_all'): op = op.replace('_all', 'All') elif op == 'dec': # Support decrement by flipping a positive value's sign # and using 'inc' op = 'inc' if value > 0: value = -value elif op == 'add_to_set': op = 'addToSet' elif op == 'set_on_insert': op = "setOnInsert" match = None if parts[-1] in COMPARISON_OPERATORS: match = parts.pop() if _doc_cls: # Switch field names to proper names [set in Field(name='foo')] try: fields = _doc_cls._lookup_field(parts) except Exception, e: raise InvalidQueryError(e) parts = [] cleaned_fields = [] appended_sub_field = False for field in fields: append_field = True if isinstance(field, basestring): # Convert the S operator to $ if field == 'S': field = '$' parts.append(field) append_field = False else: parts.append(field.db_field) if append_field: appended_sub_field = False cleaned_fields.append(field) if hasattr(field, 'field'): cleaned_fields.append(field.field) appended_sub_field = True # Convert value to proper value if appended_sub_field: field = cleaned_fields[-2] else: field = cleaned_fields[-1] GeoJsonBaseField = _import_class("GeoJsonBaseField") if isinstance(field, GeoJsonBaseField): value = field.to_mongo(value) if op in (None, 'set', 'push', 'pull'): if field.required or value is not None: value = field.prepare_query_value(op, value) elif op in ('pushAll', 'pullAll'): value = [field.prepare_query_value(op, v) for v in value] elif op in ('addToSet', 'setOnInsert'): if isinstance(value, (list, tuple, set)): value = [field.prepare_query_value(op, v) for v in value] elif field.required or value is not None: value = field.prepare_query_value(op, value) elif op == "unset": value = 1 if match: match = '$' + match value = {match: value} key = '.'.join(parts) if not op: raise InvalidQueryError("Updates must supply an operation " "eg: set__FIELD=value") if 'pull' in op and '.' in key: # Dot operators don't work on pull operations # unless they point to a list field # Otherwise it uses nested dict syntax if op == 'pullAll': raise InvalidQueryError("pullAll operations only support " "a single field depth") # Look for the last list field and use dot notation until there field_classes = [c.__class__ for c in cleaned_fields] field_classes.reverse() ListField = _import_class('ListField') if ListField in field_classes: # Join all fields via dot notation to the last ListField # Then process as normal last_listField = len( cleaned_fields) - field_classes.index(ListField) key = ".".join(parts[:last_listField]) parts = parts[last_listField:] parts.insert(0, key) parts.reverse() for key in parts: value = {key: value} elif op == 'addToSet' and isinstance(value, list): value = {key: {"$each": value}} else: value = {key: value} key = '$' + op if key not in mongo_update: mongo_update[key] = value elif key in mongo_update and isinstance(mongo_update[key], dict): mongo_update[key].update(value) return mongo_update def _geo_operator(field, op, value): """Helper to return the query for a given geo query""" if op == "max_distance": value = {'$maxDistance': value} elif op == "min_distance": value = {'$minDistance': value} elif field._geo_index == pymongo.GEO2D: if op == "within_distance": value = {'$within': {'$center': value}} elif op == "within_spherical_distance": value = {'$within': {'$centerSphere': value}} elif op == "within_polygon": value = {'$within': {'$polygon': value}} elif op == "near": value = {'$near': value} elif op == "near_sphere": value = {'$nearSphere': value} elif op == 'within_box': value = {'$within': {'$box': value}} else: raise NotImplementedError("Geo method '%s' has not " "been implemented for a GeoPointField" % op) else: if op == "geo_within": value = {"$geoWithin": _infer_geometry(value)} elif op == "geo_within_box": value = {"$geoWithin": {"$box": value}} elif op == "geo_within_polygon": value = {"$geoWithin": {"$polygon": value}} elif op == "geo_within_center": value = {"$geoWithin": {"$center": value}} elif op == "geo_within_sphere": value = {"$geoWithin": {"$centerSphere": value}} elif op == "geo_intersects": value = {"$geoIntersects": _infer_geometry(value)} elif op == "near": value = {'$near': _infer_geometry(value)} else: raise NotImplementedError("Geo method '%s' has not " "been implemented for a %s " % (op, field._name)) return value def _infer_geometry(value): """Helper method that tries to infer the $geometry shape for a given value""" if isinstance(value, dict): if "$geometry" in value: return value elif 'coordinates' in value and 'type' in value: return {"$geometry": value} raise InvalidQueryError("Invalid $geometry dictionary should have " "type and coordinates keys") elif isinstance(value, (list, set)): # TODO: shouldn't we test value[0][0][0][0] to see if it is MultiPolygon? try: value[0][0][0] return {"$geometry": {"type": "Polygon", "coordinates": value}} except: pass try: value[0][0] return {"$geometry": {"type": "LineString", "coordinates": value}} except: pass try: value[0] return {"$geometry": {"type": "Point", "coordinates": value}} except: pass raise InvalidQueryError("Invalid $geometry data. Can be either a dictionary " "or (nested) lists of coordinate(s)") mongoengine-0.10.6/mongoengine/queryset/manager.py0000664000175000017500000000426612651363712021677 0ustar travistravisfrom functools import partial from mongoengine.queryset.queryset import QuerySet __all__ = ('queryset_manager', 'QuerySetManager') class QuerySetManager(object): """ The default QuerySet Manager. Custom QuerySet Manager functions can extend this class and users can add extra queryset functionality. Any custom manager methods must accept a :class:`~mongoengine.Document` class as its first argument, and a :class:`~mongoengine.queryset.QuerySet` as its second argument. The method function should return a :class:`~mongoengine.queryset.QuerySet` , probably the same one that was passed in, but modified in some way. """ get_queryset = None default = QuerySet def __init__(self, queryset_func=None): if queryset_func: self.get_queryset = queryset_func def __get__(self, instance, owner): """Descriptor for instantiating a new QuerySet object when Document.objects is accessed. """ if instance is not None: # Document class being used rather than a document object return self # owner is the document that contains the QuerySetManager queryset_class = owner._meta.get('queryset_class', self.default) queryset = queryset_class(owner, owner._get_collection()) if self.get_queryset: arg_count = self.get_queryset.func_code.co_argcount if arg_count == 1: queryset = self.get_queryset(queryset) elif arg_count == 2: queryset = self.get_queryset(owner, queryset) else: queryset = partial(self.get_queryset, owner, queryset) return queryset def queryset_manager(func): """Decorator that allows you to define custom QuerySet managers on :class:`~mongoengine.Document` classes. The manager must be a function that accepts a :class:`~mongoengine.Document` class as its first argument, and a :class:`~mongoengine.queryset.QuerySet` as its second argument. The method function should return a :class:`~mongoengine.queryset.QuerySet`, probably the same one that was passed in, but modified in some way. """ return QuerySetManager(func) mongoengine-0.10.6/mongoengine/context_managers.py0000664000175000017500000001532712651363712021745 0ustar travistravisfrom mongoengine.common import _import_class from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db __all__ = ("switch_db", "switch_collection", "no_dereference", "no_sub_classes", "query_counter") class switch_db(object): """ switch_db alias context manager. Example :: # Register connections register_connection('default', 'mongoenginetest') register_connection('testdb-1', 'mongoenginetest2') class Group(Document): name = StringField() Group(name="test").save() # Saves in the default db with switch_db(Group, 'testdb-1') as Group: Group(name="hello testdb!").save() # Saves in testdb-1 """ def __init__(self, cls, db_alias): """ Construct the switch_db context manager :param cls: the class to change the registered db :param db_alias: the name of the specific database to use """ self.cls = cls self.collection = cls._get_collection() self.db_alias = db_alias self.ori_db_alias = cls._meta.get("db_alias", DEFAULT_CONNECTION_NAME) def __enter__(self): """ change the db_alias and clear the cached collection """ self.cls._meta["db_alias"] = self.db_alias self.cls._collection = None return self.cls def __exit__(self, t, value, traceback): """ Reset the db_alias and collection """ self.cls._meta["db_alias"] = self.ori_db_alias self.cls._collection = self.collection class switch_collection(object): """ switch_collection alias context manager. Example :: class Group(Document): name = StringField() Group(name="test").save() # Saves in the default db with switch_collection(Group, 'group1') as Group: Group(name="hello testdb!").save() # Saves in group1 collection """ def __init__(self, cls, collection_name): """ Construct the switch_collection context manager :param cls: the class to change the registered db :param collection_name: the name of the collection to use """ self.cls = cls self.ori_collection = cls._get_collection() self.ori_get_collection_name = cls._get_collection_name self.collection_name = collection_name def __enter__(self): """ change the _get_collection_name and clear the cached collection """ @classmethod def _get_collection_name(cls): return self.collection_name self.cls._get_collection_name = _get_collection_name self.cls._collection = None return self.cls def __exit__(self, t, value, traceback): """ Reset the collection """ self.cls._collection = self.ori_collection self.cls._get_collection_name = self.ori_get_collection_name class no_dereference(object): """ no_dereference context manager. Turns off all dereferencing in Documents for the duration of the context manager:: with no_dereference(Group) as Group: Group.objects.find() """ def __init__(self, cls): """ Construct the no_dereference context manager. :param cls: the class to turn dereferencing off on """ self.cls = cls ReferenceField = _import_class('ReferenceField') GenericReferenceField = _import_class('GenericReferenceField') ComplexBaseField = _import_class('ComplexBaseField') self.deref_fields = [k for k, v in self.cls._fields.iteritems() if isinstance(v, (ReferenceField, GenericReferenceField, ComplexBaseField))] def __enter__(self): """ change the objects default and _auto_dereference values""" for field in self.deref_fields: self.cls._fields[field]._auto_dereference = False return self.cls def __exit__(self, t, value, traceback): """ Reset the default and _auto_dereference values""" for field in self.deref_fields: self.cls._fields[field]._auto_dereference = True return self.cls class no_sub_classes(object): """ no_sub_classes context manager. Only returns instances of this class and no sub (inherited) classes:: with no_sub_classes(Group) as Group: Group.objects.find() """ def __init__(self, cls): """ Construct the no_sub_classes context manager. :param cls: the class to turn querying sub classes on """ self.cls = cls def __enter__(self): """ change the objects default and _auto_dereference values""" self.cls._all_subclasses = self.cls._subclasses self.cls._subclasses = (self.cls,) return self.cls def __exit__(self, t, value, traceback): """ Reset the default and _auto_dereference values""" self.cls._subclasses = self.cls._all_subclasses delattr(self.cls, '_all_subclasses') return self.cls class query_counter(object): """ Query_counter context manager to get the number of queries. """ def __init__(self): """ Construct the query_counter. """ self.counter = 0 self.db = get_db() def __enter__(self): """ On every with block we need to drop the profile collection. """ self.db.set_profiling_level(0) self.db.system.profile.drop() self.db.set_profiling_level(2) return self def __exit__(self, t, value, traceback): """ Reset the profiling level. """ self.db.set_profiling_level(0) def __eq__(self, value): """ == Compare querycounter. """ counter = self._get_count() return value == counter def __ne__(self, value): """ != Compare querycounter. """ return not self.__eq__(value) def __lt__(self, value): """ < Compare querycounter. """ return self._get_count() < value def __le__(self, value): """ <= Compare querycounter. """ return self._get_count() <= value def __gt__(self, value): """ > Compare querycounter. """ return self._get_count() > value def __ge__(self, value): """ >= Compare querycounter. """ return self._get_count() >= value def __int__(self): """ int representation. """ return self._get_count() def __repr__(self): """ repr query_counter as the number of queries. """ return u"%s" % self._get_count() def _get_count(self): """ Get the number of queries. """ ignore_query = {"ns": {"$ne": "%s.system.indexes" % self.db.name}} count = self.db.system.profile.find(ignore_query).count() - self.counter self.counter += 1 return count mongoengine-0.10.6/mongoengine/common.py0000664000175000017500000000400612651363712017664 0ustar travistravis_class_registry_cache = {} _field_list_cache = [] def _import_class(cls_name): """Cache mechanism for imports. Due to complications of circular imports mongoengine needs to do lots of inline imports in functions. This is inefficient as classes are imported repeated throughout the mongoengine code. This is compounded by some recursive functions requiring inline imports. :mod:`mongoengine.common` provides a single point to import all these classes. Circular imports aren't an issue as it dynamically imports the class when first needed. Subsequent calls to the :func:`~mongoengine.common._import_class` can then directly retrieve the class from the :data:`mongoengine.common._class_registry_cache`. """ if cls_name in _class_registry_cache: return _class_registry_cache.get(cls_name) doc_classes = ('Document', 'DynamicEmbeddedDocument', 'EmbeddedDocument', 'MapReduceDocument') # Field Classes if not _field_list_cache: from mongoengine.fields import __all__ as fields _field_list_cache.extend(fields) from mongoengine.base.fields import __all__ as fields _field_list_cache.extend(fields) field_classes = _field_list_cache queryset_classes = ('OperationError',) deref_classes = ('DeReference',) if cls_name in doc_classes: from mongoengine import document as module import_classes = doc_classes elif cls_name in field_classes: from mongoengine import fields as module import_classes = field_classes elif cls_name in queryset_classes: from mongoengine import queryset as module import_classes = queryset_classes elif cls_name in deref_classes: from mongoengine import dereference as module import_classes = deref_classes else: raise ValueError('No import set for: ' % cls_name) for cls in import_classes: _class_registry_cache[cls] = getattr(module, cls) return _class_registry_cache.get(cls_name) mongoengine-0.10.6/mongoengine/python_support.py0000664000175000017500000000134512651363712021514 0ustar travistravis"""Helper functions and types to aid with Python 2.5 - 3 support.""" import sys import pymongo if pymongo.version_tuple[0] < 3: IS_PYMONGO_3 = False else: IS_PYMONGO_3 = True PY3 = sys.version_info[0] == 3 if PY3: import codecs from io import BytesIO as StringIO # return s converted to binary. b('test') should be equivalent to b'test' def b(s): return codecs.latin_1_encode(s)[0] bin_type = bytes txt_type = str else: try: from cStringIO import StringIO except ImportError: from StringIO import StringIO # Conversion to binary only necessary in Python 3 def b(s): return s bin_type = str txt_type = unicode str_types = (bin_type, txt_type) mongoengine-0.10.6/setup.cfg0000664000175000017500000000030312651364023015326 0ustar travistravis[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [nosetests] tests = tests cover-erase = 1 cover-package = mongoengine verbosity = 2 cover-branches = 1 detailed-errors = 1 rednose = 1 mongoengine-0.10.6/README.rst0000664000175000017500000001102012651363712015176 0ustar travistravis=========== MongoEngine =========== :Info: MongoEngine is an ORM-like layer on top of PyMongo. :Repository: https://github.com/MongoEngine/mongoengine :Author: Harry Marr (http://github.com/hmarr) :Maintainer: Ross Lawley (http://github.com/rozza) .. image:: https://secure.travis-ci.org/MongoEngine/mongoengine.png?branch=master :target: http://travis-ci.org/MongoEngine/mongoengine .. image:: https://coveralls.io/repos/MongoEngine/mongoengine/badge.png?branch=master :target: https://coveralls.io/r/MongoEngine/mongoengine?branch=master .. image:: https://landscape.io/github/MongoEngine/mongoengine/master/landscape.png :target: https://landscape.io/github/MongoEngine/mongoengine/master :alt: Code Health About ===== MongoEngine is a Python Object-Document Mapper for working with MongoDB. Documentation available at http://mongoengine-odm.rtfd.org - there is currently a `tutorial `_, a `user guide `_ and an `API reference `_. Installation ============ We recommend the use of `virtualenv `_ and of `pip `_. You can then use ``pip install -U mongoengine``. You may also have `setuptools `_ and thus you can use ``easy_install -U mongoengine``. Otherwise, you can download the source from `GitHub `_ and run ``python setup.py install``. Dependencies ============ - pymongo>=2.7.1 - sphinx (optional - for documentation generation) Optional Dependencies --------------------- - **Image Fields**: Pillow>=2.0.0 - dateutil>=2.1.0 .. note MongoEngine always runs it's test suite against the latest patch version of each dependecy. e.g.: PyMongo 3.0.1 Examples ======== Some simple examples of what MongoEngine code looks like: .. code :: python class BlogPost(Document): title = StringField(required=True, max_length=200) posted = DateTimeField(default=datetime.datetime.now) tags = ListField(StringField(max_length=50)) class TextPost(BlogPost): content = StringField(required=True) class LinkPost(BlogPost): url = StringField(required=True) # Create a text-based post >>> post1 = TextPost(title='Using MongoEngine', content='See the tutorial') >>> post1.tags = ['mongodb', 'mongoengine'] >>> post1.save() # Create a link-based post >>> post2 = LinkPost(title='MongoEngine Docs', url='hmarr.com/mongoengine') >>> post2.tags = ['mongoengine', 'documentation'] >>> post2.save() # Iterate over all posts using the BlogPost superclass >>> for post in BlogPost.objects: ... print '===', post.title, '===' ... if isinstance(post, TextPost): ... print post.content ... elif isinstance(post, LinkPost): ... print 'Link:', post.url ... print ... >>> len(BlogPost.objects) 2 >>> len(TextPost.objects) 1 >>> len(LinkPost.objects) 1 # Find tagged posts >>> len(BlogPost.objects(tags='mongoengine')) 2 >>> len(BlogPost.objects(tags='mongodb')) 1 Tests ===== To run the test suite, ensure you are running a local instance of MongoDB on the standard port, and run: ``python setup.py nosetests``. To run the test suite on every supported Python version and every supported PyMongo version, you can use ``tox``. tox and each supported Python version should be installed in your environment: .. code-block:: shell # Install tox $ pip install tox # Run the test suites $ tox If you wish to run one single or selected tests, use the nosetest convention. It will find the folder, eventually the file, go to the TestClass specified after the colon and eventually right to the single test. Also use the -s argument if you want to print out whatever or access pdb while testing. .. code-block:: shell $ python setup.py nosetests --tests tests/fields/fields.py:FieldTest.test_cls_field -s Community ========= - `MongoEngine Users mailing list `_ - `MongoEngine Developers mailing list `_ - `#mongoengine IRC channel `_ Contributing ============ We welcome contributions! see the `Contribution guidelines `_ mongoengine-0.10.6/PKG-INFO0000664000175000017500000001531012651364023014606 0ustar travistravisMetadata-Version: 1.0 Name: mongoengine Version: 0.10.6 Summary: MongoEngine is a Python Object-Document Mapper for working with MongoDB. Home-page: http://mongoengine.org/ Author: Ross Lawley Author-email: ross.lawley@{nospam}gmail.com License: MIT Download-URL: https://github.com/MongoEngine/mongoengine/tarball/master Description: =========== MongoEngine =========== :Info: MongoEngine is an ORM-like layer on top of PyMongo. :Repository: https://github.com/MongoEngine/mongoengine :Author: Harry Marr (http://github.com/hmarr) :Maintainer: Ross Lawley (http://github.com/rozza) .. image:: https://secure.travis-ci.org/MongoEngine/mongoengine.png?branch=master :target: http://travis-ci.org/MongoEngine/mongoengine .. image:: https://coveralls.io/repos/MongoEngine/mongoengine/badge.png?branch=master :target: https://coveralls.io/r/MongoEngine/mongoengine?branch=master .. image:: https://landscape.io/github/MongoEngine/mongoengine/master/landscape.png :target: https://landscape.io/github/MongoEngine/mongoengine/master :alt: Code Health About ===== MongoEngine is a Python Object-Document Mapper for working with MongoDB. Documentation available at http://mongoengine-odm.rtfd.org - there is currently a `tutorial `_, a `user guide `_ and an `API reference `_. Installation ============ We recommend the use of `virtualenv `_ and of `pip `_. You can then use ``pip install -U mongoengine``. You may also have `setuptools `_ and thus you can use ``easy_install -U mongoengine``. Otherwise, you can download the source from `GitHub `_ and run ``python setup.py install``. Dependencies ============ - pymongo>=2.7.1 - sphinx (optional - for documentation generation) Optional Dependencies --------------------- - **Image Fields**: Pillow>=2.0.0 - dateutil>=2.1.0 .. note MongoEngine always runs it's test suite against the latest patch version of each dependecy. e.g.: PyMongo 3.0.1 Examples ======== Some simple examples of what MongoEngine code looks like: .. code :: python class BlogPost(Document): title = StringField(required=True, max_length=200) posted = DateTimeField(default=datetime.datetime.now) tags = ListField(StringField(max_length=50)) class TextPost(BlogPost): content = StringField(required=True) class LinkPost(BlogPost): url = StringField(required=True) # Create a text-based post >>> post1 = TextPost(title='Using MongoEngine', content='See the tutorial') >>> post1.tags = ['mongodb', 'mongoengine'] >>> post1.save() # Create a link-based post >>> post2 = LinkPost(title='MongoEngine Docs', url='hmarr.com/mongoengine') >>> post2.tags = ['mongoengine', 'documentation'] >>> post2.save() # Iterate over all posts using the BlogPost superclass >>> for post in BlogPost.objects: ... print '===', post.title, '===' ... if isinstance(post, TextPost): ... print post.content ... elif isinstance(post, LinkPost): ... print 'Link:', post.url ... print ... >>> len(BlogPost.objects) 2 >>> len(TextPost.objects) 1 >>> len(LinkPost.objects) 1 # Find tagged posts >>> len(BlogPost.objects(tags='mongoengine')) 2 >>> len(BlogPost.objects(tags='mongodb')) 1 Tests ===== To run the test suite, ensure you are running a local instance of MongoDB on the standard port, and run: ``python setup.py nosetests``. To run the test suite on every supported Python version and every supported PyMongo version, you can use ``tox``. tox and each supported Python version should be installed in your environment: .. code-block:: shell # Install tox $ pip install tox # Run the test suites $ tox If you wish to run one single or selected tests, use the nosetest convention. It will find the folder, eventually the file, go to the TestClass specified after the colon and eventually right to the single test. Also use the -s argument if you want to print out whatever or access pdb while testing. .. code-block:: shell $ python setup.py nosetests --tests tests/fields/fields.py:FieldTest.test_cls_field -s Community ========= - `MongoEngine Users mailing list `_ - `MongoEngine Developers mailing list `_ - `#mongoengine IRC channel `_ Contributing ============ We welcome contributions! see the `Contribution guidelines `_ Platform: any Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Database Classifier: Topic :: Software Development :: Libraries :: Python Modules mongoengine-0.10.6/MANIFEST.in0000664000175000017500000000016212651363712015252 0ustar travistravisinclude MANIFEST.in include README.rst include LICENSE include AUTHORS recursive-include docs * prune docs/_build mongoengine-0.10.6/setup.py0000664000175000017500000000577512651363712015245 0ustar travistravisimport os import sys from setuptools import setup, find_packages # Hack to silence atexit traceback in newer python versions try: import multiprocessing except ImportError: pass DESCRIPTION = 'MongoEngine is a Python Object-Document ' + \ 'Mapper for working with MongoDB.' LONG_DESCRIPTION = None try: LONG_DESCRIPTION = open('README.rst').read() except: pass def get_version(version_tuple): if not isinstance(version_tuple[-1], int): return '.'.join(map(str, version_tuple[:-1])) + version_tuple[-1] return '.'.join(map(str, version_tuple)) # Dirty hack to get version number from monogengine/__init__.py - we can't # import it as it depends on PyMongo and PyMongo isn't installed until this # file is read init = os.path.join(os.path.dirname(__file__), 'mongoengine', '__init__.py') version_line = list(filter(lambda l: l.startswith('VERSION'), open(init)))[0] VERSION = get_version(eval(version_line.split('=')[-1])) CLASSIFIERS = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", 'Topic :: Database', 'Topic :: Software Development :: Libraries :: Python Modules', ] extra_opts = {"packages": find_packages(exclude=["tests", "tests.*"])} if sys.version_info[0] == 3: extra_opts['use_2to3'] = True extra_opts['tests_require'] = ['nose', 'rednose', 'coverage==3.7.1', 'blinker', 'Pillow>=2.0.0'] if "test" in sys.argv or "nosetests" in sys.argv: extra_opts['packages'] = find_packages() extra_opts['package_data'] = {"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]} else: # coverage 4 does not support Python 3.2 anymore extra_opts['tests_require'] = ['nose', 'rednose', 'coverage==3.7.1', 'blinker', 'Pillow>=2.0.0', 'python-dateutil'] if sys.version_info[0] == 2 and sys.version_info[1] == 6: extra_opts['tests_require'].append('unittest2') setup(name='mongoengine', version=VERSION, author='Harry Marr', author_email='harry.marr@{nospam}gmail.com', maintainer="Ross Lawley", maintainer_email="ross.lawley@{nospam}gmail.com", url='http://mongoengine.org/', download_url='https://github.com/MongoEngine/mongoengine/tarball/master', license='MIT', include_package_data=True, description=DESCRIPTION, long_description=LONG_DESCRIPTION, platforms=['any'], classifiers=CLASSIFIERS, install_requires=['pymongo>=2.7.1'], test_suite='nose.collector', **extra_opts )