pax_global_header00006660000000000000000000000064152704027160014516gustar00rootroot0000000000000052 comment=1032c50d39ef5e737b99a42870d60bf8e2e34c39 django-commons-django-constance-1032c50/000077500000000000000000000000001527040271600201035ustar00rootroot00000000000000django-commons-django-constance-1032c50/.coveragerc000066400000000000000000000002141527040271600222210ustar00rootroot00000000000000[run] source = constance branch = 1 omit = */pytest.py */tests/* [report] omit = *tests*,*migrations*,.tox/*,setup.py,*settings.py django-commons-django-constance-1032c50/.github/000077500000000000000000000000001527040271600214435ustar00rootroot00000000000000django-commons-django-constance-1032c50/.github/ISSUE_TEMPLATE.md000066400000000000000000000003241527040271600241470ustar00rootroot00000000000000### Describe the problem Tell us about the problem you're having. ### Steps to reproduce Tell us how to reproduce it. ### System configuration * Django version: * Python version: * Django-Constance version: django-commons-django-constance-1032c50/.github/dependabot.yml000066400000000000000000000011161527040271600242720ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" commit-message: prefix: "chore(ci): " groups: github-actions: patterns: - "*" open-pull-requests-limit: 1 django-commons-django-constance-1032c50/.github/release.yml000066400000000000000000000003731527040271600236110ustar00rootroot00000000000000# Configures the automatically generated release notes: # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes changelog: exclude: authors: - dependabot[bot] - pre-commit-ci[bot] django-commons-django-constance-1032c50/.github/workflows/000077500000000000000000000000001527040271600235005ustar00rootroot00000000000000django-commons-django-constance-1032c50/.github/workflows/docs.yml000066400000000000000000000007431527040271600251570ustar00rootroot00000000000000name: Docs on: [push, pull_request] jobs: docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v7 with: python-version: '3.12' cache: 'pip' cache-dependency-path: 'docs/requirements.txt' - name: Install dependencies run: pip install -r docs/requirements.txt - name: Build docs run: | cd docs make html django-commons-django-constance-1032c50/.github/workflows/release.yml000066400000000000000000000043051527040271600256450ustar00rootroot00000000000000name: Release on: push: tags: - '*' env: PYPI_URL: https://pypi.org/p/django-constance permissions: {} jobs: build: name: Build distribution if: github.repository == 'django-commons/django-constance' runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@v7 with: fetch-depth: 0 persist-credentials: false - name: Set up Python uses: actions/setup-python@v7 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install -U pip python -m pip install -U build twine - name: Build package run: | python -m build twine check dist/* - name: Store the distribution packages uses: actions/upload-artifact@v7 with: name: python-package-distributions path: dist/ publish-to-pypi: name: Publish distribution to PyPI needs: - build runs-on: ubuntu-latest environment: name: pypi url: ${{ env.PYPI_URL }} permissions: id-token: write steps: - name: Download all the dists uses: actions/download-artifact@v8 with: name: python-package-distributions path: dist/ - name: Publish distribution to PyPI uses: pypa/gh-action-pypi-publish@v1.14.2 github-release: name: Upload distribution to GitHub Release needs: - publish-to-pypi runs-on: ubuntu-latest permissions: contents: write steps: - name: Download all the dists uses: actions/download-artifact@v8 with: name: python-package-distributions path: dist/ - name: Create GitHub Release env: GITHUB_TOKEN: ${{ github.token }} run: >- gh release create "${GITHUB_REF_NAME}" --repo '${{ github.repository }}' --draft --generate-notes - name: Upload artifact to GitHub Release env: GITHUB_TOKEN: ${{ github.token }} run: >- gh release upload "${GITHUB_REF_NAME}" dist/** --repo '${{ github.repository }}' django-commons-django-constance-1032c50/.github/workflows/test.yml000066400000000000000000000022351527040271600252040ustar00rootroot00000000000000name: Test on: [push, pull_request] jobs: ruff-format: runs-on: ubuntu-latest timeout-minutes: 1 steps: - uses: actions/checkout@v7 - uses: chartboost/ruff-action@v1 with: version: 0.5.0 args: 'format --check' ruff-lint: runs-on: ubuntu-latest timeout-minutes: 1 steps: - uses: actions/checkout@v7 - uses: chartboost/ruff-action@v1 with: version: 0.5.0 build: runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 5 matrix: python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] steps: - uses: actions/checkout@v7 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade tox tox-gh-actions - name: Tox tests run: | tox -v - name: Upload coverage uses: codecov/codecov-action@v7 with: name: Python ${{ matrix.python-version }} django-commons-django-constance-1032c50/.gitignore000066400000000000000000000002011527040271600220640ustar00rootroot00000000000000.project .pydevproject *.pyc *.egg-info build/ dist/ test.db .tox .coverage coverage.xml docs/_build .idea constance/_version.py django-commons-django-constance-1032c50/.pre-commit-config.yaml000066400000000000000000000007111527040271600243630ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: python-check-blanket-noqa - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: check-merge-conflict - id: check-yaml - repo: https://github.com/asottile/pyupgrade rev: v3.21.2 hooks: - id: pyupgrade args: [ --py38-plus ] exclude: /migrations/ ci: autoupdate_schedule: quarterly django-commons-django-constance-1032c50/.readthedocs.yaml000066400000000000000000000004741527040271600233370ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details version: 2 build: os: ubuntu-lts-latest tools: python: "3.12" sphinx: configuration: docs/conf.py python: install: - requirements: docs/requirements.txt - method: pip path: . django-commons-django-constance-1032c50/AUTHORS000066400000000000000000000032541527040271600211570ustar00rootroot00000000000000Ales Zoulek Alexander Frenzel Alexandr Artemyev Bouke Haarsma Camilo Nova Charlie Hornsby Curtis Maloney Dan Poirier David Burke Dmitriy Tatarkin Elisey Zanko Florian Apolloner Igor Támara Ilya Chichak Ivan Klass Jake Merdich Jannis Leidel Janusz Harkot Jiri Barton John Carter Jonas Kuba Zarzycki Leandra Finger Les Orchard Lin Xianyi Marcin Baran Mario Orlandi Mario Rosa Mariusz Felisiak Mattia Larentis Merijn Bertels Omer Katz Petr Knap Philip Neustrom Philipp Thumfart Pierre-Olivier Marec Roman Krejcik Silvan Spross SÅ‚awek Ehlert Vladas Tamoshaitis Vojtech Jasny Yin Jifeng illumin-us-r3v0lution mega saw2th trbs vl <1844144@gmail.com> vl django-commons-django-constance-1032c50/CODE_OF_CONDUCT.md000066400000000000000000000002731527040271600227040ustar00rootroot00000000000000# Django Constance Code of Conduct The django-constance project utilizes the [Django Commons Code of Conduct](https://github.com/django-commons/membership/blob/main/CODE_OF_CONDUCT.md). django-commons-django-constance-1032c50/CONTRIBUTING.md000066400000000000000000000004001527040271600223260ustar00rootroot00000000000000# Contributing to Django Constance This is a [Django Commons](https://github.com/django-commons/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://github.com/django-commons/membership/blob/main/CODE_OF_CONDUCT.md). django-commons-django-constance-1032c50/LICENSE000066400000000000000000000027101527040271600211100ustar00rootroot00000000000000Copyright (c) 2009-2017, Jazzband All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-commons-django-constance-1032c50/MANIFEST.in000066400000000000000000000001771527040271600216460ustar00rootroot00000000000000recursive-include constance/templates *.html recursive-include constance/locale *.po *.mo recursive-include constance/static * django-commons-django-constance-1032c50/README.rst000066400000000000000000000017041527040271600215740ustar00rootroot00000000000000Constance - Dynamic Django settings =================================== .. image:: https://img.shields.io/readthedocs/django-constance.svg :target: https://django-constance.readthedocs.io/ :alt: Documentation .. image:: https://github.com/django-commons/django-constance/workflows/Test/badge.svg :target: https://github.com/django-commons/django-constance/actions :alt: GitHub Actions .. image:: https://codecov.io/gh/django-commons/django-constance/branch/master/graph/badge.svg :target: https://codecov.io/gh/django-commons/django-constance :alt: Coverage A Django app for storing dynamic settings in pluggable backends (Redis and Django model backend built in) with an integration with the Django admin app. For more information see the documentation at: https://django-constance.readthedocs.io/ If you have questions or have trouble using the app please file a bug report at: https://github.com/django-commons/django-constance/issues django-commons-django-constance-1032c50/constance/000077500000000000000000000000001527040271600220605ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/__init__.py000066400000000000000000000003001527040271600241620ustar00rootroot00000000000000from django.utils.functional import LazyObject class LazyConfig(LazyObject): def _setup(self): from .base import Config self._wrapped = Config() config = LazyConfig() django-commons-django-constance-1032c50/constance/admin.py000066400000000000000000000260741527040271600235330ustar00rootroot00000000000000import json from collections import OrderedDict from datetime import date from datetime import datetime from operator import itemgetter from django import forms from django import get_version from django.apps import apps from django.contrib import admin from django.contrib import messages from django.contrib.admin.models import CHANGE from django.contrib.admin.models import LogEntry from django.contrib.admin.options import csrf_protect_m from django.contrib.admin.views.main import PAGE_VAR from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.template.response import TemplateResponse from django.urls import path from django.utils.formats import localize from django.utils.translation import gettext_lazy as _ from . import LazyConfig from . import settings from .forms import ConstanceForm from .utils import get_values config = LazyConfig() class ConstanceAdmin(admin.ModelAdmin): change_list_template = "admin/constance/change_list.html" change_list_form = ConstanceForm def __init__(self, model, admin_site): model._meta.concrete_model = Config super().__init__(model, admin_site) def get_urls(self): info = f"{self.model._meta.app_label}_{self.model._meta.module_name}" return [ path("", self.admin_site.admin_view(self.changelist_view), name=f"{info}_changelist"), path("", self.admin_site.admin_view(self.changelist_view), name=f"{info}_add"), # Redirect /change/ to the changelist so that "Recent actions" links in the admin index # point somewhere useful. The relative "../../" resolves to the constance changelist because the # full path is ///change/ and two levels up lands on //. path( "/change/", self.admin_site.admin_view(lambda request, object_id: HttpResponseRedirect("../../")), name=f"{info}_change", ), path("history/", self.admin_site.admin_view(self.history_view), name=f"{info}_history"), ] def get_config_value(self, name, options, form, initial): default, help_text = options[0], options[1] field_type = None if len(options) == 3: field_type = options[2] # First try to load the value from the actual backend value = initial.get(name) # Then if the returned value is None, get the default if value is None: value = getattr(config, name) form_field = form[name] config_value = { "name": name, "default": localize(default), "raw_default": default, "help_text": _(help_text), "value": localize(value), "modified": localize(value) != localize(default), "form_field": form_field, "is_date": isinstance(default, date), "is_datetime": isinstance(default, datetime), "is_checkbox": isinstance(form_field.field.widget, forms.CheckboxInput), "is_multi_select": isinstance(form_field.field.widget, forms.SelectMultiple | forms.CheckboxSelectMultiple), "is_file": isinstance(form_field.field.widget, forms.FileInput), } if field_type and field_type in settings.ADDITIONAL_FIELDS: serialized_default = form[name].field.prepare_value(default) config_value["default"] = serialized_default config_value["raw_default"] = serialized_default config_value["value"] = form[name].field.prepare_value(value) if config_value["is_multi_select"]: raw_def = config_value["raw_default"] if isinstance(raw_def, list | tuple | set): default_list = list(raw_def) elif raw_def is None: default_list = [] else: default_list = [raw_def] config_value["json_default"] = json.dumps(default_list) return config_value def get_changelist_form(self, request): """Returns a Form class for use in the changelist_view.""" # Defaults to self.change_list_form in order to preserve backward # compatibility return self.change_list_form @csrf_protect_m def changelist_view(self, request, extra_context=None): if not self.has_view_or_change_permission(request): raise PermissionDenied initial = get_values() form_cls = self.get_changelist_form(request) form = form_cls(initial=initial, request=request) if request.method == "POST" and request.user.has_perm("constance.change_config"): form = form_cls(data=request.POST, files=request.FILES, initial=initial, request=request) if form.is_valid(): changed_fields = form.save() if changed_fields: self._log_config_change(request, changed_fields) messages.add_message(request, messages.SUCCESS, _("Live settings updated successfully.")) return HttpResponseRedirect(".") messages.add_message(request, messages.ERROR, _("Failed to update live settings.")) context = { **self.admin_site.each_context(request), **(extra_context or {}), "config_values": [], "title": self.model._meta.app_config.verbose_name, "app_label": "constance", "opts": self.model._meta, "form": form, "media": self.media + form.media, "icon_type": "svg", "django_version": get_version(), } for name, options in settings.CONFIG.items(): context["config_values"].append(self.get_config_value(name, options, form, initial)) if settings.CONFIG_FIELDSETS: if isinstance(settings.CONFIG_FIELDSETS, dict): fieldset_items = settings.CONFIG_FIELDSETS.items() else: fieldset_items = settings.CONFIG_FIELDSETS context["fieldsets"] = [] for fieldset_title, fieldset_data in fieldset_items: if isinstance(fieldset_data, dict): fields_list = fieldset_data["fields"] collapse = fieldset_data.get("collapse", False) else: fields_list = fieldset_data collapse = False absent_fields = [field for field in fields_list if field not in settings.CONFIG] if any(absent_fields): raise ValueError( "CONSTANCE_CONFIG_FIELDSETS contains field(s) that does not exist(s): {}".format( ", ".join(absent_fields) ) ) config_values = [] for name in fields_list: options = settings.CONFIG.get(name) if options: config_values.append(self.get_config_value(name, options, form, initial)) fieldset_context = {"title": fieldset_title, "config_values": config_values} if collapse: fieldset_context["collapse"] = True context["fieldsets"].append(fieldset_context) if not isinstance(settings.CONFIG_FIELDSETS, OrderedDict | tuple): context["fieldsets"].sort(key=itemgetter("title")) if not isinstance(settings.CONFIG, OrderedDict): context["config_values"].sort(key=itemgetter("name")) request.current_app = self.admin_site.name return TemplateResponse(request, self.change_list_template, context) def history_view(self, request, object_id=None, extra_context=None): """Display the change history for constance config values.""" if not self.has_view_or_change_permission(request): raise PermissionDenied ct = ContentType.objects.get_for_model(self.model) action_list = ( LogEntry.objects.filter( content_type=ct, object_id="Config", ) .select_related("user", "content_type") .order_by("-action_time") ) paginator = self.get_paginator(request, action_list, 100) page_number = request.GET.get(PAGE_VAR, 1) page_obj = paginator.get_page(page_number) page_range = paginator.get_elided_page_range(page_obj.number) context = { **self.admin_site.each_context(request), "title": _("Change history: %s") % self.model._meta.verbose_name_plural.capitalize(), "action_list": page_obj, "page_range": page_range, "page_var": PAGE_VAR, "pagination_required": paginator.count > 100, "opts": self.model._meta, "app_label": "constance", **(extra_context or {}), } request.current_app = self.admin_site.name return TemplateResponse( request, "admin/constance/config_history.html", context, ) def _log_config_change(self, request, changed_fields): """ Create a Django admin LogEntry recording which config fields were changed. Uses the standard Django JSON change_message format so that LogEntry.get_change_message() can interpret it correctly. """ ct = ContentType.objects.get_for_model(self.model) change_message = json.dumps([{"changed": {"fields": changed_fields}}]) LogEntry.objects.create( user_id=request.user.pk, content_type_id=ct.pk, object_id="Config", object_repr="Config", action_flag=CHANGE, change_message=change_message, ) def has_add_permission(self, *args, **kwargs): return False def has_delete_permission(self, *args, **kwargs): return False def has_view_permission(self, request, obj=None): if settings.SUPERUSER_ONLY: return request.user.is_superuser return super().has_view_permission(request, obj) def has_change_permission(self, request, obj=None): if settings.SUPERUSER_ONLY: return request.user.is_superuser return super().has_change_permission(request, obj) class Config: class Meta: app_label = "constance" object_name = "Config" concrete_model = None model_name = module_name = "config" verbose_name_plural = _("config") abstract = False swapped = False is_composite_pk = False def get_ordered_objects(self): return False def get_change_permission(self): return f"change_{self.model_name}" @property def app_config(self): return apps.get_app_config(self.app_label) @property def label(self): return f"{self.app_label}.{self.object_name}" @property def label_lower(self): return f"{self.app_label}.{self.model_name}" _meta = Meta() admin.site.register([Config], ConstanceAdmin) django-commons-django-constance-1032c50/constance/apps.py000066400000000000000000000006051527040271600233760ustar00rootroot00000000000000from django.apps import AppConfig from django.core import checks from django.utils.translation import gettext_lazy as _ from constance.checks import check_fieldsets class ConstanceConfig(AppConfig): name = "constance" verbose_name = _("Constance") default_auto_field = "django.db.models.AutoField" def ready(self): checks.register(check_fieldsets, "constance") django-commons-django-constance-1032c50/constance/backends/000077500000000000000000000000001527040271600236325ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/backends/__init__.py000066400000000000000000000023741527040271600257510ustar00rootroot00000000000000"""Defines the base constance backend.""" from abc import ABC from abc import abstractmethod class Backend(ABC): @abstractmethod def get(self, key): """ Get the key from the backend store and return the value. Return None if not found. """ ... @abstractmethod async def aget(self, key): """ Get the key from the backend store and return the value. Return None if not found. """ ... @abstractmethod def mget(self, keys): """ Get the keys from the backend store and return a dict mapping each found key to its value. Return an empty dict if no keys are provided or none are found. """ ... @abstractmethod async def amget(self, keys): """ Get the keys from the backend store and return a dict mapping each found key to its value. Return an empty dict if no keys are provided or none are found. """ ... @abstractmethod def set(self, key, value): """Add the value to the backend store given the key.""" ... @abstractmethod async def aset(self, key, value): """Add the value to the backend store given the key.""" ... django-commons-django-constance-1032c50/constance/backends/database.py000066400000000000000000000150531527040271600257540ustar00rootroot00000000000000from django.core.cache import caches from django.core.cache.backends.locmem import LocMemCache from django.core.exceptions import ImproperlyConfigured from django.db import IntegrityError from django.db import OperationalError from django.db import ProgrammingError from django.db import transaction from django.db.models.signals import post_save from constance import config from constance import settings from constance import signals from constance.backends import Backend from constance.codecs import dumps from constance.codecs import loads class DatabaseBackend(Backend): def __init__(self): from constance.models import Constance self._model = Constance self._prefix = settings.DATABASE_PREFIX self._autofill_timeout = settings.DATABASE_CACHE_AUTOFILL_TIMEOUT self._autofill_cachekey = "autofilled" if self._model._meta.app_config is None: raise ImproperlyConfigured( "The constance.backends.database app isn't installed " "correctly. Make sure it's in your INSTALLED_APPS setting." ) if settings.DATABASE_CACHE_BACKEND: self._cache = caches[settings.DATABASE_CACHE_BACKEND] if isinstance(self._cache, LocMemCache): raise ImproperlyConfigured( "The CONSTANCE_DATABASE_CACHE_BACKEND setting refers to a " f"subclass of Django's local-memory backend ({settings.DATABASE_CACHE_BACKEND!r}). Please " "set it to a backend that supports cross-process caching." ) else: self._cache = None # Clear simple cache. post_save.connect(self.clear, sender=self._model) def add_prefix(self, key): return f"{self._prefix}{key}" def autofill(self): if not self._autofill_timeout or not self._cache: return full_cachekey = self.add_prefix(self._autofill_cachekey) if self._cache.get(full_cachekey): return autofill_values = {full_cachekey: 1} for key, value in self.mget(settings.CONFIG).items(): autofill_values[self.add_prefix(key)] = value self._cache.set_many(autofill_values, timeout=self._autofill_timeout) def mget(self, keys): result = {} if not keys: return result keys = {self.add_prefix(key): key for key in keys} try: stored = self._model._default_manager.filter(key__in=keys) for const in stored: result[keys[const.key]] = loads(const.value) except (OperationalError, ProgrammingError): pass return result def get(self, key): key = self.add_prefix(key) value = None if self._cache: value = self._cache.get(key) if value is None: self.autofill() value = self._cache.get(key) if value is None: match = self._model._default_manager.filter(key=key).only("value").first() if match: value = loads(match.value) if self._cache: self._cache.add(key, value) return value async def aget(self, key): from asgiref.sync import sync_to_async prefixed_key = self.add_prefix(key) value = None if self._cache: value = await self._cache.aget(prefixed_key) if value is None: await sync_to_async(self.autofill, thread_sensitive=True)() value = await self._cache.aget(prefixed_key) if value is None: match = await self._model._default_manager.filter(key=prefixed_key).only("value").afirst() if match: value = loads(match.value) if self._cache: await self._cache.aadd(prefixed_key, value) return value async def amget(self, keys): if not keys: return {} prefixed_keys_map = {self.add_prefix(key): key for key in keys} results = {} if self._cache: cache_results = await self._cache.aget_many(prefixed_keys_map.keys()) for prefixed_key, value in cache_results.items(): results[prefixed_keys_map[prefixed_key]] = value missing_prefixed_keys = [k for k in prefixed_keys_map if prefixed_keys_map[k] not in results] if missing_prefixed_keys: try: async for const in self._model._default_manager.filter(key__in=missing_prefixed_keys): results[prefixed_keys_map[const.key]] = loads(const.value) except (OperationalError, ProgrammingError): pass return results def set(self, key, value): key = self.add_prefix(key) created = False queryset = self._model._default_manager.all() # Set _for_write attribute as get_or_create method does # https://github.com/django/django/blob/2.2.11/django/db/models/query.py#L536 queryset._for_write = True try: constance = queryset.get(key=key) except (OperationalError, ProgrammingError): # database is not created, noop return except self._model.DoesNotExist: try: with transaction.atomic(using=queryset.db): queryset.create(key=key, value=dumps(value)) created = True except IntegrityError: # Allow concurrent writes constance = queryset.get(key=key) if not created: old_value = loads(constance.value) constance.value = dumps(value) constance.save(update_fields=["value"]) else: old_value = None if self._cache: self._cache.set(key, value) signals.config_updated.send(sender=config, key=key, old_value=old_value, new_value=value) async def aset(self, key, value): from asgiref.sync import sync_to_async # We use sync_to_async because Django's transaction.atomic() and database connections are thread-local. # This ensures the operation runs in the correct database thread until native async transactions are supported. return await sync_to_async(self.set, thread_sensitive=True)(key, value) def clear(self, sender, instance, created, **kwargs): if self._cache and not created: keys = [self.add_prefix(k) for k in settings.CONFIG] keys.append(self.add_prefix(self._autofill_cachekey)) self._cache.delete_many(keys) self.autofill() django-commons-django-constance-1032c50/constance/backends/memory.py000066400000000000000000000024331527040271600255160ustar00rootroot00000000000000from threading import Lock from constance import config from constance import signals from . import Backend class MemoryBackend(Backend): """Simple in-memory backend that should be mostly used for testing purposes.""" _storage = {} _lock = Lock() def __init__(self): super().__init__() def get(self, key): with self._lock: return self._storage.get(key) async def aget(self, key): # Memory operations are fast enough that we don't need true async here return self.get(key) def mget(self, keys): if not keys: return {} with self._lock: return {key: self._storage[key] for key in keys if key in self._storage} async def amget(self, keys): if not keys: return {} with self._lock: return {key: self._storage[key] for key in keys if key in self._storage} def set(self, key, value): with self._lock: old_value = self._storage.get(key) self._storage[key] = value signals.config_updated.send(sender=config, key=key, old_value=old_value, new_value=value) async def aset(self, key, value): # Memory operations are fast enough that we don't need true async here self.set(key, value) django-commons-django-constance-1032c50/constance/backends/redisd.py000066400000000000000000000171371527040271600254670ustar00rootroot00000000000000import asyncio from threading import RLock from time import monotonic from django.core.exceptions import ImproperlyConfigured from constance import config from constance import settings from constance import signals from constance import utils from constance.backends import Backend from constance.codecs import dumps from constance.codecs import loads class RedisBackend(Backend): def __init__(self): super().__init__() self._prefix = settings.REDIS_PREFIX connection_cls = settings.REDIS_CONNECTION_CLASS async_connection_cls = settings.REDIS_ASYNC_CONNECTION_CLASS if connection_cls: self._rd = utils.import_module_attr(connection_cls)() else: try: import redis except ImportError: raise ImproperlyConfigured("The Redis backend requires redis-py to be installed.") from None else: if isinstance(settings.REDIS_CONNECTION, str): self._rd = redis.from_url(settings.REDIS_CONNECTION) else: self._rd = redis.Redis(**settings.REDIS_CONNECTION) if async_connection_cls: self._ard = utils.import_module_attr(async_connection_cls)() else: try: import redis.asyncio as aredis except ImportError: # We set this to none instead of raising an error to indicate that async support is not available # without breaking existing sync usage. self._ard = None else: if isinstance(settings.REDIS_CONNECTION, str): self._ard = aredis.from_url(settings.REDIS_CONNECTION) else: self._ard = aredis.Redis(**settings.REDIS_CONNECTION) def add_prefix(self, key): return f"{self._prefix}{key}" def _check_async_support(self): if self._ard is None: raise ImproperlyConfigured( "Async support for the Redis backend requires redis>=4.2.0 " "or a custom CONSTANCE_REDIS_ASYNC_CONNECTION_CLASS to be configured." ) def get(self, key): value = self._rd.get(self.add_prefix(key)) if value: return loads(value) return None async def aget(self, key): self._check_async_support() value = await self._ard.get(self.add_prefix(key)) if value: return loads(value) return None def mget(self, keys): if not keys: return {} prefixed_keys = [self.add_prefix(key) for key in keys] return {key: loads(value) for key, value in zip(keys, self._rd.mget(prefixed_keys), strict=False) if value} async def amget(self, keys): if not keys: return {} self._check_async_support() prefixed_keys = [self.add_prefix(key) for key in keys] values = await self._ard.mget(prefixed_keys) return {key: loads(value) for key, value in zip(keys, values, strict=False) if value} def set(self, key, value): old_value = self.get(key) self._rd.set(self.add_prefix(key), dumps(value)) signals.config_updated.send(sender=config, key=key, old_value=old_value, new_value=value) async def _aset_internal(self, key, value, old_value): """ Internal set operation. Separated to allow subclasses to provide old_value without going through self.aget() which may have locking behavior. """ self._check_async_support() await self._ard.set(self.add_prefix(key), dumps(value)) signals.config_updated.send(sender=config, key=key, old_value=old_value, new_value=value) async def aset(self, key, value): old_value = await self.aget(key) await self._aset_internal(key, value, old_value) class CachingRedisBackend(RedisBackend): _sentinel = object() _lock = RLock() _async_lock = None # Lazy-initialized asyncio.Lock def __init__(self): super().__init__() self._timeout = settings.REDIS_CACHE_TIMEOUT self._cache = {} self._sentinel = object() def _get_async_lock(self): # Lazily create the asyncio lock to avoid issues with event loops if self._async_lock is None: self._async_lock = asyncio.Lock() return self._async_lock def _has_expired(self, value): return value[0] <= monotonic() def _cache_value(self, key, new_value): self._cache[key] = (monotonic() + self._timeout, new_value) def get(self, key): value = self._cache.get(key, self._sentinel) if value is self._sentinel or self._has_expired(value): with self._lock: new_value = super().get(key) self._cache_value(key, new_value) return new_value return value[1] async def _aget_unlocked(self, key): """ Get value with cache support but without acquiring lock. Caller must already hold the lock. """ value = self._cache.get(key, self._sentinel) if value is self._sentinel or self._has_expired(value): new_value = await super().aget(key) self._cache_value(key, new_value) return new_value return value[1] async def aget(self, key): value = self._cache.get(key, self._sentinel) if value is self._sentinel or self._has_expired(value): async with self._get_async_lock(): # Double-check after acquiring lock, then delegate to unlocked version return await self._aget_unlocked(key) return value[1] def set(self, key, value): with self._lock: super().set(key, value) self._cache_value(key, value) async def aset(self, key, value): async with self._get_async_lock(): # Use unlocked version since we already hold the lock old_value = await self._aget_unlocked(key) # Use internal method to avoid lock recursion (super().aset calls self.aget) await self._aset_internal(key, value, old_value) self._cache_value(key, value) def mget(self, keys): if not keys: return {} result = {} for key in keys: value = self.get(key) if value is not None: result[key] = value return result async def amget(self, keys): if not keys: return {} results = {} missing_keys = [] # First, check the local cache for all keys for key in keys: value = self._cache.get(key, self._sentinel) if value is not self._sentinel and not self._has_expired(value): results[key] = value[1] else: missing_keys.append(key) # Fetch missing keys from Redis if missing_keys: async with self._get_async_lock(): # Re-check cache for keys that might have been fetched while waiting for lock still_missing = [] for key in missing_keys: value = self._cache.get(key, self._sentinel) if value is not self._sentinel and not self._has_expired(value): results[key] = value[1] else: still_missing.append(key) if still_missing: fetched = await super().amget(still_missing) for key, value in fetched.items(): self._cache_value(key, value) results[key] = value return results django-commons-django-constance-1032c50/constance/base.py000066400000000000000000000123371527040271600233520ustar00rootroot00000000000000import asyncio import warnings from django.conf import settings as django_settings from . import settings from . import utils class AsyncValueProxy: def __init__(self, key, config, default): self._key = key self._config = config self._default = default self._value = None self._fetched = False def __await__(self): return self._get_value().__await__() async def _get_value(self): if not self._fetched: result = await self._config._backend.aget(self._key) if result is None: result = self._default await self._config.aset(self._key, result) self._value = result self._fetched = True return self._value def _get_sync_value(self): warnings.warn( f"Synchronous access to Constance setting '{self._key}' inside an async loop. " f"Use 'await config.{self._key}' instead.", RuntimeWarning, stacklevel=3, ) return self._config._get_sync_value(self._key, self._default) def __str__(self): return str(self._get_sync_value()) def __repr__(self): return repr(self._get_sync_value()) def __int__(self): return int(self._get_sync_value()) def __float__(self): return float(self._get_sync_value()) def __bool__(self): return bool(self._get_sync_value()) def __eq__(self, other): return self._get_sync_value() == other def __ne__(self, other): return self._get_sync_value() != other def __lt__(self, other): return self._get_sync_value() < other def __le__(self, other): return self._get_sync_value() <= other def __gt__(self, other): return self._get_sync_value() > other def __ge__(self, other): return self._get_sync_value() >= other def __getitem__(self, key): return self._get_sync_value()[key] def __iter__(self): return iter(self._get_sync_value()) def __len__(self): return len(self._get_sync_value()) def __contains__(self, item): return item in self._get_sync_value() def __hash__(self): return hash(self._get_sync_value()) def __add__(self, other): return self._get_sync_value() + other def __sub__(self, other): return self._get_sync_value() - other def __mul__(self, other): return self._get_sync_value() * other def __truediv__(self, other): return self._get_sync_value() / other def __floordiv__(self, other): return self._get_sync_value() // other def __mod__(self, other): return self._get_sync_value() % other def __radd__(self, other): return other + self._get_sync_value() def __rsub__(self, other): return other - self._get_sync_value() def __rmul__(self, other): return other * self._get_sync_value() def __rtruediv__(self, other): return other / self._get_sync_value() def __rfloordiv__(self, other): return other // self._get_sync_value() def __rmod__(self, other): return other % self._get_sync_value() def __index__(self): val = self._get_sync_value() if hasattr(val, "__index__"): return val.__index__() raise TypeError(f"'{type(val).__name__}' object cannot be interpreted as an integer") class Config: """The global config wrapper that handles the backend.""" def __init__(self): super().__setattr__("_backend", utils.import_module_attr(settings.BACKEND)()) def _get_sync_value(self, key, default): result = self._backend.get(key) if result is None: result = default setattr(self, key, default) return result def __getattr__(self, key): if key == "_backend": return super().__getattribute__(key) try: if len(settings.CONFIG[key]) not in (2, 3): raise AttributeError(key) default = settings.CONFIG[key][0] except KeyError as e: raise AttributeError(key) from e if getattr(django_settings, "CONSTANCE_DETECT_ASYNCIO", getattr(settings, "DETECT_ASYNCIO", True)): try: asyncio.get_running_loop() except RuntimeError: return self._get_sync_value(key, default) return AsyncValueProxy(key, self, default) return self._get_sync_value(key, default) def __setattr__(self, key, value): if key == "_backend": super().__setattr__(key, value) return if key not in settings.CONFIG: raise AttributeError(key) self._backend.set(key, value) return async def aset(self, key, value): if key not in settings.CONFIG: raise AttributeError(key) await self._backend.aset(key, value) async def amget(self, keys): backend_values = await self._backend.amget(keys) # Merge with defaults like utils.get_values_for_keys default_initial = {name: settings.CONFIG[name][0] for name in keys if name in settings.CONFIG} return dict(default_initial, **backend_values) def __dir__(self): return settings.CONFIG.keys() django-commons-django-constance-1032c50/constance/checks.py000066400000000000000000000047301527040271600236760ustar00rootroot00000000000000from __future__ import annotations from django.core import checks from django.core.checks import CheckMessage from django.utils.translation import gettext_lazy as _ def check_fieldsets(*args, **kwargs) -> list[CheckMessage]: """ A Django system check to make sure that, if defined, CONFIG_FIELDSETS is consistent with settings.CONFIG. """ from . import settings errors = [] if hasattr(settings, "CONFIG_FIELDSETS") and settings.CONFIG_FIELDSETS: missing_keys, extra_keys = get_inconsistent_fieldnames() if missing_keys: check = checks.Warning( _("CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG."), hint=", ".join(sorted(missing_keys)), obj="settings.CONSTANCE_CONFIG", id="constance.E001", ) errors.append(check) if extra_keys: check = checks.Warning( _("CONSTANCE_CONFIG_FIELDSETS contains extra field(s) that does not exist in CONFIG."), hint=", ".join(sorted(extra_keys)), obj="settings.CONSTANCE_CONFIG", id="constance.E002", ) errors.append(check) return errors def get_inconsistent_fieldnames() -> tuple[set, set]: """ Returns a pair of values: 1) set of keys from settings.CONFIG that are not accounted for in settings.CONFIG_FIELDSETS 2) set of keys from settings.CONFIG_FIELDSETS that are not present in settings.CONFIG If there are no fieldnames in settings.CONFIG_FIELDSETS, returns an empty set. """ from . import settings if isinstance(settings.CONFIG_FIELDSETS, dict): fieldset_items = settings.CONFIG_FIELDSETS.items() else: fieldset_items = settings.CONFIG_FIELDSETS unique_field_names = set() for _fieldset_title, fields_list in fieldset_items: # fields_list can be a dictionary, when a fieldset is defined as collapsible # https://django-constance.readthedocs.io/en/latest/#fieldsets-collapsing if isinstance(fields_list, dict) and "fields" in fields_list: fields_list = fields_list["fields"] unique_field_names.update(fields_list) if not unique_field_names: return unique_field_names, unique_field_names config_keys = set(settings.CONFIG.keys()) missing_keys = config_keys - unique_field_names extra_keys = unique_field_names - config_keys return missing_keys, extra_keys django-commons-django-constance-1032c50/constance/codecs.py000066400000000000000000000070071527040271600236760ustar00rootroot00000000000000from __future__ import annotations import json import logging import uuid from datetime import date from datetime import datetime from datetime import time from datetime import timedelta from decimal import Decimal from typing import Any from typing import Protocol from typing import TypeVar logger = logging.getLogger(__name__) DEFAULT_DISCRIMINATOR = "default" class JSONEncoder(json.JSONEncoder): """Django-constance custom json encoder.""" def default(self, o): for discriminator, (t, _, encoder) in _codecs.items(): if isinstance(o, t): return _as(discriminator, encoder(o)) raise TypeError(f"Object of type {o.__class__.__name__} is not JSON serializable") def _as(discriminator: str, v: Any) -> dict[str, Any]: return {"__type__": discriminator, "__value__": v} def dumps(obj, _dumps=json.dumps, cls=JSONEncoder, default_kwargs=None, **kwargs): """Serialize object to json string.""" default_kwargs = default_kwargs or {} is_default_type = isinstance(obj, list | dict | str | int | bool | float | type(None)) return _dumps( _as(DEFAULT_DISCRIMINATOR, obj) if is_default_type else obj, cls=cls, **dict(default_kwargs, **kwargs) ) def loads(s, _loads=json.loads, *, first_level=True, **kwargs): """Deserialize json string to object.""" if first_level: return _loads(s, object_hook=object_hook, **kwargs) if isinstance(s, dict) and "__type__" not in s and "__value__" not in s: return {k: loads(v, first_level=False) for k, v in s.items()} if isinstance(s, list): return list(loads(v, first_level=False) for v in s) return _loads(s, object_hook=object_hook, **kwargs) def object_hook(o: dict) -> Any: """Hook function to perform custom deserialization.""" if o.keys() == {"__type__", "__value__"}: if o["__type__"] == DEFAULT_DISCRIMINATOR: return o["__value__"] codec = _codecs.get(o["__type__"]) if not codec: raise ValueError(f"Unsupported type: {o['__type__']}") return codec[1](o["__value__"]) if "__type__" not in o and "__value__" not in o: return o logger.error("Cannot deserialize object: %s", o) raise ValueError(f"Invalid object: {o}") T = TypeVar("T") class Encoder(Protocol[T]): def __call__(self, value: T, /) -> str: ... # pragma: no cover class Decoder(Protocol[T]): def __call__(self, value: str, /) -> T: ... # pragma: no cover def register_type(t: type[T], discriminator: str, encoder: Encoder[T], decoder: Decoder[T]): if not discriminator: raise ValueError("Discriminator must be specified") if _codecs.get(discriminator) or discriminator == DEFAULT_DISCRIMINATOR: raise ValueError(f"Type with discriminator {discriminator} is already registered") _codecs[discriminator] = (t, decoder, encoder) _codecs: dict[str, tuple[type, Decoder, Encoder]] = {} def _register_default_types(): # NOTE: datetime should be registered before date, because datetime is also instance of date. register_type(datetime, "datetime", datetime.isoformat, datetime.fromisoformat) register_type(date, "date", lambda o: o.isoformat(), lambda o: datetime.fromisoformat(o).date()) register_type(time, "time", lambda o: o.isoformat(), time.fromisoformat) register_type(Decimal, "decimal", str, Decimal) register_type(uuid.UUID, "uuid", lambda o: o.hex, uuid.UUID) register_type(timedelta, "timedelta", lambda o: o.total_seconds(), lambda o: timedelta(seconds=o)) _register_default_types() django-commons-django-constance-1032c50/constance/context_processors.py000066400000000000000000000005271527040271600264040ustar00rootroot00000000000000import constance def config(request): """ Simple context processor that puts the config into every RequestContext. Just make sure you have a setting like this: TEMPLATE_CONTEXT_PROCESSORS = ( # ... 'constance.context_processors.config', ) """ return {"config": constance.config} django-commons-django-constance-1032c50/constance/forms.py000066400000000000000000000134741527040271600235710ustar00rootroot00000000000000import hashlib from datetime import date from datetime import datetime from datetime import time from datetime import timedelta from decimal import Decimal from os.path import join from django import conf from django import forms from django.contrib import messages from django.contrib.admin import widgets from django.core.exceptions import ImproperlyConfigured from django.core.files.storage import default_storage from django.forms import fields from django.utils import timezone from django.utils.encoding import smart_bytes from django.utils.module_loading import import_string from django.utils.text import normalize_newlines from django.utils.translation import gettext_lazy as _ from . import LazyConfig from . import settings from .checks import get_inconsistent_fieldnames config = LazyConfig() NUMERIC_WIDGET = forms.TextInput(attrs={"size": 10}) INTEGER_LIKE = (fields.IntegerField, {"widget": NUMERIC_WIDGET}) STRING_LIKE = ( fields.CharField, { "widget": forms.Textarea(attrs={"rows": 3}), "required": False, }, ) FIELDS = { bool: (fields.BooleanField, {"required": False}), int: INTEGER_LIKE, Decimal: (fields.DecimalField, {"widget": NUMERIC_WIDGET}), str: STRING_LIKE, datetime: (fields.SplitDateTimeField, {"widget": widgets.AdminSplitDateTime}), timedelta: (fields.DurationField, {"widget": widgets.AdminTextInputWidget}), date: (fields.DateField, {"widget": widgets.AdminDateWidget}), time: (fields.TimeField, {"widget": widgets.AdminTimeWidget}), float: (fields.FloatField, {"widget": NUMERIC_WIDGET}), } def parse_additional_fields(fields): for key in fields: field = list(fields[key]) if len(field) == 1: field.append({}) field[0] = import_string(field[0]) if "widget" in field[1]: klass = import_string(field[1]["widget"]) field[1]["widget"] = klass(**(field[1].get("widget_kwargs", {}) or {})) if "widget_kwargs" in field[1]: del field[1]["widget_kwargs"] fields[key] = field return fields FIELDS.update(parse_additional_fields(settings.ADDITIONAL_FIELDS)) class ConstanceForm(forms.Form): version = forms.CharField(widget=forms.HiddenInput) def __init__(self, initial, request=None, *args, **kwargs): super().__init__(*args, initial=initial, **kwargs) version_hash = hashlib.sha256() only_view = request and not request.user.has_perm("constance.change_config") if only_view: messages.warning( request, _("You don't have permission to change these values"), ) for name, options in settings.CONFIG.items(): default = options[0] if len(options) == 3: config_type = options[2] if config_type not in settings.ADDITIONAL_FIELDS and not isinstance(default, config_type): raise ImproperlyConfigured( _( "Default value type must be " "equal to declared config " "parameter type. Please fix " "the default value of " "'%(name)s'." ) % {"name": name} ) else: config_type = type(default) if config_type not in FIELDS: raise ImproperlyConfigured( _( "Constance doesn't support " "config values of the type " "%(config_type)s. Please fix " "the value of '%(name)s'." ) % {"config_type": config_type, "name": name} ) field_class, kwargs = FIELDS[config_type] kwargs = kwargs.copy() if only_view: kwargs["disabled"] = True self.fields[name] = field_class(label=name, **kwargs) version_hash.update(smart_bytes(initial.get(name, ""))) self.initial["version"] = version_hash.hexdigest() def save(self): """ Save changed config values to the backend. Returns a list of config field names that were actually modified. """ for file_field in self.files: file = self.cleaned_data[file_field] self.cleaned_data[file_field] = default_storage.save(join(settings.FILE_ROOT, file.name), file) changed_fields = [] for name in settings.CONFIG: current = getattr(config, name) new = self.cleaned_data[name] if isinstance(new, str): new = normalize_newlines(new) if conf.settings.USE_TZ and isinstance(current, datetime) and not timezone.is_aware(current): current = timezone.make_aware(current) if current != new: setattr(config, name, new) changed_fields.append(name) return changed_fields def clean_version(self): value = self.cleaned_data["version"] if settings.IGNORE_ADMIN_VERSION_CHECK: return value if value != self.initial["version"]: raise forms.ValidationError( _("The settings have been modified by someone else. Please reload the form and resubmit your changes.") ) return value def clean(self): cleaned_data = super().clean() if not settings.CONFIG_FIELDSETS: return cleaned_data missing_keys, extra_keys = get_inconsistent_fieldnames() if missing_keys or extra_keys: raise forms.ValidationError( _("CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG.") ) return cleaned_data django-commons-django-constance-1032c50/constance/locale/000077500000000000000000000000001527040271600233175ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/ar/000077500000000000000000000000001527040271600237215ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/ar/LC_MESSAGES/000077500000000000000000000000001527040271600255065ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/ar/LC_MESSAGES/django.mo000066400000000000000000000041551527040271600273120ustar00rootroot00000000000000Þ•¤,ˆR‰ ÜhæOoW8Ç #5:b?¢¨ ¯ ¹÷Ä^¼„,±ÆÀˆ‡.?@ €‹œ’ /<K\   CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in CONSTANCE_CONFIG.ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: Report-Msgid-Bugs-To: PO-Revision-Date: 2020-12-01 12:05+0100 Language: ar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last-Translator: Language-Team: X-Generator: Poedit 2.4.2 لا يحتوي CONSTANCE_CONFIG_FIELDSETS على حقول موجودة Ù�ÙŠ CONSTANCE_CONFIG.كونستانسلا يعتمد كونستانس قيم التكوين من النوع %(config_type)s. الرجاء إصلاح قيمة '%(name)s'.اÙ�تراضييجب أن يكون نوع القيمة الاÙ�تراضي مساوياً لنوع معلمة التكوين المعلن. الرجاء إصلاح القيمة الاÙ�تراضية لـ '%(name)s'.الحصول على / تعيين إعدادات التكوين Ù�ÙŠ قاعدة البيانات التي تعالجها كونستانسالصÙ�حة الرئيسيةتم تعديلهتم تحديث الإعدادات المباشرة بنجاح.الإسمحÙ�ظتم تعديل الإعدادات بواسطة شخص آخر. الرجاء إعادة تحميل النموذج وإعادة إرسال التغييرات.القيمةالتكوينكونستانسكونستانسdjango-commons-django-constance-1032c50/constance/locale/ar/LC_MESSAGES/django.po000066400000000000000000000056361527040271600273220ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2020-11-30 23:15+0100\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Last-Translator: \n" "Language-Team: \n" "X-Generator: \n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "يجب أن يكون نوع القيمة الاÙ�تراضي مساوياً لنوع معلمة التكوين المعلن. الرجاء " "إصلاح القيمة الاÙ�تراضية لـ '%(name)s'." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "لا يعتمد كونستانس قيم التكوين من النوع %(config_type)s. الرجاء إصلاح قيمة " "'%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "تم تعديل الإعدادات بواسطة شخص آخر. الرجاء إعادة تحميل النموذج وإعادة إرسال " "التغييرات." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" "لا يحتوي CONSTANCE_CONFIG_FIELDSETS على حقول موجودة Ù�ÙŠ CONSTANCE_CONFIG." #: admin.py:224 msgid "Live settings updated successfully." msgstr "تم تحديث الإعدادات المباشرة بنجاح." #: admin.py:285 msgid "config" msgstr "التكوين" #: apps.py:8 msgid "Constance" msgstr "كونستانس" #: backends/database/models.py:19 msgid "constance" msgstr "كونستانس" #: backends/database/models.py:20 msgid "constances" msgstr "كونستانس" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" "الحصول على / تعيين إعدادات التكوين Ù�ÙŠ قاعدة البيانات التي تعالجها كونستانس" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Ø­Ù�ظ" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "الصÙ�حة الرئيسية" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "الإسم" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "اÙ�تراضي" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "القيمة" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "تم تعديله" django-commons-django-constance-1032c50/constance/locale/cs_CZ/000077500000000000000000000000001527040271600243205ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/cs_CZ/LC_MESSAGES/000077500000000000000000000000001527040271600261055ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/cs_CZ/LC_MESSAGES/django.mo000066400000000000000000000017331527040271600277100ustar00rootroot00000000000000Þ• tÌ #*NSX^ e oÞzYk q%¥¬´ ¼ Ç Ñ  DefaultHomeIs modifiedLive settings updated successfully.NameSaveValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Jannis Leidel Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/django-constance/language/cs_CZ/) Language: cs_CZ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; Výchozí hodnotaDomůJe zmÄ›nÄ›na?Nastavení bylo úspěšnÄ› uloženo.NázevUložitHodnotanastaveníkonstantakonstantydjango-commons-django-constance-1032c50/constance/locale/cs_CZ/LC_MESSAGES/django.po000066400000000000000000000045051527040271600277130ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/" "django-constance/language/cs_CZ/)\n" "Language: cs_CZ\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Nastavení bylo úspěšnÄ› uloženo." #: admin.py:285 msgid "config" msgstr "nastavení" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "konstanta" #: backends/database/models.py:20 msgid "constances" msgstr "konstanty" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Uložit" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Domů" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Název" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Výchozí hodnota" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Hodnota" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Je zmÄ›nÄ›na?" #~ msgid "Constance config" #~ msgstr "Nastavení konstant" django-commons-django-constance-1032c50/constance/locale/de/000077500000000000000000000000001527040271600237075ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/de/LC_MESSAGES/000077500000000000000000000000001527040271600254745ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/de/LC_MESSAGES/django.mo000066400000000000000000000027121527040271600272750ustar00rootroot00000000000000Þ•ŒüH IhS¼Ä É#Õùþbfl s }­ˆ 6|@½ÌÒ5â|%¢ § µ ¿    ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: 2014-11-27 18:17+0000 Last-Translator: Jannis Leidel Language-Team: German (http://www.transifex.com/projects/p/django-constance/language/de/) Language: de MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); KonstanzeKonstanze unterstützt die Konfigurationswerte vom Typ %(config_type)s nicht. Bitte den Ausgangswert von '%(name)s' ändern.VoreinstellungStartIst modifiziertDie Livekonfiguration wurde erfolgreich aktualisiert.NameSichernDie Konfiguration wurde seit Öffnen dieser Seite verändert. Bitte die Seite neuladen und die Änderungen erneut vornehmen.WertKonfigurationKonstanzeKonstanzesdjango-commons-django-constance-1032c50/constance/locale/de/LC_MESSAGES/django.po000066400000000000000000000055471527040271600273110ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Jannis Leidel , 2014 msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2014-11-27 18:17+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: German (http://www.transifex.com/projects/p/django-constance/" "language/de/)\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:113 #, fuzzy, python-format #| msgid "" #| "Constance doesn't support config values of the type %(config_type)s. " #| "Please fix the value of '%(name)s'." msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Konstanze unterstützt die Konfigurationswerte vom Typ %(config_type)s nicht. " "Bitte den Ausgangswert von '%(name)s' ändern." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Konstanze unterstützt die Konfigurationswerte vom Typ %(config_type)s nicht. " "Bitte den Ausgangswert von '%(name)s' ändern." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Die Konfiguration wurde seit Öffnen dieser Seite verändert. Bitte die Seite " "neuladen und die Änderungen erneut vornehmen." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Die Livekonfiguration wurde erfolgreich aktualisiert." #: admin.py:285 msgid "config" msgstr "Konfiguration" #: apps.py:8 msgid "Constance" msgstr "Konstanze" #: backends/database/models.py:19 msgid "constance" msgstr "Konstanze" #: backends/database/models.py:20 msgid "constances" msgstr "Konstanzes" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Sichern" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Start" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Name" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Voreinstellung" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Wert" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Ist modifiziert" #~ msgid "Constance config" #~ msgstr "Constance Konfiguration" django-commons-django-constance-1032c50/constance/locale/en/000077500000000000000000000000001527040271600237215ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/en/LC_MESSAGES/000077500000000000000000000000001527040271600255065ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/en/LC_MESSAGES/django.mo000066400000000000000000000005211527040271600273030ustar00rootroot00000000000000Þ•$,89Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit django-commons-django-constance-1032c50/constance/locale/en/LC_MESSAGES/django.po000066400000000000000000000041161527040271600273120ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-19 21:00+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "" #: admin.py:267 msgid "Failed to update live settings." msgstr "" #: admin.py:285 msgid "config" msgstr "" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "" #: backends/database/models.py:20 msgid "constances" msgstr "" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "" django-commons-django-constance-1032c50/constance/locale/es/000077500000000000000000000000001527040271600237265ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/es/LC_MESSAGES/000077500000000000000000000000001527040271600255135ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/es/LC_MESSAGES/django.mo000066400000000000000000000027021527040271600273130ustar00rootroot00000000000000Þ•ŒüH IhS¼Ä É#Õùþbfl s }®ˆ 7vA¸ÇÎ9ß o(˜ž ­ ·    ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Igor Támara Language-Team: Spanish (http://www.transifex.com/projects/p/django-constance/language/de/ Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); ConstanceConstance no soporta valores de configuración de los tipos %(config_type)s. Por favor arregle el valor de '%(name)s'.PredeterminadoInicioEstá modificadoLas configuraciones en vivo se actualizaron exitosamente.NombreGuardarLa configuración ha sido modificada por alguien más. Por favor recargue el formulario y reenvíe sus cambios.Valorconfiguraciónconstanceconstancesdjango-commons-django-constance-1032c50/constance/locale/es/LC_MESSAGES/django.po000066400000000000000000000055371527040271600273270ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Igor Támara , 2015 msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Igor Támara \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/django-constance/" "language/de/\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:113 #, fuzzy, python-format #| msgid "" #| "Constance doesn't support config values of the type %(config_type)s. " #| "Please fix the value of '%(name)s'." msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Constance no soporta valores de configuración de los tipos %(config_type)s. " "Por favor arregle el valor de '%(name)s'." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance no soporta valores de configuración de los tipos %(config_type)s. " "Por favor arregle el valor de '%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "La configuración ha sido modificada por alguien más. Por favor recargue el " "formulario y reenvíe sus cambios." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Las configuraciones en vivo se actualizaron exitosamente." #: admin.py:285 msgid "config" msgstr "configuración" #: apps.py:8 msgid "Constance" msgstr "Constance" #: backends/database/models.py:19 msgid "constance" msgstr "constance" #: backends/database/models.py:20 msgid "constances" msgstr "constances" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Guardar" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Inicio" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nombre" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Predeterminado" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Valor" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Está modificado" #~ msgid "Constance config" #~ msgstr "Configuración de Constance" django-commons-django-constance-1032c50/constance/locale/et/000077500000000000000000000000001527040271600237275ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/et/LC_MESSAGES/000077500000000000000000000000001527040271600255145ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/et/LC_MESSAGES/django.mo000066400000000000000000000032651527040271600273210ustar00rootroot00000000000000Þ•” h ihsÜoä8T �#™½ÂbÇ*0 7 AkL¸‚¿ B�L)Îød( �—§®    ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); SeadedConstance ei toeta seadete seda tüüpi %(config_type) seadete väärtusi. Palunsisestage korrektne väärtus väljale '%(name)s'.VaikimisiVaikimisi väärtus peab olema vastav seade parameetri tüübile. Palun sisestagekorrekne vaikimisi väärtus väljale '%(name)s'Loe/salvesta andmebaasi põhiseid seadeidMuudetudSeaded edukalt muudetud.NimiSalvestaSeadeid on vahepeal muudetud, palun esitage oma muudatused peale seda kui olete lehe uuesti laadinudVäärtuskonfiguratsioonseadedseadeddjango-commons-django-constance-1032c50/constance/locale/et/LC_MESSAGES/django.po000066400000000000000000000050731527040271600273230ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Vaikimisi väärtus peab olema vastav seade parameetri tüübile. Palun " "sisestagekorrekne vaikimisi väärtus väljale '%(name)s'" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance ei toeta seadete seda tüüpi %(config_type) seadete väärtusi. " "Palunsisestage korrektne väärtus väljale '%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Seadeid on vahepeal muudetud, palun esitage oma muudatused peale seda kui " "olete lehe uuesti laadinud" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Seaded edukalt muudetud." #: admin.py:285 msgid "config" msgstr "konfiguratsioon" #: apps.py:8 msgid "Constance" msgstr "Seaded" #: backends/database/models.py:19 msgid "constance" msgstr "seaded" #: backends/database/models.py:20 msgid "constances" msgstr "seaded" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "Loe/salvesta andmebaasi põhiseid seadeid" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Salvesta" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nimi" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Vaikimisi" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Väärtus" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Muudetud" django-commons-django-constance-1032c50/constance/locale/fa/000077500000000000000000000000001527040271600237055ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/fa/LC_MESSAGES/000077500000000000000000000000001527040271600254725ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/fa/LC_MESSAGES/django.mo000066400000000000000000000044051527040271600272740ustar00rootroot00000000000000Þ•¬<°R± hwo8ï( -#9]bsbxÛá è òýMb¨qÄ*‘ï�Š; Ü%ã ¶ ËÖçö   CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in CONSTANCE_CONFIG.ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: Report-Msgid-Bugs-To: PO-Revision-Date: 2020-09-24 17:33+0330 Language: fa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last-Translator: Mehdi Namaki Language-Team: X-Generator: Poedit 2.3 CONSTANCE_CONFIG_FIELDSETS شامل Ù�یلدهای CONSTANCE_CONFIG نیست.تنظیماتتنظیمات مقادیر پیکربندی از نوع %(config_type)s را پشتیبانی نمی کند. لطÙ�اً مقدار '%(name)s' را اصلاح کنید.پیش‌Ù�رضنوع مقدار پیش Ù�رض باید برابر با نوع پارامتر پیکربندی اعلام شده باشد. لطÙ�اً مقدار پیش Ù�رض '%(name)s' را اصلاح کنید.دریاÙ�ت/تنظیم تنظیمات پیکربندی درون پایگاه داده Ú©Ù‡ توسط تنظیمات بکار برده Ù…ÛŒ شودخانهتغییر یاÙ�تهتنظیمات زنده با موÙ�قیت به روز شد.نامبازنشانی به پیش‌Ù�رضذخیرهتنظیمات توسط شخص دیگری تغییر یاÙ�ته است. لطÙ�اً Ù�رم را بارگیری کنید Ùˆ تغییرات خود را دوباره ارسال کنید.مقدارپیکربندیتنظیماتتنظیماتdjango-commons-django-constance-1032c50/constance/locale/fa/LC_MESSAGES/django.po000066400000000000000000000061341527040271600273000ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2020-09-24 17:33+0330\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Last-Translator: Mehdi Namaki \n" "Language-Team: \n" "X-Generator: Poedit 2.3\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "نوع مقدار پیش Ù�رض باید برابر با نوع پارامتر پیکربندی اعلام شده باشد. لطÙ�اً " "مقدار پیش Ù�رض '%(name)s' را اصلاح کنید." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "تنظیمات مقادیر پیکربندی از نوع %(config_type)s را پشتیبانی نمی کند. لطÙ�اً " "مقدار '%(name)s' را اصلاح کنید." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "تنظیمات توسط شخص دیگری تغییر یاÙ�ته است. لطÙ�اً Ù�رم را بارگیری کنید Ùˆ تغییرات " "خود را دوباره ارسال کنید." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "CONSTANCE_CONFIG_FIELDSETS شامل Ù�یلدهای CONSTANCE_CONFIG نیست." #: admin.py:224 msgid "Live settings updated successfully." msgstr "تنظیمات زنده با موÙ�قیت به روز شد." #: admin.py:285 msgid "config" msgstr "پیکربندی" #: apps.py:8 msgid "Constance" msgstr "تنظیمات" #: backends/database/models.py:19 msgid "constance" msgstr "تنظیمات" #: backends/database/models.py:20 msgid "constances" msgstr "تنظیمات" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" "دریاÙ�ت/تنظیم تنظیمات پیکربندی درون پایگاه داده Ú©Ù‡ توسط تنظیمات بکار برده Ù…ÛŒ " "شود" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "ذخیره" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "خانه" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "نام" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "پیش‌Ù�رض" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "مقدار" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "تغییر یاÙ�ته" #: templates/admin/constance/includes/results_list.html:44 msgid "Reset to default" msgstr "بازنشانی به پیش‌Ù�رض" django-commons-django-constance-1032c50/constance/locale/fr/000077500000000000000000000000001527040271600237265ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/fr/LC_MESSAGES/000077500000000000000000000000001527040271600255135ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/fr/LC_MESSAGES/django.mo000066400000000000000000000035201527040271600273120ustar00rootroot00000000000000Þ•œx yhƒìoô8d� ¢#®Ò×bÜ?E L Vla Î…Ø^˜f\ÿ\ b%o• ™‡¥-4 ; E   ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: Report-Msgid-Bugs-To: POT-Creation-Date: 2017-07-02 19:01+0100 PO-Revision-Date: 2017-07-03 12:35+0100 Last-Translator: Bruno Alla Language: fr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Language-Team: X-Generator: Poedit 2.0.2 ConstanceConstance ne prend pas en charge les valeurs de configuration du type %(config_type)s. Veuillez corriger la valeur de ‘%(name)s’.DéfautLe type de la valeur par défaut doit être le même que le type déclaré dans la configuration. Veuillez corriger la valeur par défaut de '%(name)s'.Obtenir/définir les paramètres de configuration de base de données gérées par ConstanceIndexEst modifiéParamètres mis à jour avec succès.NomEnregistrerLes paramètres ont été modifiés par quelqu'un d'autre. Veuillez rafraichir le formulaire et soumettre de nouveau vos modifications.Valeurconfigconstanceconstancesdjango-commons-django-constance-1032c50/constance/locale/fr/LC_MESSAGES/django.po000066400000000000000000000051051527040271600273160ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-02 19:01+0100\n" "PO-Revision-Date: 2017-07-03 12:35+0100\n" "Last-Translator: Bruno Alla \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Language-Team: \n" "X-Generator: Poedit 2.0.2\n" #: admin.py:111 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Le type de la valeur par défaut doit être le même que le type déclaré dans " "la configuration. Veuillez corriger la valeur par défaut de '%(name)s'." #: admin.py:121 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance ne prend pas en charge les valeurs de configuration du type " "%(config_type)s. Veuillez corriger la valeur de ‘%(name)s’." #: admin.py:145 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Les paramètres ont été modifiés par quelqu'un d'autre. Veuillez rafraichir " "le formulaire et soumettre de nouveau vos modifications." #: admin.py:209 msgid "Live settings updated successfully." msgstr "Paramètres mis à jour avec succès." #: admin.py:271 msgid "config" msgstr "config" #: apps.py:8 msgid "Constance" msgstr "Constance" #: backends/database/models.py:19 msgid "constance" msgstr "constance" #: backends/database/models.py:20 msgid "constances" msgstr "constances" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" "Obtenir/définir les paramètres de configuration de base de données gérées " "par Constance" #: templates/admin/constance/change_list.html:68 msgid "Save" msgstr "Enregistrer" #: templates/admin/constance/change_list.html:77 msgid "Home" msgstr "Index" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nom" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Défaut" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Valeur" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Est modifié" django-commons-django-constance-1032c50/constance/locale/it/000077500000000000000000000000001527040271600237335ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/it/LC_MESSAGES/000077500000000000000000000000001527040271600255205ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/it/LC_MESSAGES/django.mo000066400000000000000000000035271527040271600273260ustar00rootroot00000000000000Þ•œxRy ÌhÖ?oG· ¼#ÈìñböY_ f p¤{O  ph}ætîc j;u±¶j¼'. = J   CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in CONSTANCE_CONFIG.ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.HomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: PO-Revision-Date: 2018-03-13 15:26+0100 Last-Translator: Paolo Melchiorre Language-Team: Italian (http://www.transifex.com/projects/p/django-constance/language/it/) Language: it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 2.0.4 CONSTANCE_CONFIG_FIELDSETS non contiene campi che esistono in CONSTANCE_CONFIG.ImpostazioniConstance non supporta valori di impostazioni di tipo %(config_type)s. Modifica il valore di '%(name)s'.DefaultIl tipo dei valori di default deve essere uguale al tipo del parametro. Modifica il valore di default di '%(name)s'.InizioModificatoLe impostazioni attive sono state aggiornate correttamente.NomeSalvaLe impostazioni sono state modificate da qualcuno. Ricarica la pagina e invia nuovamente le tue modifiche.Valoreconfigurazioniimpostazioneimpostazionidjango-commons-django-constance-1032c50/constance/locale/it/LC_MESSAGES/django.po000066400000000000000000000054101527040271600273220ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2018-03-13 15:26+0100\n" "Last-Translator: Paolo Melchiorre \n" "Language-Team: Italian (http://www.transifex.com/projects/p/django-constance/" "language/it/)\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.0.4\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Il tipo dei valori di default deve essere uguale al tipo del parametro. " "Modifica il valore di default di '%(name)s'." #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance non supporta valori di impostazioni di tipo %(config_type)s. " "Modifica il valore di '%(name)s'." #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Le impostazioni sono state modificate da qualcuno. Ricarica la pagina e " "invia nuovamente le tue modifiche." #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" "CONSTANCE_CONFIG_FIELDSETS non contiene campi che esistono in " "CONSTANCE_CONFIG." #: admin.py:224 msgid "Live settings updated successfully." msgstr "Le impostazioni attive sono state aggiornate correttamente." #: admin.py:285 msgid "config" msgstr "configurazioni" #: apps.py:8 msgid "Constance" msgstr "Impostazioni" #: backends/database/models.py:19 msgid "constance" msgstr "impostazione" #: backends/database/models.py:20 msgid "constances" msgstr "impostazioni" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Salva" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "Inizio" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nome" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "Default" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Valore" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Modificato" #~ msgid "Constance config" #~ msgstr "Configurazione Impostazioni" django-commons-django-constance-1032c50/constance/locale/pl/000077500000000000000000000000001527040271600237325ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/pl/LC_MESSAGES/000077500000000000000000000000001527040271600255175ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/pl/LC_MESSAGES/django.mo000066400000000000000000000017621527040271600273240ustar00rootroot00000000000000Þ• tÌ"* /#;_di o yç„l ƒ Ž ˜!¦ÈÎ Õß è   Constance configDefaultHomeIs modifiedLive settings updated successfully.NameSaveValueconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2014-11-27 19:05+0100 PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Jannis Leidel Language-Team: Polish (http://www.transifex.com/projects/p/django-constance/language/pl/) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: pl Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); Konfiguracja ConstanceDomyÅ›lniePoczÄ…tekZmodyfikowanaParametry zostaÅ‚y zaktualizowaneNazwaZapiszWartośćparametrparametrydjango-commons-django-constance-1032c50/constance/locale/pl/LC_MESSAGES/django.po000066400000000000000000000045021527040271600273220ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: Polish (http://www.transifex.com/projects/p/django-constance/" "language/pl/)\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Parametry zostaÅ‚y zaktualizowane" #: admin.py:285 msgid "config" msgstr "" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "parametr" #: backends/database/models.py:20 msgid "constances" msgstr "parametry" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Zapisz" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "PoczÄ…tek" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Nazwa" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "DomyÅ›lnie" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Wartość" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Zmodyfikowana" #~ msgid "Constance config" #~ msgstr "Konfiguracja Constance" django-commons-django-constance-1032c50/constance/locale/ru/000077500000000000000000000000001527040271600237455ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/ru/LC_MESSAGES/000077500000000000000000000000001527040271600255325ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/ru/LC_MESSAGES/django.mo000066400000000000000000000022511527040271600273310ustar00rootroot00000000000000Þ• |Ü !)I N#Z~ƒˆŽ • ŸÏªz:’ÍÜ5ö,=Ppƒ–   DefaultFailed to update live settings.HomeIs modifiedLive settings updated successfully.NameSaveValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Jannis Leidel Language-Team: Russian (http://www.transifex.com/projects/p/django-constance/language/ru/) Language: ru MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); По умолчаниюÐ�е удалоÑ�ÑŒ Ñ�охранить наÑ�тройки.ГлавнаÑ�Было измененоÐ�аÑ�тройки уÑ�пешно Ñ�охранены.Ð�азваниеСохранитьТекущее значениенаÑ�тройкинаÑ�тройкинаÑ�тройкиdjango-commons-django-constance-1032c50/constance/locale/ru/LC_MESSAGES/django.po000066400000000000000000000051201527040271600273320ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-19 20:59+0500\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Jannis Leidel \n" "Language-Team: Russian (http://www.transifex.com/projects/p/django-constance/" "language/ru/)\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: admin.py:113 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "Ð�аÑ�тройки уÑ�пешно Ñ�охранены." #: admin.py:267 msgid "Failed to update live settings." msgstr "Ð�е удалоÑ�ÑŒ Ñ�охранить наÑ�тройки." #: admin.py:285 msgid "config" msgstr "наÑ�тройки" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "наÑ�тройки" #: backends/database/models.py:20 msgid "constances" msgstr "наÑ�тройки" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "Сохранить" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "ГлавнаÑ�" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "Ð�азвание" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "По умолчанию" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "Текущее значение" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "Было изменено" #~ msgid "Constance config" #~ msgstr "Ð�аÑ�тройки" django-commons-django-constance-1032c50/constance/locale/tr/000077500000000000000000000000001527040271600237445ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/tr/LC_MESSAGES/000077500000000000000000000000001527040271600255315ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/tr/LC_MESSAGES/django.mo000066400000000000000000000035241527040271600273340ustar00rootroot00000000000000Þ•¤,ˆO‰hÙ BOoWÇ Ì#Øübz€ ‡ ‘HœVåw<´ Ã‹Ï [e(už¤¶u½3: ? I   CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG.Constance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.Current fileDefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.HomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Ozcan Yarimdunya Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); CONSTANCE_CONFIG içinde mevcut olan alan(lar) için CONSTANCE_CONFIG_FIELDSETS eksik.Constance %(config_type)s tipinin yapılandırma deÄŸerlerini desteklemiyor. Lütfen '%(name)s' in deÄŸerini düzeltin.Geçerli dosyaVarsayılanVarsayılan deÄŸer tipi, tanımlanan ayarlar parametresi tipi ile aynı olmalıdır. Lütfen '%(name)s' in varsayılan deÄŸerini düzeltin.Ana SayfaDeÄŸiÅŸtiril miCanlı ayarlar baÅŸarıyla güncellendi.İsimVarsayılana dönKaydetAyarlar baÅŸkası tarafından deÄŸiÅŸtirildi. Lütfen formu tekrar yükleyin ve deÄŸiÅŸikliklerinizi tekrar kaydedin.DeÄŸerayarconstanceconstancesdjango-commons-django-constance-1032c50/constance/locale/tr/LC_MESSAGES/django.po000066400000000000000000000061331527040271600273360ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-09 19:14+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Ozcan Yarimdunya \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: constance/admin.py:116 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Varsayılan deÄŸer tipi, tanımlanan ayarlar parametresi tipi ile aynı olmalıdır. Lütfen " "'%(name)s' in varsayılan deÄŸerini düzeltin." #: constance/admin.py:126 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance %(config_type)s tipinin yapılandırma deÄŸerlerini desteklemiyor. Lütfen " "'%(name)s' in deÄŸerini düzeltin." #: constance/admin.py:160 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Ayarlar baÅŸkası tarafından deÄŸiÅŸtirildi. Lütfen formu tekrar yükleyin ve " "deÄŸiÅŸikliklerinizi tekrar kaydedin." #: constance/admin.py:172 constance/checks.py:19 msgid "" "CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in " "CONSTANCE_CONFIG." msgstr "" "CONSTANCE_CONFIG içinde mevcut olan alan(lar) için " "CONSTANCE_CONFIG_FIELDSETS eksik." #: constance/admin.py:240 msgid "Live settings updated successfully." msgstr "Canlı ayarlar baÅŸarıyla güncellendi." #: constance/admin.py:305 msgid "config" msgstr "ayar" #: constance/backends/database/models.py:19 msgid "constance" msgstr "constance" #: constance/backends/database/models.py:20 msgid "constances" msgstr "constances" #: constance/management/commands/constance.py:32 msgid "Get/Set In-database config settings handled by Constance" msgstr "Constance tarafından veritabanında barındırılan ayarları görüntüle/deÄŸiÅŸtir" #: constance/templates/admin/constance/change_list.html:60 msgid "Save" msgstr "Kaydet" #: constance/templates/admin/constance/change_list.html:69 msgid "Home" msgstr "Anasayfa" #: constance/templates/admin/constance/includes/results_list.html:6 msgid "Name" msgstr "İsim" #: constance/templates/admin/constance/includes/results_list.html:7 msgid "Default" msgstr "Varsayılan" #: constance/templates/admin/constance/includes/results_list.html:8 msgid "Value" msgstr "DeÄŸer" #: constance/templates/admin/constance/includes/results_list.html:9 msgid "Is modified" msgstr "DeÄŸiÅŸtirildi mi" #: constance/templates/admin/constance/includes/results_list.html:22 msgid "Current file" msgstr "Geçerli dosya" #: constance/templates/admin/constance/includes/results_list.html:39 msgid "Reset to default" msgstr "Varsayılana dön" django-commons-django-constance-1032c50/constance/locale/uk/000077500000000000000000000000001527040271600237365ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/uk/LC_MESSAGES/000077500000000000000000000000001527040271600255235ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/uk/LC_MESSAGES/django.mo000066400000000000000000000055761527040271600273370ustar00rootroot00000000000000Þ•¼\ÐOÑh! Š—oŸ8/h m#y�¢³b¸0!R Y cÄni3¯�Mg‡>›tÚO ^ ;v ² D½  ¹ Í Eí 3 L e     CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG.Constance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.Current fileDefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Failed to update live settings.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueYou don't have permission to change these valuesconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: PO-Revision-Date: 2014-11-27 18:13+0000 Last-Translator: Vasyl Dizhak Language-Team: (http://www.transifex.com/projects/p/django-constance/language/uk/) Language: uk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); Одне чи кілька полів з CONSTANCE_CONFIG відÑ�утні в CONSTANCE_CONFIG_FIELDSETS.Constance не підтрумує значеннÑ� наÑ�тупного типу %(config_type)s. Будь лаÑ�ка, змініть тип длÑ� значеннÑ� '%(name)s'Поточний файлЗа замовчуваннÑ�мТип значеннÑ� за замовчуваннÑ�м повинен Ñ�півпадати із вказаним типом параметра конфігурації. Будь лаÑ�ка, виправте значеннÑ� за замовчуваннÑ�м длÑ� '%(name)s'.Ð�е вдалоÑ�Ñ� зберегти налаштуваннÑ�.Отримати/вÑ�тановити налашуваннÑ� в базі даних, Ñ�кими керує ConstanceГоловнаБуло зміненоÐ�алаштуваннÑ� уÑ�пішно збережені.Ð�азваСкинути до значеннÑ� за замовчуваннÑ�мЗберегтиÐ�алаштуваннÑ� було змінено кимоÑ�ÑŒ іншим. Буд лаÑ�ка, перезавантажте форму та повторно збережіть зміни.Поточне значеннÑ�У ваÑ� немає прав длÑ� зміни цих значеньналаштуваннÑ�налаштуваннÑ�налаштуваннÑ�django-commons-django-constance-1032c50/constance/locale/uk/LC_MESSAGES/django.po000066400000000000000000000076111527040271600273320ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-19 16:00+0000\n" "PO-Revision-Date: 2014-11-27 18:13+0000\n" "Last-Translator: Vasyl Dizhak \n" "Language-Team: (http://www.transifex.com/projects/p/django-constance/" "language/uk/)\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: admin.py:109 msgid "You don't have permission to change these values" msgstr "У ваÑ� немає прав длÑ� зміни цих значень" #: admin.py:117 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "" "Тип значеннÑ� за замовчуваннÑ�м повинен Ñ�півпадати із вказаним типом параметра " "конфігурації. Будь лаÑ�ка, виправте значеннÑ� за замовчуваннÑ�м длÑ� '%(name)s'." #: admin.py:127 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "" "Constance не підтрумує значеннÑ� наÑ�тупного типу %(config_type)s. Будь лаÑ�ка, " "змініть тип длÑ� значеннÑ� '%(name)s'" #: admin.py:166 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "" "Ð�алаштуваннÑ� було змінено кимоÑ�ÑŒ іншим. Буд лаÑ�ка, перезавантажте форму та " "повторно збережіть зміни." #: admin.py:178 checks.py:19 msgid "" "CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in " "CONSTANCE_CONFIG." msgstr "" "Одне чи кілька полів з CONSTANCE_CONFIG відÑ�утні в " "CONSTANCE_CONFIG_FIELDSETS." #: admin.py:250 msgid "Live settings updated successfully." msgstr "Ð�алаштуваннÑ� уÑ�пішно збережені." #: admin.py:267 msgid "Failed to update live settings." msgstr "Ð�е вдалоÑ�Ñ� зберегти налаштуваннÑ�." #: admin.py:326 msgid "config" msgstr "налаштуваннÑ�" #: apps.py:8 msgid "Constance" msgstr "" #: backends/database/models.py:19 msgid "constance" msgstr "налаштуваннÑ�" #: backends/database/models.py:20 msgid "constances" msgstr "налаштуваннÑ�" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "Отримати/вÑ�тановити налашуваннÑ� в базі даних, Ñ�кими керує Constance" #: templates/admin/constance/change_list.html:61 msgid "Save" msgstr "Зберегти" #: templates/admin/constance/change_list.html:70 msgid "Home" msgstr "Головна" #: templates/admin/constance/includes/results_list.html:6 msgid "Name" msgstr "Ð�азва" #: templates/admin/constance/includes/results_list.html:7 msgid "Default" msgstr "За замовчуваннÑ�м" #: templates/admin/constance/includes/results_list.html:8 msgid "Value" msgstr "Поточне значеннÑ�" #: templates/admin/constance/includes/results_list.html:9 msgid "Is modified" msgstr "Було змінено" #: templates/admin/constance/includes/results_list.html:26 msgid "Current file" msgstr "Поточний файл" #: templates/admin/constance/includes/results_list.html:44 msgid "Reset to default" msgstr "Скинути до значеннÑ� за замовчуваннÑ�м" #~ msgid "Constance config" #~ msgstr "Ð�аÑ�тройки" django-commons-django-constance-1032c50/constance/locale/zh_CN/000077500000000000000000000000001527040271600243205ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/zh_CN/LC_MESSAGES/000077500000000000000000000000001527040271600261055ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/zh_CN/LC_MESSAGES/django.mo000066400000000000000000000025301527040271600277040ustar00rootroot00000000000000Þ•ŒüH IhS¼Ä É#Õùþbfl s }©ˆ2SB – §·Ð×NÞ-18H    ConstanceConstance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.DefaultHomeIs modifiedLive settings updated successfully.NameSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: django-constance Report-Msgid-Bugs-To: POT-Creation-Date: 2017-06-13 19:40+0530 PO-Revision-Date: 2015-03-15 18:40+0000 Last-Translator: Yifu Yu Language-Team: Chinese (China) (http://www.transifex.com/jezdez/django-constance/language/zh_CN/) Language: zh_CN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; Constance模å�—Constanceä¸�支æŒ�ä¿�存类型为%(config_type)s的值,请修正%(name)s的值。默认值首页是å�¦ä¿®æ”¹è¿‡æˆ�功更新实时é…�ç½®å��ç§°ä¿�存设置已ç»�被他人修改过,请刷新页é�¢å¹¶é‡�æ–°æ��交您的更改。值é…�ç½®Constance模å�—Constance模å�—django-commons-django-constance-1032c50/constance/locale/zh_CN/LC_MESSAGES/django.po000066400000000000000000000052611527040271600277130ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Yifu Yu , 2015 msgid "" msgstr "" "Project-Id-Version: django-constance\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-06-13 19:40+0530\n" "PO-Revision-Date: 2015-03-15 18:40+0000\n" "Last-Translator: Yifu Yu \n" "Language-Team: Chinese (China) (http://www.transifex.com/jezdez/django-" "constance/language/zh_CN/)\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: admin.py:113 #, fuzzy, python-format #| msgid "" #| "Constance doesn't support config values of the type %(config_type)s. " #| "Please fix the value of '%(name)s'." msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "Constanceä¸�支æŒ�ä¿�存类型为%(config_type)s的值,请修正%(name)s的值。" #: admin.py:123 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "Constanceä¸�支æŒ�ä¿�存类型为%(config_type)s的值,请修正%(name)s的值。" #: admin.py:147 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "设置已ç»�被他人修改过,请刷新页é�¢å¹¶é‡�æ–°æ��交您的更改。" #: admin.py:160 msgid "" "CONSTANCE_CONFIG_FIELDSETS does not contain fields that exist in " "CONSTANCE_CONFIG." msgstr "" #: admin.py:224 msgid "Live settings updated successfully." msgstr "æˆ�功更新实时é…�ç½®" #: admin.py:285 msgid "config" msgstr "é…�ç½®" #: apps.py:8 msgid "Constance" msgstr "Constance模å�—" #: backends/database/models.py:19 msgid "constance" msgstr "Constance模å�—" #: backends/database/models.py:20 msgid "constances" msgstr "Constance模å�—" #: management/commands/constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "" #: templates/admin/constance/change_list.html:75 msgid "Save" msgstr "ä¿�å­˜" #: templates/admin/constance/change_list.html:84 msgid "Home" msgstr "首页" #: templates/admin/constance/includes/results_list.html:5 msgid "Name" msgstr "å��ç§°" #: templates/admin/constance/includes/results_list.html:6 msgid "Default" msgstr "默认值" #: templates/admin/constance/includes/results_list.html:7 msgid "Value" msgstr "值" #: templates/admin/constance/includes/results_list.html:8 msgid "Is modified" msgstr "是å�¦ä¿®æ”¹è¿‡" #~ msgid "Constance config" #~ msgstr "Constance é…�置页é�¢" django-commons-django-constance-1032c50/constance/locale/zh_Hans/000077500000000000000000000000001527040271600247115ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/zh_Hans/LC_MESSAGES/000077500000000000000000000000001527040271600264765ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/locale/zh_Hans/LC_MESSAGES/django.mo000066400000000000000000000035651527040271600303060ustar00rootroot00000000000000Þ•¬<°O±h jwo8ï( -#9]bsbxÛá è òdýKbS®  P9j¤«»ÔÛîNõ DNUe   CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in CONSTANCE_CONFIG.Constance doesn't support config values of the type %(config_type)s. Please fix the value of '%(name)s'.Current fileDefaultDefault value type must be equal to declared config parameter type. Please fix the default value of '%(name)s'.Get/Set In-database config settings handled by ConstanceHomeIs modifiedLive settings updated successfully.NameReset to defaultSaveThe settings have been modified by someone else. Please reload the form and resubmit your changes.ValueconfigconstanceconstancesProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2018-04-19 11:31+0800 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; CONSTANCE_CONFIG_FIELDSETS中缺少在CONSTANCE_CONFIG中声明的字段。Constanceä¸�支æŒ�ä¿�存类型为%(config_type)s的值,请修正%(name)s的值。当å‰�文件默认值默认值的类型必须与å�‚数声明类型相å�Œï¼Œè¯·ä¿®æ­£%(name)s的值。获å�–或设置由Constance模å�—处ç�†çš„æ•°æ�®åº“é…�置首页是å�¦ä¿®æ”¹è¿‡å®žæ—¶é…�置更新æˆ�功å��ç§°é‡�置至默认值ä¿�存设置已ç»�被他人修改过,请刷新页é�¢å¹¶é‡�æ–°æ��交您的更改。当å‰�值é…�ç½®Constance模å�—Constance模å�—django-commons-django-constance-1032c50/constance/locale/zh_Hans/LC_MESSAGES/django.po000066400000000000000000000053411527040271600303030ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Yifu Yu , 2015. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-04-19 11:31+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: YinKH <614457662@qq.com>\n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: .\admin.py:115 #, python-format msgid "" "Default value type must be equal to declared config parameter type. Please " "fix the default value of '%(name)s'." msgstr "默认值的类型必须与å�‚数声明类型相å�Œï¼Œè¯·ä¿®æ­£%(name)s的值。" #: .\admin.py:125 #, python-format msgid "" "Constance doesn't support config values of the type %(config_type)s. Please " "fix the value of '%(name)s'." msgstr "Constanceä¸�支æŒ�ä¿�存类型为%(config_type)s的值,请修正%(name)s的值。" #: .\admin.py:157 msgid "" "The settings have been modified by someone else. Please reload the form and " "resubmit your changes." msgstr "设置已ç»�被他人修改过,请刷新页é�¢å¹¶é‡�æ–°æ��交您的更改。" #: .\admin.py:173 msgid "" "CONSTANCE_CONFIG_FIELDSETS is missing field(s) that exists in " "CONSTANCE_CONFIG." msgstr "CONSTANCE_CONFIG_FIELDSETS中缺少在CONSTANCE_CONFIG中声明的字段。" #: .\admin.py:240 msgid "Live settings updated successfully." msgstr "实时é…�置更新æˆ�功" #: .\admin.py:301 msgid "config" msgstr "é…�ç½®" #: .\backends\database\models.py:19 msgid "constance" msgstr "Constance模å�—" #: .\backends\database\models.py:20 msgid "constances" msgstr "Constance模å�—" #: .\management\commands\constance.py:30 msgid "Get/Set In-database config settings handled by Constance" msgstr "获å�–或设置由Constance模å�—处ç�†çš„æ•°æ�®åº“é…�ç½®" #: .\templates\admin\constance\change_list.html:60 msgid "Save" msgstr "ä¿�å­˜" #: .\templates\admin\constance\change_list.html:69 msgid "Home" msgstr "首页" #: .\templates\admin\constance\includes\results_list.html:5 msgid "Name" msgstr "å��ç§°" #: .\templates\admin\constance\includes\results_list.html:6 msgid "Default" msgstr "默认值" #: .\templates\admin\constance\includes\results_list.html:7 msgid "Value" msgstr "当å‰�值" #: .\templates\admin\constance\includes\results_list.html:8 msgid "Is modified" msgstr "是å�¦ä¿®æ”¹è¿‡" #: .\templates\admin\constance\includes\results_list.html:21 msgid "Current file" msgstr "当å‰�文件" #: .\templates\admin\constance\includes\results_list.html:36 msgid "Reset to default" msgstr "é‡�置至默认值" django-commons-django-constance-1032c50/constance/management/000077500000000000000000000000001527040271600241745ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/management/__init__.py000066400000000000000000000000001527040271600262730ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/management/commands/000077500000000000000000000000001527040271600257755ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/management/commands/__init__.py000066400000000000000000000000001527040271600300740ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/management/commands/constance.py000066400000000000000000000067301527040271600303320ustar00rootroot00000000000000from django.conf import settings from django.core.exceptions import ValidationError from django.core.management import BaseCommand from django.core.management import CommandError from django.utils.translation import gettext as _ from constance import config from constance.forms import ConstanceForm from constance.models import Constance from constance.utils import get_values def _set_constance_value(key, value): """ Parses and sets a Constance value from a string :param key: :param value: :return: """ form = ConstanceForm(initial=get_values()) field = form.fields[key] clean_value = field.clean(field.to_python(value)) setattr(config, key, clean_value) class Command(BaseCommand): help = _("Get/Set In-database config settings handled by Constance") GET = "get" SET = "set" LIST = "list" REMOVE_STALE_KEYS = "remove_stale_keys" def add_arguments(self, parser): subparsers = parser.add_subparsers(dest="command") subparsers.add_parser(self.LIST, help="list all Constance keys and their values") parser_get = subparsers.add_parser(self.GET, help="get the value of a Constance key") parser_get.add_argument("key", help="name of the key to get", metavar="KEY") parser_set = subparsers.add_parser(self.SET, help="set the value of a Constance key") parser_set.add_argument("key", help="name of the key to set", metavar="KEY") # use nargs='+' so that we pass a list to MultiValueField (eg SplitDateTimeField) parser_set.add_argument("value", help="value to set", metavar="VALUE", nargs="+") subparsers.add_parser( self.REMOVE_STALE_KEYS, help="delete all Constance keys and their values if they are not in settings.CONSTANCE_CONFIG (stale keys)", ) def handle(self, command, key=None, value=None, *args, **options): if command == self.GET: try: self.stdout.write(str(getattr(config, key)), ending="\n") except AttributeError as e: raise CommandError(f"{key} is not defined in settings.CONSTANCE_CONFIG") from e elif command == self.SET: try: if len(value) == 1: # assume that if a single argument was passed, the field doesn't expect a list value = value[0] _set_constance_value(key, value) except KeyError as e: raise CommandError(f"{key} is not defined in settings.CONSTANCE_CONFIG") from e except ValidationError as e: raise CommandError(", ".join(e)) from e elif command == self.LIST: for k, v in get_values().items(): self.stdout.write(f"{k}\t{v}", ending="\n") elif command == self.REMOVE_STALE_KEYS: prefix = getattr(settings, "CONSTANCE_DATABASE_PREFIX", "") actual_keys = [f"{prefix}{key}" for key in settings.CONSTANCE_CONFIG] stale_records = Constance.objects.exclude(key__in=actual_keys) if stale_records: self.stdout.write("The following record will be deleted:", ending="\n") else: self.stdout.write("There are no stale records in the database.", ending="\n") for stale_record in stale_records: self.stdout.write(f"{stale_record.key}\t{stale_record.value}", ending="\n") stale_records.delete() else: raise CommandError("Invalid command") django-commons-django-constance-1032c50/constance/migrations/000077500000000000000000000000001527040271600242345ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/migrations/0001_initial.py000066400000000000000000000014331527040271600267000ustar00rootroot00000000000000from django.db import migrations from django.db import models class Migration(migrations.Migration): initial = True dependencies = [] operations = [ migrations.CreateModel( name="Constance", fields=[ ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("key", models.CharField(max_length=255, unique=True)), ("value", models.TextField(blank=True, editable=False, null=True)), ], options={ "verbose_name": "constance", "verbose_name_plural": "constances", "permissions": [("change_config", "Can change config"), ("view_config", "Can view config")], }, ), ] django-commons-django-constance-1032c50/constance/migrations/0002_migrate_from_old_table.py000066400000000000000000000025721527040271600317350ustar00rootroot00000000000000from logging import getLogger from django.core.management.color import no_style from django.db import migrations logger = getLogger(__name__) def _migrate_from_old_table(apps, schema_editor) -> None: """ Copies values from old table. On new installations just ignore error that table does not exist. """ connection = schema_editor.connection quoted_string = ", ".join([connection.ops.quote_name(item) for item in ["id", "key", "value"]]) old_table_name = "constance_config" with connection.cursor() as cursor: if old_table_name not in connection.introspection.table_names(): logger.info("Old table does not exist, skipping") return cursor.execute( f"INSERT INTO constance_constance ( {quoted_string} ) SELECT {quoted_string} FROM {old_table_name}", # noqa: S608 [], ) cursor.execute(f"DROP TABLE {old_table_name}", []) Constance = apps.get_model("constance", "Constance") sequence_sql = connection.ops.sequence_reset_sql(no_style(), [Constance]) with connection.cursor() as cursor: for sql in sequence_sql: cursor.execute(sql) class Migration(migrations.Migration): dependencies = [("constance", "0001_initial")] atomic = False operations = [ migrations.RunPython(_migrate_from_old_table, reverse_code=lambda x, y: None), ] django-commons-django-constance-1032c50/constance/migrations/0003_drop_pickle.py000066400000000000000000000044211527040271600275440ustar00rootroot00000000000000import json import logging import pickle from base64 import b64decode from importlib import import_module from django.db import migrations from constance import settings from constance.codecs import dumps logger = logging.getLogger(__name__) def is_already_migrated(value): try: data = json.loads(value) if isinstance(data, dict) and set(data.keys()) == {"__type__", "__value__"}: return True except (json.JSONDecodeError, TypeError, UnicodeDecodeError): return False return False def import_module_attr(path): package, module = path.rsplit(".", 1) return getattr(import_module(package), module) def migrate_pickled_data(apps, schema_editor) -> None: # pragma: no cover Constance = apps.get_model("constance", "Constance") db_alias = schema_editor.connection.alias for constance in Constance.objects.using(db_alias).exclude(value=None): if not is_already_migrated(constance.value): constance.value = dumps(pickle.loads(b64decode(constance.value.encode()))) # noqa: S301 constance.save(using=db_alias, update_fields=["value"]) if settings.BACKEND in ( "constance.backends.redisd.RedisBackend", "constance.backends.redisd.CachingRedisBackend", ): import redis _prefix = settings.REDIS_PREFIX connection_cls = settings.REDIS_CONNECTION_CLASS if connection_cls is not None: _rd = import_module_attr(connection_cls)() else: if isinstance(settings.REDIS_CONNECTION, str): _rd = redis.from_url(settings.REDIS_CONNECTION) else: _rd = redis.Redis(**settings.REDIS_CONNECTION) redis_migrated_data = {} for key in settings.CONFIG: prefixed_key = f"{_prefix}{key}" value = _rd.get(prefixed_key) if value is not None and not is_already_migrated(value): redis_migrated_data[prefixed_key] = dumps(pickle.loads(value)) # noqa: S301 for prefixed_key, value in redis_migrated_data.items(): _rd.set(prefixed_key, value) class Migration(migrations.Migration): dependencies = [("constance", "0002_migrate_from_old_table")] operations = [ migrations.RunPython(migrate_pickled_data), ] django-commons-django-constance-1032c50/constance/migrations/__init__.py000066400000000000000000000000001527040271600263330ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/models.py000066400000000000000000000010141527040271600237110ustar00rootroot00000000000000from django.db import models from django.utils.translation import gettext_lazy as _ class Constance(models.Model): key = models.CharField(max_length=255, unique=True) value = models.TextField(null=True, blank=True, editable=False) class Meta: verbose_name = _("constance") verbose_name_plural = _("constances") permissions = [ ("change_config", "Can change config"), ("view_config", "Can view config"), ] def __str__(self): return self.key django-commons-django-constance-1032c50/constance/settings.py000066400000000000000000000024141527040271600242730ustar00rootroot00000000000000from django.conf import settings BACKEND = getattr(settings, "CONSTANCE_BACKEND", "constance.backends.redisd.RedisBackend") CONFIG = getattr(settings, "CONSTANCE_CONFIG", {}) CONFIG_FIELDSETS = getattr(settings, "CONSTANCE_CONFIG_FIELDSETS", {}) ADDITIONAL_FIELDS = getattr(settings, "CONSTANCE_ADDITIONAL_FIELDS", {}) FILE_ROOT = getattr(settings, "CONSTANCE_FILE_ROOT", "") DATABASE_CACHE_BACKEND = getattr(settings, "CONSTANCE_DATABASE_CACHE_BACKEND", None) DATABASE_CACHE_AUTOFILL_TIMEOUT = getattr(settings, "CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT", 60 * 60 * 24) DATABASE_PREFIX = getattr(settings, "CONSTANCE_DATABASE_PREFIX", "") REDIS_PREFIX = getattr(settings, "CONSTANCE_REDIS_PREFIX", "constance:") REDIS_CACHE_TIMEOUT = getattr(settings, "CONSTANCE_REDIS_CACHE_TIMEOUT", 60) REDIS_CONNECTION_CLASS = getattr(settings, "CONSTANCE_REDIS_CONNECTION_CLASS", None) REDIS_ASYNC_CONNECTION_CLASS = getattr(settings, "CONSTANCE_REDIS_ASYNC_CONNECTION_CLASS", None) REDIS_CONNECTION = getattr(settings, "CONSTANCE_REDIS_CONNECTION", {}) SUPERUSER_ONLY = getattr(settings, "CONSTANCE_SUPERUSER_ONLY", True) IGNORE_ADMIN_VERSION_CHECK = getattr(settings, "CONSTANCE_IGNORE_ADMIN_VERSION_CHECK", False) DETECT_ASYNCIO = getattr(settings, "CONSTANCE_DETECT_ASYNCIO", True) django-commons-django-constance-1032c50/constance/signals.py000066400000000000000000000001021527040271600240630ustar00rootroot00000000000000import django.dispatch config_updated = django.dispatch.Signal() django-commons-django-constance-1032c50/constance/static/000077500000000000000000000000001527040271600233475ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/static/admin/000077500000000000000000000000001527040271600244375ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/static/admin/css/000077500000000000000000000000001527040271600252275ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/static/admin/css/constance.css000066400000000000000000000010711527040271600277150ustar00rootroot00000000000000#result_list .changed { background-color: #ffc; } #changelist table thead th .text { padding: 2px 5px; } #changelist table tbody td:first-child { text-align: left; } #changelist-form ul.errorlist { margin: 0 !important; } .help { font-weight: normal !important; } #results { overflow-x: auto; } .item-anchor { visibility: hidden; margin-left: .1em; } .item-name { white-space: nowrap; } .item-name:hover .item-anchor { visibility: visible; } .sticky-footer { position: sticky; width: 100%; left: 0; bottom: 0; } django-commons-django-constance-1032c50/constance/static/admin/js/000077500000000000000000000000001527040271600250535ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/static/admin/js/constance.js000066400000000000000000000050661527040271600273750ustar00rootroot00000000000000(function($) { 'use strict'; $(function() { $('#content-main').on('click', '.reset-link', function(e) { e.preventDefault(); const rawFieldId = this.dataset.fieldId; const fieldEl = document.getElementById(rawFieldId); const field = fieldEl ? $(fieldEl) : $('#' + rawFieldId.replace(/ /g, "\\ ")); const fieldType = this.dataset.fieldType; const defaultValue = (this.dataset.default || '').trim(); if (fieldType === 'checkbox') { field.prop('checked', defaultValue === 'true').trigger('change'); } else if (fieldType === 'multi-select') { let defaults = []; try { defaults = JSON.parse(defaultValue); } catch (err) { defaults = []; } if (!Array.isArray(defaults)) { defaults = [defaults]; } const stringDefaults = (defaults.flat ? defaults.flat(Infinity) : defaults).map(function(v) { return String(v); }); // CheckboxSelectMultiple: individual checkboxes inside a wrapper field.find('input[type="checkbox"]').each(function() { $(this).prop('checked', stringDefaults.indexOf($(this).val()) !== -1).trigger('change'); }); // SelectMultiple:

{% endblock %} {% block breadcrumbs %} {% endblock %} django-commons-django-constance-1032c50/constance/templates/admin/constance/config_history.html000066400000000000000000000037071527040271600330460ustar00rootroot00000000000000{% extends "admin/base_site.html" %} {% load i18n %} {% block breadcrumbs %} {% endblock %} {% block content %}
{% if action_list %} {% for action in action_list %} {% endfor %}
{% translate 'Date/time' %} {% translate 'User' %} {% translate 'Action' %}
{{ action.action_time|date:"DATETIME_FORMAT" }} {{ action.user.get_username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %} {{ action.get_change_message }}

{% if pagination_required %} {% for i in page_range %} {% if i == action_list.paginator.ELLIPSIS %} {{ action_list.paginator.ELLIPSIS }} {% elif i == action_list.number %} {{ i }} {% else %} {{ i }} {% endif %} {% endfor %} {% endif %} {{ action_list.paginator.count }} {% blocktranslate count counter=action_list.paginator.count %}entry{% plural %}entries{% endblocktranslate %}

{% else %}

{% translate "This object doesn't have a change history." %}

{% endif %}
{% endblock %} django-commons-django-constance-1032c50/constance/templates/admin/constance/includes/000077500000000000000000000000001527040271600307315ustar00rootroot00000000000000results_list.html000066400000000000000000000053561527040271600343050ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/templates/admin/constance/includes{% load admin_list static i18n %}
{% for item in config_values %} {% endfor %}
{% trans "Name" %}
{% trans "Default" %}
{% trans "Value" %}
{% trans "Is modified" %}
{{ item.name }} ¶
{{ item.help_text|linebreaksbr }}
{{ item.default|linebreaks }} {{ item.form_field.errors }} {% if item.is_file %}{% trans "Current file" %}: {{ item.value }}{% endif %} {{ item.form_field }} {% if not item.is_file %}
{% trans "Reset to default" %} {% endif %}
{% if item.modified %} {{ item.modified }} {% else %} {{ item.modified }} {% endif %}
django-commons-django-constance-1032c50/constance/test/000077500000000000000000000000001527040271600230375ustar00rootroot00000000000000django-commons-django-constance-1032c50/constance/test/__init__.py000066400000000000000000000001311527040271600251430ustar00rootroot00000000000000from .unittest import override_config # pragma: no cover __all__ = ["override_config"] django-commons-django-constance-1032c50/constance/test/pytest.py000066400000000000000000000035531527040271600247470ustar00rootroot00000000000000""" Pytest constance override config plugin. Inspired by https://github.com/pytest-dev/pytest-django/. """ from contextlib import ContextDecorator import pytest from constance import config as constance_config @pytest.hookimpl(trylast=True) def pytest_configure(config): # pragma: no cover """Register override_config marker.""" config.addinivalue_line("markers", ("override_config(**kwargs): mark test to override django-constance config")) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item): # pragma: no cover """Validate constance override marker params. Run test with overridden config.""" marker = item.get_closest_marker("override_config") if marker is not None: if marker.args: pytest.fail("Constance override can not not accept positional args") with override_config(**marker.kwargs): yield else: yield class override_config(ContextDecorator): """ Override config while running test function. Act as context manager and decorator. """ def enable(self): """Store original config values and set overridden values.""" for key, value in self._to_override.items(): self._original_values[key] = getattr(constance_config, key) setattr(constance_config, key, value) def disable(self): """Set original values to the config.""" for key, value in self._original_values.items(): setattr(constance_config, key, value) def __init__(self, **kwargs): self._to_override = kwargs.copy() self._original_values = {} def __enter__(self): self.enable() def __exit__(self, exc_type, exc_val, exc_tb): self.disable() @pytest.fixture(name="override_config") def _override_config(): """Make override_config available as a function fixture.""" return override_config django-commons-django-constance-1032c50/constance/test/unittest.py000066400000000000000000000063001527040271600252670ustar00rootroot00000000000000from functools import wraps from asgiref.sync import iscoroutinefunction from asgiref.sync import sync_to_async from django import VERSION as DJANGO_VERSION from django.test import SimpleTestCase from django.test.utils import override_settings from constance import config __all__ = ("override_config",) class override_config(override_settings): """ Decorator to modify constance setting for TestCase. Based on django.test.utils.override_settings. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.original_values = {} def __call__(self, test_func): """Modify the decorated function to override config values.""" if isinstance(test_func, type): if not issubclass(test_func, SimpleTestCase): raise Exception("Only subclasses of Django SimpleTestCase can be decorated with override_config") return self.modify_test_case(test_func) if iscoroutinefunction(test_func): @wraps(test_func) async def inner(*args, **kwargs): try: await sync_to_async(self.enable, thread_sensitive=True)() return await test_func(*args, **kwargs) finally: await sync_to_async(self.disable, thread_sensitive=True)() else: @wraps(test_func) def inner(*args, **kwargs): with self: return test_func(*args, **kwargs) return inner def modify_test_case(self, test_case): """ Override the config by modifying TestCase methods. This method follows the Django <= 1.6 method of overriding the _pre_setup and _post_teardown hooks rather than modifying the TestCase itself. """ original_pre_setup = test_case._pre_setup original_post_teardown = test_case._post_teardown if DJANGO_VERSION < (5, 2): def _pre_setup(inner_self): self.enable() original_pre_setup(inner_self) else: @classmethod def _pre_setup(cls): # NOTE: Django 5.2 turned this as a classmethod # https://github.com/django/django/pull/18514/files self.enable() original_pre_setup() def _post_teardown(inner_self): original_post_teardown(inner_self) self.disable() test_case._pre_setup = _pre_setup test_case._post_teardown = _post_teardown return test_case def enable(self): """Store original config values and set overridden values.""" # Store the original values to an instance variable for config_key in self.options: self.original_values[config_key] = getattr(config, config_key) # Update config with the overridden values self.unpack_values(self.options) def disable(self): """Set original values to the config.""" self.unpack_values(self.original_values) @staticmethod def unpack_values(options): """Unpack values from the given dict to config.""" for name, value in options.items(): setattr(config, name, value) django-commons-django-constance-1032c50/constance/utils.py000066400000000000000000000052111527040271600235710ustar00rootroot00000000000000from importlib import import_module from . import LazyConfig from . import settings config = LazyConfig() def import_module_attr(path): package, module = path.rsplit(".", 1) return getattr(import_module(package), module) def get_values(): """ Get dictionary of values from the backend :return: """ # First load a mapping between config name and default value default_initial = ((name, options[0]) for name, options in settings.CONFIG.items()) # Then update the mapping with actually values from the backend return dict(default_initial, **config._backend.mget(settings.CONFIG)) async def aget_values(): """ Get dictionary of values from the backend asynchronously :return: """ default_initial = {name: options[0] for name, options in settings.CONFIG.items()} backend_values = await config.amget(settings.CONFIG.keys()) return dict(default_initial, **backend_values) def get_values_for_keys(keys): """ Retrieve values for specified keys from the backend. :param keys: List of keys to retrieve. :return: Dictionary with values for the specified keys. :raises AttributeError: If any key is not found in the configuration. """ if not isinstance(keys, list | tuple | set): raise TypeError("keys must be a list, tuple, or set of strings") # Prepare default initial mapping default_initial = {name: options[0] for name, options in settings.CONFIG.items() if name in keys} # Check if all keys are present in the default_initial mapping missing_keys = [key for key in keys if key not in default_initial] if missing_keys: raise AttributeError(f'"{", ".join(missing_keys)}" keys not found in configuration.') # Merge default values and backend values, prioritizing backend values return dict(default_initial, **config._backend.mget(keys)) async def aget_values_for_keys(keys): """ Retrieve values for specified keys from the backend asynchronously. :param keys: List of keys to retrieve. :return: Dictionary with values for the specified keys. :raises AttributeError: If any key is not found in the configuration. """ if not isinstance(keys, list | tuple | set): raise TypeError("keys must be a list, tuple, or set of strings") default_initial = {name: options[0] for name, options in settings.CONFIG.items() if name in keys} missing_keys = [key for key in keys if key not in default_initial] if missing_keys: raise AttributeError(f'"{", ".join(missing_keys)}" keys not found in configuration.') backend_values = await config.amget(keys) return dict(default_initial, **backend_values) django-commons-django-constance-1032c50/docs/000077500000000000000000000000001527040271600210335ustar00rootroot00000000000000django-commons-django-constance-1032c50/docs/Makefile000066400000000000000000000011721527040271600224740ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) django-commons-django-constance-1032c50/docs/_static/000077500000000000000000000000001527040271600224615ustar00rootroot00000000000000django-commons-django-constance-1032c50/docs/_static/screenshot1.png000066400000000000000000000641341527040271600254350ustar00rootroot00000000000000‰PNG  IHDR€Y\=¯sRGB®ÎébKGDÿÿÿ ½§“ pHYs a Äî–@µtIMEÚ /):äÕ}tEXtCommentCreated with GIMPW� IDATxÚì½y`Å™þÿV÷Ü÷è¾-Y—-Y–,ù”ñ c°¹ !Ù@kØ„,$a� !üÖ@Ø@’�eÉ7!ÜGB¸}ø”d˲eÙ’u_3ºæ¾»«~ôÌH²%K–0–í÷ó‡=ê駪ºZª§ß·ª» c A€Ã.@AÐA´A-AAK@AÐA´A-AAK@AÐA´A-AAK@AÐA´A-AAK@AÐA´Ad*@Vüèר ‚ F ‚ Z‚ òå[‚2ðÔO­ïÜT_~}£ <õSë{_j¾âþ™²]zŸkA@6êÿ�ìEC6„ü|§E^}RõÙQegûmêÀ’gyWÅrÛöª;„©\&‚ —®%„á?þT{ZÂ1ƒI(Èñ¯»Þ¿n™â¯o˜Þí$LÚ% |üéBIûrD&Ð?ç)I.ôÜš'?yà˾G.Ï5‚\á–pä˜ú` üÃ{Ûõé%ŽgÖî¹ÛÑó{Ó.Wx» ìÇs0�þ¹]Ê+$Á¯üX¹l,á H{µá¹¬þŸÏ Ü3_Ø¿M„pŠ)·Î|Ï» ¯¬[ê+Ï M7S@È#«­Óüu‡ºÁ)CxêGöüzÓ÷+…o]ëYÏ ÄWWè³CÙGkRÆú¿±Ú}]–¨¢\K½æÿvð·ÿ›#¿Þü�·^i€ÓoXéY33˜¬àkë4Û®©õŒ> ŽÙ0…Ù×j÷õÙ¢†rm§Õ¯lÞ9CZþ�«½ )à+*ô/îRÚTÁ›®s¯›Šá‰¥IóÒ?u•®aªhÿŒëð‡KˆBX¾Ü}ˬà4-€ —onUÿõm�wh~/ø_?²JñßÏš+z¸B¸c…wa U0Åÿ�ù``¬®«ÌÁcó,Œï\#r‰Zp'ª”®Y¾„ü@âNY»xæ×ŠxÿÚzªA½¯—÷0š”X=×ù|†øð+ºÆ!éEª{S:?¬ûC%Ë*ò\_nÿ©/ö‘}2)KÁéýÿñmG¹Š?tPwx’s|ï"CDxð>ûµ®îˆî£n¢KñßXâz&KøÙˆÃÞ‘Û=fÃ8]à{ßv,Óð•u•�’ë{lx¥árRÜÿ}3ÔÖþé0ËœåY}•ýq0ÔÍp÷©?ج"q�[æy¿�{àÏš®ÑG½sþp¦W­ø�<Ö~Ró—™‹2£Y(˜ÌRkj¼B²·ÿ¡o*wÝ”Nñ�Èëç4×/²Ysµæ/G8NVq]1V™ç{Îã`¹Ô,‚v¹|zC(–‡³-Áßj¸ÿd‘Ÿ+5Ÿ4Ûÿ°Ö{W¶æÿ;ÉE7ƒ>ýSÌ›V䘜Èvm©?í ®E–w•«\Åíx3æW €*uͺþŸÎ‚HÒƒå\åºÖ@ª?Šyâ/@•zG·ý÷«}-V?°E>bnd¬†±œÅÎen×Û1Ï� WZ{sßÀ™¥éضÿ‹ý[aä˜\ùï¶•W9µb¾·EîPŸâûž.õ.�U¿Þ;zŽå\‡†{„Vf3è4üä]µ��õ-å«�©¹÷M ÃR|ƒhØ�×cÛȱñwŘežïYÿÁ"r±9ïE¨4D‚ cÊ‘F<&†ÇÂ1�š5,Сl–�)ʇî×¥ÙÚÖ³€ì À ÄIö$Ës)8ÔÿhŽ dŒ«> êt1±<_Ÿê­Z>2ª�îcÚ½~ˆŸHÅãÆh˜L,ϧàTý½q°ÒÊýêþ³ êÖ|ÖžZgYE/È>©’E.‹Iói™bžñœ�xŽÃ?³»9[ &° ‰ò8½}š÷š96±s4ƵĸÏÂøA�K.JàäL °‘¾æiéB×e�™Æa_ûµl¨ùÝœwð{àYxÈãÅt@§¬wHf!ä�÷";LÓôÈ-C³!þ¤–Å 1<4 çß0^œ¦è’÷ ­Ô.ïÐ/&äæ=CZî×í%C-SPÊÀ¨Q¹ÿ B²77«f¯ó?x_ïŸìd›üèiÕöãŠîñM v™�Nð�Á¸ÏÂy,‚ —œ%(L¡$pÊûFüÙ¬U?Ÿ+v7hþºWÞí&~¨2tï-žrÆùå/\açürÜ «6ÂîôìºÇ(tüu’žcÆïžÖçŠÒC…™þ;ò}w¬T¾øŠñ³�± ¡”\¸®÷YÀEJrÙZ-( èzN){FJ|¯.¡Ë°ñ-u_dl�Å çwI(òín˜kâeàŠ\iÊ�¡ø!;´º`ž)”$W÷ó3L.ÙÀˆ!˜ ùV7Ì5†dªÁJM¡„©q’DŸìðQÙá£Àâf8}›ÿžÅÂÎåþq˜áÏû4�÷Y@dÊs^s ,­ÄùÈ, Aå_ÉFL](x¸èu!Gç/ñ¦žW‹~ÿiŒ¾›2#—¸„–,ðÇ Ùaß)ÔþÛ ÄÈ@Æ’‹<‹UÐ{RÙ9ʤå øýõüë§V:w‘?ö¢ŸŒʡÃ3è–÷(TƒÑJ0@˜nÜóã9Gc—9¡³€ È%%ˆsŠ|za“03Ç¿ ‰�Oñ—7Œ{\#í’m9Å-,t=}»øÉi™O.Î,ð•kxÐó˜·R¿Gp–cå�ÚêÃ6HÎñ]—ȹ€*ïäôú-3í׮ؔªÙk!ÚdÿMsBr‡úw{G^n4ކ‘†/ô_Ìr,ûú€æ ZZ„z]"窸¸çGüÑÆ6ÕávY·›ðZ¡lŽw:p[«d‘*ˆ¥•g³C_¿Ö#oâWS§è?Ǫÿq�£ñ”yþgA�KÝÖ¬v†/ |§EùÁçªO«GÆã*>2ÿÚ纳Øû­\€_W«Ýøÿ͇ì¹ç•'q©~ù ܳڽj¡{ãÚÔ¿xSvûýŽiÁp>ƒy•/¾ln½Ú½v–ûþR€ _[­ÿÛvÍqïÄFݪ_½½«Ý«¸çI•¾.»ù»Žü‹{~Ù– åªÜÀêe>`ÄÒ¡úË'º4 ®#ê«5ü~šó®Yîg€â¿ŸUôÎé�ë�§Ìó> ‚Ly&ý UàéÚ³O˜ïyOá¿p×ÊIî?|Ç#ìŠ}p7Þâ„ r¡˜ìñyµ¨ð8É—û„ÅÐéNBKú€;ÒÈ£ ‚\8&qË'–Jgy2�ÛÖ û2ÓǼpó·ìÅ}ªÊNÞË‹9ù¾UÓ¨¯Áð^7.gD™š– n_ë*øÊ]†WZ¿ÔÁšrõͲòÙÞoÍftÉ÷¡ÿóU/>+ AäB2é¹Aärß½Œ ‚ % ‚ h ‚ Z‚ ‚–€ ‚ % ‚ h ‚ Z‚ 2Qc {AÁ(AAK@AÐA´A-AAK@AÐA´A-AAK@AÐA´Aä!ûÒJò}îáß×ämøõ#Åê)v _Vó&SÎï¢Ë¦Í‚\`K`Þ¶Ÿ~²£ª®­ßÏ@¦6Å&$§e,YsÍLÃ9C��eßgû,!yRùõåIrìóóçB÷!ž#AÆm Ì{êí§ŸßÞ3d“à³[[íÖÖ.âaKPæÜñÃG<€Ó¦*‡îjÝÿÑçÒefö5—þp3Úa^Pí…îÃs”?™ãEä2´Ú·ÿ�°˜‹oX»hz¬Šz,­§O=:Ä&8mjNÞåßa“9̯¬‹˜…’'÷x¹ÔÇ+tüµ/>òB�Ó¿ý??Z`2ÎÐ`€Ê•20RÒypËD³Ò,ÔW»ãÓ­ûkNw9CrCj^Ùò5k®ÊÖ�ž‹¢öc¿·íXkWOŸÃ'𚸌üÒ¥«W/œ¦‰ê„þê_çq«ŸÓ§—^{û�ñ>þÒ(ÍËûέl}ÿ½]'zÊ„Âå7ßuc±ºc×Ûo|r¨Å)* WÞ~ÏÚ"#æð�lºÅµùï[�êñ1™qZÙuw|}åt-UË‚=ÕŸÿsËÁ­½8•)!%mZÎÜ57.J”�݇CÊüîÏoèÿôƒGû{ýO¾>�³¯Î§üas ÔÓzhó统líñ¼6~Z~éÒë¯[0M=ãìA.©Ä'ãhúè�-òå%yÓâu’ p åÄ—,1ÏÉ÷ŸÿÍæÝrvÖîx­¶âø7~|ÿU £4MtœÜ{¨Î6t‹·¯ùÈææ#5Ý?þÏ›§«sÕ¼úÌ8¥ÁËÕ^ùþsõɣό6¿ûË?Úüè©ýü�ÏYç*�Vv±È–O_|Qóó¯JGg5¿óÔÓýžˆ-9Z¾ýœMþä–ÆŽÜOÌUó—§ÿXéâx~»¥Éni ”¬^”x^¢æwž~~ÀÞ}5BÖÝÿû?¯u )×ÓÛtøó¦ÃûŽÜýÃû—œÕSçÙ-‚L]KP¦ÍËW® @Ï¡÷^:ôoHÉ+(™¿lÅ‚é~taÎ?|Äã;ýæï>èH½é¡;r”á¬4óÖ½ý§°hg¬Z·|F,ë«Ýöþ¶Ó~÷Ñ¿½²'ÿ‡+âG,š3ä_³.Fk6hÕ aAoóÞ÷?>îËÖ÷�\ý‹L\¨í³×Ã~`˜}ãm˦AÛîw>8:úgÓλí›óµ ÿËö.Ð_] ± n¿m®ªîݿ¶Ý{VÞ˜2vo…ú}©‹oûZi’ؼý­�j=BýÖƒÖò¯%�¨ ´í=*ù�jæš;Wæù »¿«¥¾ö”(»ϨzÀo(XµvÉŒx…·w@£ãؘ}uåG¬;þ7ì|jùºëKÓ4þÎßýco‡®£{yGÞc×&ò“éA¦®%}É7î¿ú…—¶µ‡¢×„ήº]u6ïºá‘‡o˜®ùbSJCû|ÚðÏÚÔœ¼¼ðµ:ó6l«”.e¹×]]”¤€äÙ+לÞÖ ¬ygeï’ÕI#µŽ7Í,IíúdûGµÍ]vÿ°¬m¯µ†™8ëá#Ò•±vá·¿ýµ™j…ÙëO~uÀ3òj~óîkòU4Ó¾}ûkmºòoýËÕy*šiß¹ëµ6°µ 0K�|ëûw/0r3‡ú^ô70òØGx¥1>1%=-V''Åó–®OžYVæí?|ðj)¸šÞ6f_)Ç_~Ôzíi—üjö}ßSj PP8#Öõ“—jm»YW¬ÞWçÙ-‚LáÄp†¢Û7þriÝ¡CGNœjhh´¸Ã£‹ÐòÑë{üç5 üùÖ+:Ú{…p! ï¿øÜ™ß÷5õ 0’%ˆ}_üþÉ¿�ŒXjÀå§ÌÞé�~N*H“r#D•Z�GnLbA²8•^Þ’0#I œÒÞ"D6žãJ�—^“ËkcÃã¬àFÓ*2W\•X±Õ à8ðÿž9œ6~ZNá¼e«–ÄÊÏ+¯“^>'Nv¾}5„�–�ðÁ–åè#M$ºœ²T¨i€�–³ìó<»A�©l œ*©pé�…K€ú¬Ç7ÿåOŸ4† »®'4K€sŽ£�¡Î�‡Ç¸˜9k×.š«–ý/~ØlbƒœL% ¾„;kËù&ÛZeDÂÉÆUö­ŸÈû⋊ã§Ûº-=v?õô6ÝÙ|´²õ¡'ÿµè|&`UFwáûjìS÷%t ‚ SׄÞcu�ì´èjN�X´biú'�MÀÉÇ´BƒJxCz‚ zÐÌÿþ¦oh†ÒJ¦‘£‹®p0}Ý¿¬Y` Ì_È=,µdL1±~°œè ,˜¡€@g�e*ö?c J.¾æ¶âk¤ÃsÕÿã™ç6÷¸«÷¶ŠfªÎч#õóùöœGù‘_™˜ÌX8Þ�‡O»”J�s7î”vˆÉŒÁt‚\¶–êÚüâïë5i³ËŠgd§'ÕÄß{jÏMá”@A¢âÜá…R¯ð@Ǿ/ªÕ™™:13ݨÉ]Y¦=~Ðà=ôÒoU7,/J3ÊB›µíô‰£G{ ¾÷³Û3GXpÃëâõhþøým²¹ ÔR³åƒ=ΡûÈ“JKÌŸo³x¼òJâmK3 mÏ»û=S°û}Ç~ÿÔ?I~Q^Vj‚I§àü½ÇO;"£ô˜}8ÆÉO_M |YÂü%é½×8ú¿yµÿºÒ4µ¿³ú³¿•"’ô%óÐärN1OGÍÝg|‘´ê¶ùæs/$T¤”dÉ*ëoÍ{¨€ô;ŸÚ¸` €`@†?L%(A JP2%NAäú„±¨;ÒGùÌ�HÝLz¶3JP‚” dªJ&%P $l+ ˜ôÊ”ˆûHŽ$} LÚ�0” %(AÉ•L>JˆT�IX$v`á £¡P¬%(A JP2Å$“³`„‚ä R|@­_V ×P§mÛi_0â>ьՙ�¥$kt tX¼Œ0àT Kbb!PYÝo¥#I(P)¢R,âmç¨%(A JP2–dr– 2�æ0Æ( Ä«ÑgÐÆóÞö`¤e €“†ø¡Ÿ–¬‹…À€Åãd0¬i”16’$E³al¬ZP‚” %cIF»'mœ‰#ž¸¯^oléëGŠÌ$­BmŒ‰” 4 ðòô$ç t÷û ŒŠdð3¥R6Œ„§ ‰®µ•"!¤Ȉ1 C†” %(—dÒs ÒMD––¢ ÝíŽ&%@ñ«R ¥ZmŽ®ßjó×6ºòõÉ™12«×z=0ʘHPÿ‰v_|¦:#+.ÃoûxÀïÃ÷ÚQFÅðT3£"©tlÑ£• © Ñt)S6¤a(A JP‚’qJF�°sO?TUUÕ:�Cç)†þ;|Ù«”à þE JP‚”L)I�Á1ñ'¡RJ#7ÉEÖ4 þ8¤,|OZ$*A JP‚”LYÉ$GC†:¬DÂ" a!rç\Ô�P‚” %SS2ÙGáµC,ò¥h<2xëaLZžî@ JP‚”LMɤ,Ad‘; "à|ØRd²{pî›EvC JP‚”LIɤGŒ²ÈS‰HØŽX´iÕkd™“ô$�ð” %(AÉT”LÆ6¬)Aär¡ªªjÄív ‚ ‚–€ ‚ % ‚ h ‚ Z‚ ‚–€ ‚ŒÙ„•¢«ïóíõÛNXý €7Æ›‹ ³n[’‹.ƒ ȗǪWF?o~pûlaçã•©Oν¢£ÑÙñ»?¿_[>7ëšÙ±zgß;›ZBø Œ È—ÌüÅóç/ž?Ú·Boýþá·_·¸Fü:`Ùô‹}U�qlœ¨²b:¯™±eçþ_îqƒ!nY‘ºÿTû‘=‡,°tÓJ£bìú¼õæââôØSí'OýúSÓ¯nIЊÎ¼ZñÏàã“Vf©‚¶�êÆž^-A.†æ‹ÎÞ8˜Aœ»jÙÜu¹©ŸïÝz:° D%�I¢ÛúæÛ5{ˆFElA5Œ¾q2~°xýìîg¤sGÔ³g·-ÿ†åWÅñˆ,9?÷N¸Aôîûdÿ;ín»�/¼vÁ¥Z9ОªŠMÛìOQÍ^Qvï­<`ÙôÜqmž¦«ÓãôG÷Ñm}ëÝc{ú™ZÉ;ê �.*SRk퉗wX¬AJ•¦k×ι1C>WœÐJƒý~P¦è8öÿþù}ûÂ6 ]ÚK˜ÖÝT\¦Œºˆsûa7€æÆYp{"*Sýø� ݇[[—Ìλ¾ØoÜYZ¦ažæ±ßŸ²œîl %Ìd�€Úd˜>=.;¥à^¹ *ðïA.«|Qôs0šQ8ÃïóÁ®Î®Á Pw[%Kü~ŠÎXª{«ÂÒ?;3ž`�Škf,x¡Ü ÷v>ùB+À('áåë‹™»5ù›_ì=`Xr×SC.š½§\šEÉ# Í Åß]¯�îº�o44•äË9óÌâ§ç¨4„z~úꩆÂÒ``.*Ùpë�=e�Škòþ¦\/÷t<ùÛ6õ4ünŸìî{¯ÎW_{íã4?P�9‰+ã I9¹Y à÷·:é¼XuYy¶ÉãÞ{Èê8§‹X½ Î4p 7cz¼ÖYc´U§7X~PÆÜ²2¡þóWCýË õ_2Tøg„ —Á@Èïóù½þ`08ü¡±ÒÊ͘Ÿ.#|nFÚææ {Æ×b89÷wªW®Õ+€çe$|a:ÂÆ ûÁºÙàj%îÖ½ŒÃüà܃™L3+S£³ÙjvS)véûࣖê.¯[^£��²gȹ¿S½b­N2™Œíoènéö¿ð‡võ²§8™eC“Êõó3e_œô|¶¥½üëÓ,-X°4ŸÛ8E¢j=¾V'�¯æB.§ 4Z=@ OXh À@u®³äïí³€Noâ¨@òJ_(¶tÚ››ÛßÛ×Û[ÓÚ|}R‰ÿ|ä2áÐÞCÒ‡…3|>ksË™#¯`ë)ÛÿP5°@Pð÷®Zª“1&2";ã�Ÿ#nœh|@Ü­ànÑ@¦ÉÓzw…ÊsGÏáéÝ—‚í­×O ,ž÷Øz“™Z7ýºiÔ=‘sʤ sŸ»5î˺ž˜›(f_[PÔTsìtÍ�~Õ:'ל¨ðu�á"†•¥ºí{ܼ~Èž¯î?ÕfH.�6M ª§›à¸ÍùÚß5¤Èì]=ugŠ�Ÿo®=ÞÚ‹`Ú‚Ì @Àöê‹•ÖÔ¤¢dµÌ €ÑÃã‚\&Dg V½¸Òçó…!8kª»¥¥Î8óÙûs’yWÝ¡G·vt-ž‘!ך¼šý g)}½v›(�B#m<?Xr×S{^Û¸x¡cd?^»t‰é'Õ캫xY‚Œ0±§±e‡+ñ–‚3(‚MPÎÌ1šåÌÙbë?G«äÚB£÷Pk`áL…¯Ïa€‹ÏITï=ùyç‚Så„[”ÙII_u” ‹™öÿ¦úx[ÃŽS¶#G¼)>vaÞ´4€0¢‚Ï\¶ð8öÆ!ë® ¥nÎÒ‚]jT¯ÿÚºüÖ¿7íë*Ñ* IDATÙçÖ—%'ôµ÷ 7wG{G]o�É´ÅKg}g‘N¼¶¬Ðøé©î�íDž’=}ýêÜtœ[F�ËÉ΄ŽWØæÎJˆ\ ê²²Š5»­¹w§hW¬É:ööη(â5~)Ã�´qBH®0²sÑœ�´î¯oo'Td긄k®Iù’UsËõó/mþX­LLÕ�˧8íŠ5™ÇßÙ±á™9Aë#< OÎ{ô†ºWÞßñY@]zÖCÙI“éj~çèTUU•••]œ_„3Ö§"rå1¥nU»&-íg¿¸Gr…¯¾v*"/“ðµ×>ñ©òáûr’'zkðh;^W#2¥™Rw,oíè¸x•3W}õ„x©L×­ËOº�Š˜ÚQ‚ rm`Ç!‚ h ‚ Z‚ ‚–€ ‚œ‹q­8²ÙlØS‚ h f³{ Aä²G‚ Z‚ ‚–€ ‚ % ‚ h ‚ Z‚ 2~&ô$Ô³ŸY�O±FäÂ0¥Ž="��W¦>9£A�¯‚ù‹çÏ_<´o…ÞúþüÃo¿nq�v û‹}U�qlœ¨²b:¯¾Y´Ôžøå·þûó[6<»ùÑ?ù¸-H¿¼J§X”0!»åïŸ�ÚÙàtR…:+;ë;7ggÈXètEíëûºê"Å´ÂÜo­ÉÊU‘H�¡¹j®¡í„µÍûƒ/*SŽ^‚ HÚUÓáÏN�ëh>îN\¤#_eÝ{^ÛX’ußY±³Ÿ8²i wûÝ+‰ã ºë›wöiâ%Л`|�ÿxµâŸÀÇ'­ÌRmÕ�=½bv†Œví?ðä»")yå ¥¿«kßñÚg©öW·&ê"ÆÝéQäÏΚ%×'óç(ÿäògh¾èì�ƒ$Á¹«–Í]—›úùÞ­§ JTRêCt[ß|»fÏѨˆ-¨†Ñ7NƯŸÝýÌ€ôã`îˆzöì¶åß°üª8ž‘%ççÞ 7ˆÞ}Ÿì§Ým÷ñ…×.x T+ÚSU±i›]à #ªÙ+Êî�£•,›ž;®ÍÓtuzœþèž º­o½{lO?S+y‡C½áÑEeJj­=ñò‹5H©ÒtíÚ97fÈ'ãŠÂ.µÉ0}z\vJÁ½rATέì¹ijG”‰F]{¯ûtGk(±0¬4­»©xp*"8J9‚\1ù"B3 gø}þ` ØÕÙ5˜ên«d‰ßOÑKuoUXúggÆs,PñaMÃŒ/”äÞÎ'_heã$ü |}1s·&ó‹½ ÃÞ»òžri%�4430w½ºë6¾ÑÐTT’/çÌ3‹Ÿž£Òpêiøé«§ K €�¹¨díCö”*>¬iÈ[ø›r½ÜÓñäoÛ ÔÓð»}²»ï½:_M|íµ�ÐPü@Aæ$Æõ J¥C¥Ñ²…ßÍF1·¬L¨ÿ¼ÇÕPÿrC=Ä—Ì{âÆ$•èr@ðXeÓ±!çyÔ—b�Vþ¡ ÈF0òû|~¯? ÿFh¬´r3æ§ËŸ›‘¶¹¹ÂžñµBÎý�ê•kõ ày™4`�¸qÂ~°n6¸Z‰»uïãÙïaõ]•2ͬL�ÌfS¨ÙM¥Ø¥ïƒ�Zª»¼nAtx�>6Êž!çþNõе:Èd2´¿¡»¥ÛÿÂÚ9`ÔËbœâ¤.õ'$åqjŸ«ª#87GA€¹;,� Vë8*�¼ÒŠƒ-�öææö÷öõöÖ´6_ŸTÂ)5pÜ£»mò›â¹ˆ›ú@p¤ZF+W4!È•Á¡½‡¤3 gø|þÖæ–3G^ÿÀÖSþ>¶ÿ¡j`� à9î]µT'cLdDF†�ú#nœh|@Ü­ànÑ@¦ÉÓzw…ÊsGÏá ’q¶·^?1°xÞcëMfjÝôë¦Q÷dL`DÎ +’R0Ì}îÖ¸/ëZyB– 7^3G»{Ÿç‹×¶Ô%Mào³úBI¥™™r€€íÕ+­©IEÉj™'£1†�¯]hܾÍñÎÿîn(ˆMT2Gou»êß]4ò›�C£”ƒ È@t¶`Õ‹+}>_(‚³¡º[ZêŒ3Ÿ½?'™pÕztkG×ârm¡É{ Ù¿p–Ò×k·‰ÒÀ5ÒÆó÷ƒ%w=µçµ�‹:FöàµK—˜~òQÍ®»Š—%È{[v¸o)ùÚ×&(gæÍræl±õŸ£Urm¡Ñ{¨5°p¦Â×ç°‹ÀÅç$ª÷žü¼sÁ�©rBƒ­ Ê줤¯ÐuìhK5§Òæ&'Ë„+­A®,$?88^aK˜;+!r¥¨ËÊ* Ôì¶æÞ�¢]±&ëØÛ;Ü¢ˆKÔ„³ÓÜH'„ä #û1ÍÙHëþúööw@E¦ŽK¸æšô‘/h1·,Q?ÿÒæ�ÕÊÄTݹ|ŠÓ®X“yü�>‘™´>Âóòä¼Go¨{åýŸDÐ¥g=”�4™®&áY€Ñ©ªª*++Ã_JA. SêVµkÒÒ~ö‹{$Wøêk§‚ ò29_{íŸ*¾/'y¢·–�6°ãU7‚ Sš)uÇòÖŽŽ‹W9sÕW?ññ@ˆ'œÊtݺü¤ p«1Z‚ È%1ÌýUÁ…­h� ‚ % ‚ h ‚ Z‚ r.Æ5½l³Ù°§AÐššš°§A.{ƾU A¹BÀ¹A-AAK@AÐA�s1öŠ£ªª*ì&A�ËŒ‰? Õ®HÇîC¹l0Û'%ÀÐ7+3„ ¯(`Lz0éŸá_¢%(A J¦˜d‰# ’#0uB@úÈ"Ÿ‹¼"TÚˆ” %(™Â’ G ” Û F¤È!ì>’#I(“ö# %(A JP2E%“�"UDc‰X¸Âh(ëE JP‚”L1Éä,¡ ù‚ŸN£ÉKѦäJ@E‡Ã}´Å㢃«3% KIÖè@è°x= X4ÿÅÎ’pª…%1±¨ªî·P •2`£K†4ŒJP‚” ä\’ÉY‚È@šÃ @£@dzCù ½h¯³ÞC‰BžhR¨˜ÛÉÂ� £p†d|Z².Ox·èÁœ!áX$:a”ñ´sH¢µD#'” %(AÉ(’ÑîIg∅'®Ãuð©éz=°¾ëþ�€ÆŽðl¶*&fQ¦Z/#ôúZl�n¼ª|Nl,€rÞœT€ÀÁª^ ULLùð�›Ü”Ò°�QÊ(àäi¦¼8¥– B_Ÿëx»ÇI�1àTÚÒ);~N,rºŽõ ªcn‚y1/lnð ŸžeNU�§ÏÑÍé %d°†4l¸�FSmƒ¾‰” %WŠ„Ÿ”%P Í\3$¼®‰Q Ò=a,<‡M/—§¤j�ÑúTJ»(X{ý¾éz=½žN0`„;{g{8J`Ld”“M3sÁc ¶æ #,®,ÆlÖ‰×Êd-êZ�m &ÇÅÆHÆ‹DKƒ Âh¸íC�EНJP‚”\A’IE "e@¾zÝ^HÖ)¦™¸‹ÀF€PÆdª‚,­‘;šš²ümzL¢‚P{_ÅI‡S„)Ñ0” %(™ê’I$ކ< uX‰„EÂBäι¨;]x uô}´³/z¤CW[]܆¡%(AÉ”–LvÅQxÕ?‹™ ¡'—1cZšì]‡:;^}G™ÿPAfxgÏ®ò¢´„¼nký€åÍ÷g?8#ƒ§�{þr·x]ÉœøxÑþEoÔ}�ÿxµâŸÀÇ'­ÌRmÕ�=½"Z‚\V Í�½q0ƒ$8wÕ²¹ërS?ß»õt`A‰JJ}ˆnë›o×ì ±Õ0úÆÉøÁâõ³»Ÿ�~ÌQϞݶü–_Ç ²äüÜ; àÑ»ï“ýï´»í>¾ðÚ”jå@{ª*6m³ ùB+À('áåë‹™»5ù›_ì=`Xr×SCòÞS.͢䑆f†‚âï®×@wÝÆ7šŠJòåœyfñÓsTB= ?}õTCai00•l¸uÈž²@Ň5 y S®—{:žüm„z~·Ov÷½W竉¯½öñŠ(Ȝĸ>KˆýÁcåe ±¿¡ö‰7Z÷|Z¿<¿8ĸÎmùóÿ«ØÑÀ)âͼK꘡^­ÑAä‘Þb Ë ÚÙ‰gu«"æ–• õŸ÷¸ê_n¨€ø’yOܘ¤Â? ¹L B~ŸÏïõƒÁáß�•VnÆütás3Ò67WØ3¾ÃAȹ¿S½r­^ø¨¥ºËëD‡×èc£ìrîïT¯X«S€L&#@ûº[ºý/ü¡�F½,Æ)Nj\Ÿt¶…ð1É&3´Ú~9Ó×SsrG?@|Á³ßÍN†¯Vn ƒçˆ“™U¾@‡›ÎWŸ§É+}¡8ØÒionno_ooMkóõI%¸ A./í=$}˜Q8Ãçó·6·œ9òú¶žò÷±ýU Ïq瘟:c"#²3ø9âÆ‰ÆÄÝ î¶ýdš<­÷pW¨wkÜ—uM<Ù¹„Sœ`mjoPd¦NSG8t¹’°u}²[T÷uÕ�§l¹aIžâ‹£ž÷_=Ø3CG{­ƒ.²½úb¥55©(Y-óƒ`4Æðøçƒ —ÑÙ‚U/®ôù|¡@ÎZ„êni©3Î|öþœdÀUwèÑ­]‹gdȵ…&ï�fÿÂYJ_¯Ý&­†qãùûÁ’»žÚóÚÆÅ #ûðÚ¥KL?ù¨f×]ÅËd„‰=�-;\‰·ŒxÍ,ØåÌ£YΜ-¶þs´J®-4zµÎTøúv¸øœDõÞ“Ÿw.¸1UNh°µa@™�”t‘¢„È\QL›•sÏêTóȆHbŠfß}ºò­“ö�ûC¥eñ‰àê‡'\?ÿÎÀ‘÷Nö}qØ3sVLñ„S™®[—ŸtH„–€ rI@ŒsUpaëÀÇÞ!‚ h ‚ Z‚ ‚–€ ‚œ‹qM/Ûl6ì)A´€¦¦&ì)A�Ëž±oUCA®p.AAK@AÐA´Aä\Œ½â¨ªª » Aä2câOBµ+Ò±ûA.LÁö F y2F€°áï%Œ…ߒɤ†‰” %(™b’ 'Ž€JŽÀXÔé#‹|f,òŠPi#JP‚” d K&%P $l+ ‘"‡°ûHŽ$} LÚ�0” %(AÉ•L>JˆT�IX$v`á £¡P¬%(A JP2Å$“³`„‚ä R|8�&/E›b�+ ÷Ñ�‹f¬Î–P)ž¡R:+bTl /KKÔhAè´z]âø$¨%(A J®<Éä,Ad Ía ŒQ 2½¡|†^´¿×Yï¡D!O4)TÌídáÀ…Q8C2ÓDS[уQBøÔd],,Ÿdµ %(AÉ•'íž´q&ŽXxâ:\Ÿš®×ëk±îï(hìÏf/OK3åÇ)µ<úú\ÇÚ=.‘1N½xnl­=bb¼RÅBmÍ}Õý"cĘSšªÖË€ùÜ§^\  œ7' p°ª×"‚*&¦»«ÎIåz]δxy°ûÀ€@áŒc9Ãs£Ù¹A«” %(¹%ü¤,�iæš1 áuMŒR ˜4�!e®dÊif x¼a )`cqe1f³ÎH|VQRk›M4”œ¢Œ“ËŒ‰Lt Y­L41‡/ØkõÚ™híõû¦ëu X{=Ýbø°x¹<%UkŒŒJ¡fc‘’íÍT�œ'—+ˆœBjLÍÉþÓAé9à„ðÊ 3j“~¦)\L|,}…áÇB€0>Ü¡‡/…d„1‰ÉP‚” ä’’L*J)B„¯ÆAt{!Y§˜fâZ,#@B“rLa‰H GYd+ÃÓáÔ¤” ù‚Žíî�SÅj±ñæé©ÆÎ“�ûúE)ŠQ*Ч.ÈÒy±£e Å'ËË7'pÀ(D�úBT$Lj$)%‘¥#”†ýŒv5÷�ö…çßY0’êz,$"vøáÅ]¾C%(A J.9ɤ,�1BH«[jntdr’—jœín‘S*’cùúc=Ý¡@S?M�WÌÎ5s=‚:Þlõ;ûFe,:3A#.E�Q¢*ÊÒŠž Ãåçtšx§S&RJh@àåé‰:Î趃R Çq:“6Ž G0bd­-ñ ŒQ!ܘâq¼Åä庠óh�¿±�¦&pɉš�.Ÿ—È uØ·�B‘½R¼EÂ91Æ€ ?|ž¶gP‚” äR”Lz.AºéAª•‰öþÍU�¢,SV²9‘Pa ßá˜HCm§:÷‡âŠ�¥&Qèéî¯lôx) §›cbx.„QÊD*ŠJun‚AI€:úmÝÁcò�h÷Çgª2²â2üö�+ü>ÁW}ÚeÊÓ§d˜•gWH‘&g”11â”R†Ô"5&7+I7;GTè8í Tl¯ïÜŒ-JÒÍÊÑ0ŸÇßÙ RʢÑyz)wæáGNƒ4u�” %—šdÆ~ÑfUUU­Ó8tžbè¿Ã—½J9,2ì[” %(AÉ”’*¥4r“\dMÓà�C =ZcpÅJP‚” dÊJ&‘8ò0Ôa%Y ‘;ç¢î„” %(™š’É®8 /Ýa‘'(Eã‘Á[cÒÊØðtJP‚” djJ&e "‹Üy÷àÖ"“݃sß,²JP‚” dJJ&•8’Öv2ßÁ¤§*E&/Èà2'éIá-(A JP‚’©(™Œ%lXS‚ ÈåBUUÕˆÛ9ìA-AAK@AÐA´A-A7² +CvëG[ë·Ÿ²Û^‘”–°beáê ÅDMFì¬:úâÖÎ6?€"ñ‡f|ô«Š:ˆýÁcåeJniõ0ELÒÍëKÖ¤ÉIø+ÖU7×Al~™bïžÆ:ˆÍ_œ‘"}Û�¾QÑÛẩº ÕéÁA�‹Íž×6–dÝwV¬Àì'ŽlÚÂÝ~÷ŠGâx„îúæ�ýAšx¹F !ç¡@wý’„ð‹€èÒ³nN±eçþ_îqƒ!nY‘ºÿTû‘=‡,°tÓJ£",öì:!/JKÈë¶ÖXÞ|¿qöƒ32xÝâòÄ®}V(Kç§%)uÉÃÚ%6ïØÿ›}àu%sâãûVüUDäâûÁâõ³»Ÿ�~Œ¨gÏn[þ ˯Šã Yr~î�pƒèÝ÷ÉþwÚÝv_xí‚Jµr =U›¶Ùž0¢š½¢ìÞ9ZyÀ²é¹ãÚG<1K r“À°x(¨†» Z½ Î4p 7cz¼×Ð7@^­ÑAôÞçv `¯´³'å~‚ _𬛠®VânÝ{À8ÌÂ#ÿh#®fV¦Ff³)Ô즢»ïƒ�Zª»¼nAtx�>6Êž!çþNõе:Èd2´¿¡»¥ÛÿÂÚ9`ÔËbœâd– ML*×ÏMç÷6¸?û¢wÙ�‰ÌÛݾ¥?î†|E¢j=¾V'�¯æB.§ 4Z=@ÏðïøÎÉÍJ ÓMA�«f¹øñq·‚»mD?™&Oë=Ü*Ïý*–Hï¾lo½~b`ñ¼ÇÖ›ÌÔºé×M£îɘÀˆœV$¥`(˜ûÜ­qª/éè&4ÂEɪy<8ª=ú§Š—?=öÒk»¾÷§£;­•V–ê¼¼~è?9úÜkõV€äÒiÓä“h£Ü°(Gàyïo_úôØïß<ŽY#A.–,¹ë©}? ®Ö3óEQxíÒ%¦º�jvõ ˜Øsºñ­#naä,ˆ`”3sŒf9svÛúÅsŒ„ÚB£÷Pk€óö9ì"pñ9‰êÓ'?ï 1 ÁÖS‹0©œ`€!�›þãTÿØÖ°»Á²Ó@)YWç©eÀg.[ø{ã�uW€R7giÁ¿.�Î-O°¶Ù_›‡XóϺ¾=‡ìIzøÜ …³H‚\ –Üõ”ä £]5›‹æl¤u}{û; "SÇ%\sMúÈ+ê1·,Q?ÿÒæ�ÕÊÄT�x® xíŠ5™ÇßÙ±á™9Aë#< OÎ{ô†ºWÞßñY@]zÖCÙI“942f2¿ªªª¬¬ìâž_ÛÉW*:­LÆ„¾¶®ŠnA5sÞó·%Ðù ¹&-íg¿¸Gr…¯¾v*"/“ðµ×>ñ©òáûr’'šJm`—]§� �¶†ŽN?Pjòç̼kU"ú‚ _1[;:.â@誯~âã�O8•éºuùI`jõÒˆA�/‘Ñv\Àƒ ‚ % ‚ h ‚ Z‚ r.ƵâÈf³aO!‚ %455aO!‚\ö�ñ’#I(“ö# %(A JP2E%“�"UDc‰X¸Âh(ëE JP‚”L1Éä,¡ ù‚ŸÈÔÚYÓô)j„PÈÚ9p´_d¼jaIL,*«û­ÈR“5::,^P RqŽ&êõê„$SvZ\êÙþš™  1F€P6ù:r?3o P‚” %—•,È#”Adv+c Òß\j¦#�Á åô:=ðü˜ÌdŽEg(Q™"˼  PeXô\Pt¹E‘†:GiZgµÄ¸�|�(âât)à=ÚÁ –ꥀèã9CL’š3h€1�üJ&çÚFFB„EFFŒ™qc„JP‚” ä²’ç"‹€�$º|47Ùœ®"ŒŒ}Üáq‡)¨ 8JP¾ÕJÊÒeÚ’2yÏ!·’©£½¿FH(J1-3°Ð8ßß/Š”“Õº\[œ†õ�zë\b˜BôÓF2ä‘{˜Ì G"e3oŒ1” %(AÉå%spùm644´ø�ÓóÓΜö‰a�GQ‚” %‹JRç›ÿN¨”Òè"¹ÉiEdZ*›Me³ ™6ã %(A JP²h% MÛ uÆ;� Ñ•s“î„” %(Yœ’…Î8š˜;Ä¢;(MŽG¦–>D&‘ÉtJP‚” dqJd 2‹®€ˆnÀ=µÙR4Ù=•ûfÑÓP‚” %‹R² À£lbýòÄBÙU)š¼ SÓœ";iL” %(A J£d!–°mK! ‚ × ³–sX5‚ Z‚ ‚–€ ‚ % ‚ h ‚ Z‚ rÅ(ç©“ù¦SmoÖØ}hcc—fg=¼%+MØýl]$üø©Š2 €0óWA�OÉí/oœ|}øñáö?]Ÿ¶kõ ˆzIaη·Ør´ÂÞ�7ßk?n÷û)€Zg˶}÷«Ù™JüF 2ÔÙÔÇg§%öu7,ë äó¼öÉWw”ؽh¬À¼­ï>Â}ý¡ ?IT&¹:º��ŠÔr Ôæ¼š[NeÖŒ³æ7Žê•„íÊ e�¬Ò«„�ÝÏ7ëscœýã~~òL�ƒ¯¿qæä(Ói>ŸnÛöue:ØÒúʱ�A‘R�ióÝ«îÉT-ÄçÕܪL÷Þ‘Ñü†c¨Û5Tíª®=»/+ÿ©oä,�ñÞ†Ûnɬ®ô�áÖ�¹É_U률ÎI×i9¢± ŽáÀ¹¾Þ°¥„¾1�)néÒÄìÔ‚GT’¬Æ¯‚ܸñ¢©Ø„Î/ÌçC¼(ˆÎ~çTÈu¾žYžL5K ¯× Œ®ÌJâ˜Pw°Éž¿foEœ*Ø¿ko/À… ðƒŠ­Å,Ðk}ø¿ÕµqëüåÔ±p°},f�u¶¦™A\Añ÷·Æ€«mÇßí]+JòTœyyñÿ¬ÒÆp²ÿ|_»½°´˜W”l{`Ú™J¡î`“=wío*bUã}»~{ÂCößÕ(zäKy:r´<ý–½ø±‚¬t£ç'% …%¿²Ù›]Ÿôz:ºFúxöœýë™ô�+.'•gÄ3õ]g¦ýµy ‰¿crÇûCcöŽWì�TrÓÎ{R´øÍ@�Qó¡äEQœyDê¬äòË3”D‘“™~¸»Î›yg<aÿ©~ÝÆ»cÕ P(#mó¬…óöƒûVÂX/ ôV×gøÁDË?W‹S”£³ÙîÐÈØeä­·{�Á€$û‚Æ›ã̰ÿT¿nÃÝ5(•JtÔîêqñ{ïà€Ñ ‹÷ËóŸ64_K{ß=Í—®I)+7–•ã÷¾xºNA�/®ˆHµS:tR[b yÜðµm·Þ›ÄE=•—ÔT"¹¥{‹Åž~ow·ãŸ5ÃÃM½Ý_I)Á©JrCrºútäE~a~(Ä÷v÷\Øòòîªv~„�z¢˜ JãÍÁÛo1(“Q^°á笅ó�@/ÎÏê ŒÉÕ?r†+ræŽá�ȳ/$Ï믵º+ozj«ÉLw¿Ô5癌IŒ¨¸oI)Ĭ~þ�Ä«Õuž—%Pþ£ªºW�ëórâ3ã¡¡Á:@‘°¹ F�grêD@ÈýúÁæÎdã-7g¤ªŒ›×?8ê;ð§ö‚‹†ù†GÚl_Wž}/צ¥¬°ê”ã¢Fc<Î\B�’ÉlÁí/o …Ba! MB ôô´—ïùÞ²ÈDZ¶ÓÛ«úœ•ù™*}¡)XÛͯ-Ò„†½žHŽsÖÂOïëüåÉWwT®õÍî Ðß²Þô³·›>|°øÖd%aòPgϱ1Ëý³6§’GÒ,_f4«˜¿Ç3z‰»Ré �ÁÓ½ÂÚåêЈÏ+—´Ì¢«>û~ÿš{ÒT„нv·&;%åó%(co[g•ÎŽØÛí ÐdæfoÙ”[aæà‚ùU*Ó½·§w¿×çhë~û|ZQEF*pé•ëžÑ�Ý_ë<óIO#§ÕçZ­J¦/+4¾Ûî:î ªÔì¥[ïÈÉÀÜ2‚ÜðDüàB˜Ð\çI^]”í8l¶BÓ‰Áœ‡Rõ¶ØÎì?þøu¢%†�„/¸Ù çEÄf÷b^±jmûÛþ@e¦KLÞ´)cöþ­:þþõºþxø�NcI3\ʧ8ý†-YÍŽm{GiNÖ‡ˆB ²æn¿«íÏÿ:öž ƒ!ÃöDvÊBªšL<�snÊÊÊðŸA�/„EµTmSzú/žûVÄ>ÿ«SI’J��£e绚=ºÌ:ß (æjرŽ È¢fQ­X®êëûâ.ÎÆ:wr‡„Óš¾|_^Êg°!Z‚ È51¬~±à³½n{‡ ‚ % ‚ h ‚ Z‚ r)®(½ìñx°¦Aкºº°¦A®{.¿T A¹AÀ\‚ ‚–€ ‚ % ‚ h ‚ È¥¸üŒ£††¬&A�ëŒùï„êUg`õ!‚\7˜DÇGм ,r`(A JP‚’E*Yø(!z‰É1 ‹ŽØÄ'‡@aêº(A JP‚’E&Y˜%#"¾Ÿ(uú¢%±©:¤px°ßýɨ̀(ÓSb õ Çå @#ã gE�ŠM¹.¸ JP‚” ä³�,Ìd‘ÂåÔ¹6cª†¹}t1j=Œ2 @8EºÕ�‚Û5îgÓ$ÑTÇ”A±i&ji>�„Q˜qc(A JP‚’+�̵&í Gl"q=1óˆ‹S€ìöû|’4y ]媄ÍM¥iÂé†a—  Pe¦›r5z•FGüMŽ _Pè*ËAê–S’4²kð¨#¬Ž�_—¥‹Uƒ!{�§3@ÑJsMiZæ ª’ ± ü¿†á „Sefšr51аØïô6Š"2Y‹$‹¯f~6­%(A JnÉÂF tÒq"æv�C²A¹,ϺŒÉ>ïxûy_€ g»ÆJ–Æ@êìö{%É-Q \JVÒª.ì;3"kãr’ãoVȇυÄÈL&P¦kùÖ®`˜—eJ¥°ÐçDJTM–UW¸4<Ðäõƒb‰Íœ¦…ñ¿‹ÓN$½¥œ5;©8� yÇÚüTkX¶$I%ºjÝ�J·�é ÷©¡ÔD¨mÊ7%(A Jn‰bA–@�@$sÍw¶º¤tã’xm¢Va4Ç•kYÕ'>¯, Œð¡¥±�††Æœ¢P/1sb³ÝÝ%xhbY¼Ùl0’à �ØU¸Ù>jçIä œZ�–¦�›¼]­Z àUŠ„¶_/HÖÄ„x`”2J”™ft¦Øå¦ ER‚F$© Æ&’ë£ "W™þY‹°P‚” äz—,h” S„ “Yk…$tv uvƒRoÚTbŠÓj´@)�.a ”É” t" 2e² dâFeˆ’0J)(µ…Yú8…Ü×ãî)óòÌÉ¥rôPFéä‰RyÂõ¨³{ä\h"™ÎD1Li4O€L{ù3? ˆ&1P‚” äÆ�,È#”Adv+c Òß\j¦#�Á åô:=ðü˜Ìd„R^P¨2R ܸàò"ºFiZ’zeŽ™–t‰f3@Ø=6¦’jª¡—c@ˆL)ÂkÖ'r‘ø£’Ø€Ô8ÍòÌ8 ‰K˜˜Ee‰ï¡iÉœÕãv†‚D§KïѳR8:=72x",2˜b ÈŒÏBÈDž@ JP‚’D²à\BdÑ0�D—�æ&›ÓU€ñ�±�;<î0B€†Z¡¤,]¦-1“÷:͇˜t¾½ÿT8q…ÅXj Ò�k´¾3d�F•idÊßxnÌ”›º$^3àw†Õé* ”Rîj¶%.±˜ÒÆÇ 6–QIJeGGµ˜°"ÅP´ÌÀBã|Ÿ(RÊ&G'“î‘àÚÌÏÂ&ë4’‡G JP‚’@2—ÐfCCC‹ß8=O1ýçÌi¯‘™qôªH8�!ÇÄüÖ¤©Á;ôf£?Àà ¿1” %(¹æ$q¾ùï„J)�.’‹ÎišúuÚ D¶Ö˜šñtU%œjÉÒx‹šÈÞwÝY¿_¦Wÿ*(A JPr£H8š¶êŒw$,:¢+ç&ÝéªJdßèÛÇG¦-½øL®‚” %7„d¡3Ž"–ÃXt¥ÉñÈÔÒÂXdfìDº%(A JP²8% ²™EW@D7àžÚl)šìžÊ}³èi(A JP‚’E)YPàˆQ6±~yb!‹ìªM^�©iN‘�4&JP‚” %‹Q²Kض¥A�ë…†††YË9¬A-AAK@AÐA´A-A¹b”óÔÉ|Ó©¶7ëì> ´±±K³³Þ’•&ì~¶® ~üTE™æŠßM˜— A�€Û_Þ8ùúðã,Â;ìº>m×êx”ÀÄÆƒ'öí³‡c×”Ù6•¤¤“@k£s@Æÿ^A®>å•åå•ås•†;~úÌÁï¼606W�ó¹šá çëq¶øþ§ë/è5´´þúU?|áȶ=‡·ÿåãCçEzõ.ºÈF ¢÷h³@Šî,¢PM€IŽŽa•`ÂÂ]õMïÖ9Îú¨:>å«[K¶¤«°ð¹º–×jœ>ˆzIaη·Ør´dºÙÔÿ½ê¥9vUåK÷ÄküÏG^éƒôÍvWsËÃÞ�7ßk?n÷û)€Zg˶}÷«Ù™Jü*!Èuu6õñÙi‰}ÝÍË:ù<¯}òÕ%¶G/+0oëÇ»�p_hÃO„I®Žîã£"µ\µ9¯V“S™µã¬ù�#OV'.__”—VžgÕÀ¤%øßª %%êŒÜ¸Ï=ð�u®|^4›Âù…ù|ˆÑÙ¹Î×3Ë“©c©áõº�Ñ•YIê6Ùó×ì­ˆSûwí혣p~P±µ˜z­ÿ·º6nýƒ¿œ:¶�Ŭ³ÎÖ43ˆ+(þþÖpµíø»½kEIžŠ3//þŸUÚÂCöŸïk·–óŠ’mL;S)Ôl²ç®ýME¬j¼o×oÏ@xÈþ»åC�|)OGBŽ–§ß²?V�µ€Þðü¤$¡°äW6[c³ë“^OG×HÁž³=“¾sÅÄ8B«PÅÅ™"»tË‚3â™ú®3ÓþÎü[òiL ÔÕñ Íîe#nÈò›’ãÉ%åšøû7&w¼?4fïxÅÞI%7í¼'E‹_)¹.…0 ñA^Å™G¤ÎúA.¿y‘_˜ ñ½Ý=¶¼¼»ª�a§žh&ˆÒxsðö[ JÆdF”lø9ká|Ç$Ð �ó³ú(crõÁ�œáŠœ¹c8$òìKÉóúk­îÊ›žÚj2ÓÁÝ/uÍy&c#*nÆ[R q«Ÿ ñjõ€çe ”ÿ¨ªîÕãú¼œøÌ8Ehh°NP$l.ˆQ@`îp“qóZãG}þtÂ^�`Ñ0ßðh£Cûƒíë.x¨Ú’Qiì~ÛçÐ,Ë*Ô“ËÉ=û^®LKYaÕ)ÇEŒÆx~¡äf2[pûËC¡PXÃE“P==mÆå{¾·Ìªk;½½ªÏY™Ÿ©Òš‚µÝüÚ"MhØë‰ä8g-üô~°þÁ_ž|uGåZßì~ ý-ëM?{»éËoMV&uö³Ü_0ks*y$ÍòeF³Šù{<£—¸+•¾Ð<Ý+¬]®�ø¼2pIË,ºê³ï÷¯¹'ME¨Økwk²SR>ïQ‚2ö¶uVé숽ÍÑ Mfnö–M¹f.5¿ŠK¯\÷ŒîìþZç™Oz8­>§ÐjUH¾ÿÍ%ú·?P®º)ÁpY9Ó—ßmwwUjöÒ­wäd`nA®"~p!Lh®ó$¯.JŽöÿ 6Û ¡éÄ`ÎC©ú [lgöüˆ:Ñ3�æf+œW˜ÝˆyŪ´íoû?8 •™.1yÓ¦ŒÙ»©êøû×ë^øãáC:�%Íp)Ÿâô¶d58¶í¥9Y" €Êš»ý®¶?ÿëØ{€ † ÛÙ) ©j2ñ4ιihh(++ÃJA¾ÕRµMéé¿xî[Wøü¯N%IV(UBŽ–�ïj~ôè2ë|7 ˜«aǾ4‚ ‹šEµb¹ª¯ï‹»8ëhÜyÈVNkúò}y)ŸÁ†Dh ‚ ×ÄX°úÅ‚Ïö¸í‚ ‚–€ ‚ % ‚ h ‚ È¥¸¢ô²ÇãÁšBAK0›ÍXS‚ ×=8BAÐA´A-AAK@AÐA�+g¾ÛÞÉ|Ó©¶7ëì> ´±±K³³Þ’•vÕ·Ñv?[× ?~ª¢ ¦½Æ'¦!È�Á¢Ú{VúŸ®OÛµú%0±ñà‰=GûìáØ5e¶M%)é$ÐÚè�?ã›U*×gß¹>ÝŠOLC�‰òÊòòÊò¹ŽJÃ?}æàw^›«[ù\Mƒp…óõƒ8[|ÿÓõôšZZý‡ª¾pdÛžÃÛÿòñ¡óþÑšb“IDAT"½z]d£Ñ{´Y Ew–?Q¨&À$GǰJ1³S¯™µ�S±JßÙ2<(r‰Ù9ßûjNA ‰ž6Û¡•¨>ÙÙ y•™©J>W×òZ�³Ã'Q/)Ìùö[Ž–@Ø;ðæ{íÇí~?PëlÙ¶ï~5;7G�ëêlêã³Óûº›–uòy^ûä«;Jl�^4V`ÞÖ�wá¾þІŸ$*“\ÝÇGEj¹js^Í$§2kÆYóGž¬N\¾$¾(/­<Ϫ€Ë‚nsqqFB»£µ³ý¥wM/ÞŸ¬¿ü¡ÙþNÕî:âU§X7ækx§³¦¹eÕ¿ø€Å@ýÿÞW÷7(’R6Ú´¢ÇÝØ94,£% ȵÄôxÑÅ…S$Éÿa [}_NÚûÕUç„5%Zn¢9ø�ýM'Ý$FK<¢æ.\ˆTn]ézÖùu*vDÇOžðäÝuÛ͉ DiÍËù&�ƒ5ïœ:àxCŠÂÍk+Õ«€5Ôí>ê•„íÊ e�¬Ò«„�ÝÏ7ëscœýã~~òL�ƒ¯¿qæä(Ói>ŸnÛöue:ØÒúʱ�A‘R�ióÝ«îÉT-ÄçÕLªL÷Þ‘Ñü†c¨Û5Tíª®=»/+ÿ©oä,½¼8á}³´LÒM1OýŸö�sýçÃÉË/èâ±¢¿ªÖKA�“®ÓrDc1Ã�s}½aK!}c:SÜÒ¥‰Ù©�¨$Y�_1¹öâES± !œ_˜Ï‡xQ�ýΩ�ë|=³<™j0–^¯]™•Ä0¡î`“=ÍÞŠ8U°×Þ^€9 à[‹Y ×úð«kãf‚=gÿz&}犩O~™ b�¢Ÿâд ’à €x¦¾ëÌ´ÿžhâïߘÜñþИ½ã{$•Ü´óž-~ÃäÚDÂ|(ÄyQ/èvÖrùåJ¢ÈÉL?Ü]çͼ3žƒ°ÿT¿nãݱjP(”‘¶yÖÂyûÁ}+a¬—z«k�?‡yÎöOS”£³ÙîÐÈØeä­·{�Á€$û‚Æ›ã̰ÿT¿nÃÝ5(•JtÔîêqñ{ïà€Ñ ‹÷Ë zZ漤aï»§ùÒ5)e寲r`üàÞO׉ 2¢ ,ìh/UåüðÈ bMŠ ÃM—84-x¥¶Ä@ó¸ákÛn½7‰‹z3/©¨DrK÷‹=ýÞînÇ?k†‡›z»¿’R‚“”äšâtõéÈ‹üÂüPˆïíî¹°ååÝUíü;õD#0A”Æ›ƒ·ßbP2&3¢$3› Y ç;> �^œŸÕ@“«~ä WäÌÃ!@"Æ!y^­Õ]yÓS[Mf:¸û¥®9ÏdLbDÅÍxKJ!®`õó$^­.ï¼,�òUÕ½z\Ÿ—Ÿ§ ։ЄÍ1 %]j‚f�ÿÕÿ{Úžªô:‡Ú.ûß?ÜÒ Á–¦/À’5Y™ÊÉvÿ‡.^7¯5~pÔwàO'ì  ó �6:´?ؾ® <û^®LKYaÕ)ÇEŒÆxœ¤„ דقÛ_Þ …ÂB.š„èéi3.ßó½e‘Yˆcm§·Wõ9+ó3UúBS°¶›_[¤ {=‘fdÖÂOïëüåÉWwT®õÍî Ðß²Þô³·›>|°øÖd%aòPgϱ1Ëý³>6™JI³|™Ñ¬bþÏè%îJ¥/4O÷ k—«C#>¯ \Ò2‹®úìûýkîIS*öÚÝšì””Ï{” Œ½m�U:;bos´€B“™›½eSn…™ˆ½ó¾¼Þ7ퟌ ÕbKWX“GC3�Úçèk™R_|KÑw×”Wth–aBzåºgtg÷×:Ï|ÒÓÀiõ9…V«€éË �ï¶»Ž;ˆ*5{éÖ;r20·Œ ×,?¸&4×y’W%G;|›m…Ðtb0ç¡Tý†-¶3û�?~D�h‰á#¡n¶Âyq…Ùý€˜W¬ÚAÛþ¶ÿƒP™é“7mʘ½_ªŽ¿½î…?>¤ÓXÒ —ò)N¿aKVó�cÛÞQš“õ!¢P¨¬¹Ûïjû󿎽'È`Ȱ=‘�²�ª&Œ]¦bÊÊÊ®Â_U˜{¡™€kЙ�EµTmSzú/žûVÄ>ÿ«SI’J��£e绚=ºÌ:ß (æjرóŒ ȵAZ Tõõ}qgc�;¹Ã ÂiM_¾//å3Ø�ès% ‚ ‹ƒ¹vÜöAAK@AÐA´AäR\ÑŒ#�ǃ5… ‚–ÐÕÕ…5… rÝsùI¨‚ È æA´A-AAK@A.Åÿ”y]Õ"5­IEND®B`‚django-commons-django-constance-1032c50/docs/_static/screenshot2.png000066400000000000000000001766631527040271600254510ustar00rootroot00000000000000‰PNG  IHDRR¨“BŸE oFFs9�¹öp, pHYs  ÒÝ~ü vpAg�Ü@Ìw€IDATxÚìÝg`Uû7à™í%mÓ{ï¤H%¡$!ôÞÁ‚"Š ‚ à£ˆ"ˆ"MŠ€""½‡Þ„¤wÒ{ÝÝlßy?ìßH !$þ®Qf§œ6³3÷ž3‡ìßÑ¢uëxÃÑP]‚}]‚}]Äk ö±Ù2ÙÊ•_]^~ðà¸qr9—‹Šø÷kªµÃ‡'N”Ëy¼×Ÿ’—Ù~ÐJQboÆóVÐ<À/ZTWçíÝò …TJ§—•1™ ÎÙ³‰‰lvq±\ŽxEYXxy56FDÑh—.ÅÆr¹EEJ%Ò Ãhßêtztô™3|~NŽRI$I£Q”žž��Réééì,•Ž5x°T:jTh(‹µ{÷?ÿ:T\L’Õ|/2›ýÍ7?ü`jJ’j5I*¨h»WÓ~^îQ,,ºu‹Ç�sue232nßþ·ÑÚ“Nœ×¯_»ƒ}$'s¹wîÈd�røðå˺º66~~õõ«V .“͘1mZ}}EÅæÍ11Báãë+•J%I¢àżšöó¦´R:�Å¢(:].'É×ÛŸç5ÀëÅèÌ�‘daaB‚žÞÚµÕÕË–yyÉd3fôì©TÞºu郡 C4 vqIOfÌ8tˆÅ’H‚ èt]]™lÔ¨~ý$’�…ÂÑQ P« ‚ …B,f0RSÓÓy¼Ý»¯\ár³³¥Ò'ÓÑ´7·¬,ƒ¹sïÝS*ß~;<\, 21¡(‚P(èô„„»wuuýõÊ6»ªJ­n)Ol¶‘‘T:}zT”Héà Rq8j5�öèQV�·cÇ•+tú„ ~X_ïæ–•%LŸ¾?‹ÕØØ´:�ϗˇ 0@,:ÔÃC.·°`³ ‚¢d2:]“—={._æñRSÅâö—zg”A‹%H¥S§FE‰Dƒ;9©T<žZM£ääp¹;w^¾ÌxFki¹Ì§O8°±1(ÈÌL­¦Óe2:ýîÝ»wuu7mЉa³kk9¹|äÈÈH‘hÔ(…ÂÐ�N'ɲ²Ü\oëÖ'ttîÝ{2LüìöÓyµÿ¬£�$‹¥T†………‰DcÇzyÉåvv|¾¶ÿª\ÞØH§çååçs¹»wŸ>Íç'%iZDKƒâåòÿýoÑ¢òòæ5±|ùêÕ�6¤þdŽæÍ»{W©œ4©ÿÆÆà`KKµšÅ¢(kùò5k´[u¬Ut4�-ÕKÇÏ…Î<¯º*ºƒCïÞƒ·¾ƒ¡R Ø»·TjfFtúµk±±\nq±JÕÚ$Y[ÛØ¨REEùù)•††<�sÿ>‹ÕР ‡4íÓȨªŠË=z4-�N× äp¬­Åâùó{õR« >äp®\IJârcc³³™ÌâbµZ¥ öó“H† sq!ˆ¸¸¤$«¶¶ù#}Óþ--9¥2$ÄÓS©,-MIáóOŸNMe±ª«ù|¹¼OŸnݤR…‚ùx±°�Fk¾MWW*]¸ðý÷ëëÃÃMLh´û÷ïÜáóOžLIa2Åbcc‰äí·{õR(ôôØlµšÁ¨®ær�MM¥Ó5I’Ç“É>þøý÷ëêÆ�·³£¨¢¢äd>ÿرØì¬,™L›—!C<=ÕêÌÌäd6»´´}Ã;^b4šŽŽL6oÞûï××GE™™Ñh‰‰wîðù'Nhs*•¾õV¯^ …¾>›­VÓéOæôñ2g³•Ê^½ºuS*++ÓÒx¼3gRRX¬ª*>_. õö–J}|h4:ÝÎnР††Þ½¥R6ûôéøx'1±®Ž¢|}½¼¤Òðp[[-&&1‘É ›Vûé¬Úoý($©V÷í;n\uõçŸûú*•Ban.�wìØ½{\î�YY,VNN}=Iêë›™)•RéÇNf¦DB’AQ$Y]]SC§ …††r¹»;Ÿ¯¯«{æLF›}çNz:›�ž^VF§76>y¾XZr¹ E¿~~~ …Tš“ÃãEG?xÀá$$32™uujuG[EGÓùt½tƹÐY5 ÐuÑh..C‡nßÞ½»X|òdJŠL¦¹ÓÑqw÷ð8~|áB>?-­¬,>^s—M’,Vxø‡öè1>IVVž=[R¢R=þ"¦gãpŒŒ~þùÿsv‰nß®¨(,üï¼åY“÷+¿ø"<\_?'çÆ�ŠŠÂB£ååÿî’¡ÓÍÍ==ƒƒ-š2eРY³ÂÂüüÆŽí×/0pÌ??ss«¢¢¨(5U*µµýùçÿýoÀ‘èöíôôÿR�CûÏGÇö¶CC/¯Í›ƒƒ%’³g32äò–"P/²ç7 ƒ! ܯŸ££\^TTYY_ßÖ§[ÆËHŽ\^WÇd–•„D¢««§§PÑéQXØzˆPK*ÍÏ×Ó›5kýz’”ËŸþйw�Ç;}:/¯®î·ß†olœ:ÕɉÇûþûŒ ­¥/$>Ÿ ΜٶÍÐpß¾òríC’LNf2éô�?®­ �J­­ïÜÑÑyôH›BŠruíÓG( áph´+Wöí34üå—ì즣ÄÇs¹II£FUWýµ—A<9|’¢œ�ûô ÃÃõôH2!áÔ)CÃo¿}ð€N×#>žË½r¥´´®nóæ¨(‰äã�{÷ærgϾp�Élû`ÌŽ–E9;÷îÝÐÊãÑh11®]›™ùxNSSÇŒ©ªZ¼ØÓóéœ>IG‡¢.]Ú±ÃÈhÏž’í5eÎfòImí€}ú44ðù·o~úé… LfS_H.73“N¯ªúᇀ€ÆÆ°0##.wïÞÊÊú¢µÿ,,–B1`€“EDq±žÞW_:ÄåÖÖ¶åvH­¦Ó’“¹\ÍÓS$9ÒÔ´õ¡ñ-áñ(êöí½{�Œ6lxø°å6ßñVÑñt¾ìsáeÔ,À›O­..ÎÈ IrìX�ÀÊŠÁ¨¯ÏÉQ*i4[[ww¯ºº°�¢¼½X,:½¤D*U©Øl>¿O¹üÎ�‚¹?”¶‡B!þõ×Þ½YYg΄L–™©ù¹šÉ|}ib0èt’T*Û²%IOÏAƒÖ®2D¥Ú±ã�?öìYº´ªJ©¤(MGKKgç�‹NG½Có¶C£‘$E©Õíù‰àYûc2½½ýüX¬ÔÔ«WÛÿüÙ•0ADDX˜‡‡PxûvZZAA[š/%اV+Mïc0(ŠÍnk¨†¢TªÇß;¦™„Ëe³)ŠÉ$I‚�ÉŠŠØìü|‚�Ë�œìíU*&3#ƒFk)(PRÂã]¼XQñøñ)J&c0îÜ)+#ˆðp;;¥ÒؘÁ mP€ÁP©BB\\Ôj‚¨¯çó�ËË{2°BQ4ZBÂíÛNU•—Wc£žÞãU¢R…„¸¹©T!‘p¹û÷§¦6…6šòVZšœÌçÇÆöï/—‡†º»ËdVVW®0™yym­ÂŽ•˜6�j5A44ðxG�>B¢(íÞ½[·¸ÜêjOÏÆF]Ýg§¨´”Ç;{¶´ôñ‰Y4e~÷ne%A `kË`œ>Ï`4ò¬I}^^Nƒ¡RÈå®®úúA••/Ò_¬öŸ×ºi´ÚZÍÇÐP& 27g³/\(-¥Ñ^~H©ªŠÇ;|øéÖø²Ï£Ž]ž:ÿ\x5 ÐH$eeÕÕyy<žµµ——®.Iæä44°Ùþþ¶¶JåéÓ11"Ñ€::::7oJ¥R©‰‰¡¡««BÑаM�ö.“$9�áÇwt=ÚÞžÉd2i4�¢jkÿþûðáÜÜsç**ZÀ§Ñtt ¦M;ÖÑqÈ++M¥R©B$ÊÊÊΞ?ÿرúz…ÂÈÈÌì³Ï¦Nµµ äñH’¢kkwíúûï‡ÏžÕ¼”EÓ{nñb[Ûìì¸8©ÔÇÇÝ�Ï75åñTª;wN�ÊξtÉÐÐÆæ�w||ø|KK]]µúÖ­ãÇSSþ91Q&ÓÜ vœ“sð`A�RIQš2Y¶ÌÃÃÏ� $’S§ââŠ>}zöÔÓ“H®^}ðà�wΜ‹ÛvoO§ëëGF†‡[Y%';¶gÏåËUUÍïÁÕj¡°¤$+ëØ±’‚ ÇØXS¢>>6uª››‰‰¹¹ŽŽBqûö‰Ç�ÿüsB‚P¨T�nmùÕW½{ëè°XL&‡£VWWŸ:uèÐÙ³»wçæJ¥¥é¥µxq¯^ÙÙqqõõÞÞ­ï“ Øl33_ß%KfÌîÖ�$E¢¼¼œœ’##??CÃ;V®<|86V*Õ¤ÒÔtøðáÃCCG�¶·çñšê½ àï¿މ9w®¢BÖl™&mK–ôéSR’’ÒØèééà *UqñÎ�»wŸ?òdE…&emÍ/I2™ŽŽÁÁ~ñEX˜••žžZ­RQ�F£‰Å™™çÏÏ�{äHQQccÛÖ,.ÖÑ š0aýz{ûØØwÞÙ¿¿¨H­¶µíßÿï¿çÌqv¾~}Ó¦]»~ýµ €$ýü&Nœ>ý½÷=:xðÓOïÜ  ccOÏÏ>›:µW¯À@� (ª±± `×®¿ÿ¾|ùìÙª*•ª©z÷.,LH‰<=uuÅâ„„Ó§-ºx±ªŠ$MM½½¿ürÆŒ^½||èôÆÆüüœœ’�€FkÛE’\®žÞ A††"ÑÑ£%%�ñ$Çbµ%wíkW;–¦$¿üÒßÿðáU«šöÙ|ù�?ž>M£F…†úùq¹zzóæ½ýöGI¥%%¿þºuëùóqqÍ_šõ¤—ì£Ñ˜LŠb±4ÿR*IR&kû“NW«‚ƒ…ÂI“e2}ýÖ·–Jù|Šj­áÈå"�¦fød#’Éd2’$:�¢ÿu‚NW©lltt‚ Š‹ŒÊÊ–Co E}=“© D=ì£ÓU*;;MX¬¢‚É,+kmp®BA§gdÔÕDh¨‰‰RihH§DÛƒ}+1M:59-)a2µ—¨§SYWÇdVTA<;اPˆDtºXÜr™K¥Ú¯­´´±±¥ °&X¬½ˆ3Úž�ím‡/ZûϦP0ûö�?ÏáøøŒ%•ΙóÞ{••}$‘0LfbbN‡sùrJ ‹UZÚ™¡3¥²®ŽÁx|Ðú«8�:¦óÏ…—S³]EI$"Qllm-‡Ó»·µ5ƒqòd~¾ž^` �ÞаeKnn]�‹ËÀ�&&LfuuU•““¾>—[RòèQA�öuDLf¯^£G»¸øødd¤§ðÁ¡CR©\NQ$i`àêêè¸~ýäÉVVéé7æç?� �ÅêßÂ7·~ýŠ‹ÓÓß{o÷ˆ¢H’Çãói4™Œ$Ùì!CÆ�··77¿s'1qʔ۷e2SÓà`/¯�Ç�75}ð`ëÖ²2í[£Y,.×ѱª*#cîÜŸ–Hèt;;'§]»fÏîÖM_ÿðáÄÄÏ>û婔ɴ·wrúý÷I“¬­]\ÒÒ>ÌÈP*Û—£Îþ©˜Ç ¤Ó=:pàÞ=&óý÷»w71ùì³qãêêîÞݺµ¼¼¢B­&¥R,‰._¾zU( U*’ÔÑÑ××Ó6¬_?cãÙ³'M’HîÞýùçüü¼¼Ç÷Ï冇›™ÕÕíÜyäHE…PXY©P´'L¦��ŸŸ¾¾PxñbÛŸ`X,{ûŠŠ“'?ý4:º¡�Ų· Û¾}Ò¤À@—ÔÔ«WÓÓ •ª²29ùÒ¥O>‰‹“É4!5ÏήOŸM›ÆŽõ÷¿vmýú[·´9jË>U*]݈ˆ±c{öÔщ‰ùçŸI“ââÄb“  Q£þü³G�¦’$—Û«×èуùøddDGðÁ¡CõõMõÞ¿ÿúõ“'¤§oÜ÷¼zg2õô,-óó�Ÿ5ëÀ‘ÈÔ48xôè ÆŽuw¿sgëÖ””Š µº­ùe0,-gÎŒˆ04<~ãÆ]»®®Ö<çÑh<ŸO§kCJlv[Ö¤(¹üÑ£G�Äâ¾}¬­ŒÒR¹ÜÕÕÂB")(�Ë==]]õôŒ’#8ØÖV.�‹»zµ±Q­¦Ñtu##Ç�ïÝÛÜüÎ�¦L¹}[›»�Ç�÷ô|ð`ëÖädí™ÈdêëÛÚ––ž<9gΑ#uuMOøººÃ†�l`pãÆþý'6íç�?ºwo[LˆÏ·µ51±±)--/ÏÌìx¯¾öä®­íª3ŽÕ¶kylìÑ£11 nnþþ‡¯[÷¼Pcs/%ØÇb(ææš540™M‘ÒçeÇË+"¢¦fÙ²îÝUªÒÒìlo÷îØX&³´T$"I©T©$µšÍV(fÎ;V,65%Ég‚Þ„¹A_¼»ëË(±Žæ…¢Ú¶÷¶ýfÖ±”¾Œ|’dEEr²¾þääðù¾¾®®2™··��BÑ­›½½T:i’››D2iÒ€lö¦M;wêëŸ=[SÓ)Q«Õê¶ìçß×*:ÿ\Àœ¿-S«åò¤¤ìl¥ròdOOÏÐP¡ŒŒÊÊÊÊŠŠÄâÆÆ;wD"{û=LLX¬G�ÜÝY¬ÂÂ{÷D"íi,Ÿ?x°‡ŸïãcgçåµkWDDó#Ðh::tzCƒ‰ “I-ûX,>Àkk©ôðáC‡4a>Í'ÕØ(kú¶èéK$gΤ¦jîP ¢´45µ¼¼¸8"ÂØØÎŽÅj öÉå"Qttr²D¢}MMEE]]Y™LÆç_¸�š*•jB• EEE]]E‡ca¡ùñ˜ h´ö娳ƒ}Báߟ=[Ttþ êÛ×ÊÊÔ4)‰Å  ÑJJÖ­«©Q©‚Å24 66®¯o*ym#"\\ììX¬¦•\^Q�œÜæÓ–¶¡a¯^-ï'++*ª©^ZG§»¹ùùñùùù…OŽ|íÉ][ÛUgëå{ Á>µÚÓ30P&ÓöäÉÌd³+*Úú6´¨(oo•Š JJôô–,Ù¿ŸË­ªzòrÅ`+•/³ÏŽJE§ŠDѽ»¾¾RibÂ`„¶Ëp&S__¡01iyùùB!Aôè¡ }2™\nUÕÓ¿’0*•»»��æK€Á¨©i{Ÿ¾Ž—˜&�Úœ*¦¦ ‡ÓRN  SÓÁÝË¿†J%‘0÷ï'&jþj–R”±±»{CúuãÇK¥3fôî­T^½zò$“ùxþå} wîyÔñt¾šs´ÔêââÌL¡�$ÇŒ12 R©x¼ŠŠôt±X"Q«•Êôô‡)jèP77�ÀÎÎÔT.¿s'?_¡hÂK’4šR)íØ±eKbâÉ“uuϺ+äpZZJQm½“lÛšEQ Eó1.jµ¦AËË5ùx±½\-ýlÍbéê¾óÎðáš0ŸBqíZtôÇgÏVWs8ãÇOžloïïÏd’dËcq”ʲ²–ǽ´�BQW—�P_¯«Û£‡¹9‹uéÒÇÏïßGQjõã%OQšºl*y&ÓÂbæÌñã==kk:p`êÔììÆFµZGÇÝ}Æ… »u{›ÝÚÉK’55¥¥Lfuµ¦v8œ–z:Êåš¡¦AQ::�ÿúÞÎ:�:#�¯î\ ©Tóæ>==3³É“=<Ôê»w 5ÏUV–“#õîmcãç§PÔ×ß¿ß<"“‰DgÏæå±XãLJ… šéµèt&ÓÎÎÚšÃár[{(•ËÅâË—‹ŠØì1czõÒÓÓÑi »q¹|>�N£Éå��·nUUq8‘‘žžl6ƒA$inîéiffiYYYS“ŸßY¯ÄioŽØlƒÅ‹?þØÕ5 àéví§«;uêàÁÖÖ'lcãáA¡VUW7ÏcÓÈ'’d±Øl&ÓÎÎÍÍØØËëeO÷¡RÕן?ñbI‰�ϨQ}û††1MµK’::..#Gv릫ËhG—!�ÁÐÕe2•ÊÊÊúz™L¥"IÃÞ>0Ð××Ý]ûê¯ö‘ËkkïÜ©ªÒ×�ˆððФ‡F33óððôtqi¾O™¬²òìÙ¼<]ÝñãÃÂìퟬw.×ÎÎÚZ_¿õ–Üœ¦Ÿ §§æˆš™‹==-,ªªòò=ÒÔcÛóË`ðxvvƒQ__VVXxõêíÛqq›6;–�P]maacca¡)鶯I ENNjj]�£ã€=zØÙ•—çå66ÖÖÞ¾]Vfh8mÚ€FFEE©©••Ú~sryMÍ­[UUúú‘‘OæÎÒ²²2/¯mgbÓ~¢¢ºuÓÓk¾W×ç×5‡caabâáQWWY™œÜ½úÚ—»¶·«ŽK¥‹óó¥R33Kó* ‹nݺu{²¬T*‰D­Ö´«ö„ÛÙ³O¥ò÷÷ö–Htu•JM’¢ôô ”Jgg©4(ÈÜœ¢B"a±þüóŸôõ¯_ Ûºw…‚Á¸p!3“F îÖM(üᇠTªÓ§sr ‰„ÉT©<<<=%’��N B­~Y—=’Ìʺ~]W÷Î/¯úú&O®©áóoßærï߯­% gg‰$2ÒÌŒFÓ¤Dó%ÐôI’997nèê^¸àáQW>|xMÍŠVV<^llYIòùRéÈ‘þþ “Y_ÏånÜÛžyx;§ÄH2;ûÆ ]Ý7¼¼êëCC'N¬©áñîÜárïÝ«©!KKmNE"‚P«_ì’ÜU°Xrù¢EŸ}V_¯¯_PÀáÜ¿_XÈ`h†ÇÒé|¾RèïߨèèH4ÚÅ‹ññ Æ“£êI²¬,?ŸN§(…bâÄðp±˜ÉÌÍ¥Óe2¥’FKJJOg±´obh¯Î;�:+�¯â\€&%‘…±±UUtú§Ÿòù ‰‰ Úg¹\JãpÌÍýü23kj´oëÓn­PÄÆ<˜–fh8l˜£ã¯¿~ñ…“IªTA§ÓhjuYYrrnîÒ¥ÅÅRiK¯ˆW«åò«WÈʲ¶?ÞÙy׮ŋi4¹\¥"ˆ††ŒŒììÏ??q¢¾þüùƒó󽼦NõñÙ»wà@ÍuuÛ·ÿýwEEyygÍ Ü¾Éå<�gmÍf+"ѳ~nþ2m´§56Þ¿¯VMžÜ³'—«TVWWUýúë¡C••š·õ„\Þаk׉ÎΣG›šdgg`p÷nYÙÝ»��::!!<^ûrÍfëëþù»ïšš^¼¸sgvvBBëo@£¨ÚÚÔÔ , É  �?ž>}úôÙ³ÙlŠR«5Sý …%%'OFG¶çy™¬¤äÏ?/_®¯_¾üÃß}wøp±¸±±  +«¨(?_.o©ãÌó¨Õ çÏ9r÷n�3fL™²o߀"Q~þÇå奥R)Ÿ¯íuFQ��±±?nh8lXXد¿~ñEÿþ$©RQTS½_½ºtiqq}ý³&; ‚P(JJììÂ÷mëßßÐP P«‹‹wîüóÏŒ m=¶5¿$Éd6iÒ Aýúéë7ÍžLQbq||tôÅ‹ÉÉ2™&<Þ¶559®¯/(ÈÍ-/çr--kkSS«ª$‚�ËsrÒÒêêtuýýy¼›7 ¤Rm[U«…ÂóçŒ�õòš:uüø½{Ô”]aáöíÿ�–Ö¶3Q­ /\8|øöíÀÀ·Þš4iß¾°0‘(??'§´´¸X¡žµ5�foïë«§WS“�PPО˜޵õÒ¥_}õá‡ÚþA*UMMrò¢E›6ݸÑÖܵµ]=«Úv,µº²rß¾3gŠ‹W®œ1ã­·þþ[$ ss³²JJŠ‹åò¦Ñ£2YEÅÑ£ 4Ú—_Î�ûÎ;Ó§‹DEEm™ ƒìßÑ¢uëžu��ÉV®\´¨®ÎÛ»å5”J™ŒN/..+c2228œ3gØìââÖÕ›öéâ’ž.̘qè‹¥I&I²XJå€ …“'ûúÊåffš@„BA§§§§¦òx;vܸA§¿õÖÇ·´‡æûwsËʦOß¿ŸÅz22LQ~~ãÆUV~ÿ½‡“¹|ùêÕÁ�;O^þ8cc©tÆŒ¨(‘("ÂÞ^¥b³)ŠF+(ÈÎærwî¼z•Á˜0aÖ¬úz;»”�àí·�e±šwèt>_.6là@‘høp…ÂÌL.“ËéôÔÔ´4oϞ˗y¼”í[2Ú£3JŒ 4ãË¥ÒiÓ¢¢D¢¨(GG•ŠÃiÊé®]W®0cÆ|ðA}}K¥ÚÖ2ïÞ}ò䊊¥K��Y¬ÖÊœËut¬«û믩Se²ììC‡LL/NOoêÞý¬öÓyµßÚQèt•*,,,L$Šˆpq‘Ë]\Œ�ÕjGÛÿ�$ËÊŠŠØìsç®_×Ñ9vìáC:½¥‹“©T:] hl\·îã�…B�Û· ?øàÂ…ë�=Í»Ï4Ο(:R­îÙsäÈê꯿öö&ˆèè�;��·l).Æ<¡ð:0^Õ�h4•ÊÏÏÛ[& ðò‹íí ‚F»t);»=Ów¼ZtºR9fÌÛo×ÕùúVUq8÷îÓé��tºJåììæ&‘DDØÙ©ÕIv¶žÞáÃ¥¥óÀëóÊ‚}L¦R9aÂðáB¡··RI§ß»£§·sg~þ¿7@¦VÓhYYyy FHˆ�OcãÛoûø4½ÔV(d2oݺqCWw×®ë×9œÊJtÖ€×éµ¼³: EÐ5 ØÐE ØÐE ØÐE ØÐE ØÐE ØÐE ØÐE�EQ…‚xÓ1Po: ãè"t:�N§£ Þt �F£¡Àa>€.≞}2YEÅêÕ›7„‡ôQ÷îþþlvÓÊÏþ´«S««ª²³ss÷í»q£´´ @*%’¤(µš$ù|� 4tà@�Áƒ­­™L’|]©T*ÅâØØ»wëë{ô°´´°`0þË \¥‰ “’îÞMHÈË«©‘Ë ‚ (J¥"I.×ÌÌÕµG�  nÝLMÙìή5Š’JKJâã/_¾y3/O"¡ÑX,cc?¿°0Wײ²›7óò 17g2q)€ÎðŸµE54dg§¦®Yså �6f̸q!!sæÒé$I%————ß¾]_¯TªÕÁd¾¾· ªTbqlìíÛ%%<^@À9ØGQRiaáÍ›çÎÝ»G§ 8bDÿþzzšòP«e²ššüü¼<‘H¡ (‚èü`ŸRYU•šš“C’£F�ëèÈáh?stŒŠ²°À¹�«A EMMIÉîÝÇ�ç䤧K$E\®‘ÑèÑ£Fyzöî-ÐhM½>´³óõår³²> kj$ÃÇgàÀnÝ‚‚êêrs�ÍÈ «ªÄb:ÝÇ'<< à­·<<8MïCŠ’É®^½t)#ãòåÂB¹\¡P«Õj’ÔÕ566,*ÊÍ-$ÄÈH†k™ZÝØX]-1™FFzzÏïS¥VK$7oÞ¸Q[ëêdhØ<˜G’|¾™™¹ùÀ�ff+“ììÜÜ'óîéÉáÐé¡RÝ¿Ÿ–¶cÇíÛ55b1IÒh$IQÅå::zy}ñEd¤ŽNFƹs™™2™TZ\¼sçõëû÷³ÙÁÔ©S¦xzšš¦§§¤lÛ/66*J¥LF£é脆FEùøŒakËd’d{R¥)ÍššS§ÎœÉȸ~½¬L¥¢Ói4‚àñ»u[¸02ÒÐ�Ãyµ&æä$$”—›™�éà  óiÐhl¶¡¡ƒƒ»»¡¡v™JÕÐ�“sëVL̃¥¥2A›miéïì쬧G§„\^XxîÜ¡CººêêVT×ÔˆD ƒam4hP¯^66��ññçÎÅÆ–”ÈdRiuutô‘# šüQ”\.ŠÅÁ€“&EFÚØ°X¡TÖÕedܸqåJBBEEñx�……©icc^^}}@À¸qÚ5ZóÄl¼t:�F’ryMÍŸîÜyãÆþýÍÃ/¥TVVÊå::‘‘šíHR"¹víôéììêjÿàà5k9œššøø»w—/�Ž.-íÖmút[[##ÍžŠÆÆâb##ÿ¯¾ çóUª¢¢´´%Kvï~ð@$:48xÉ’Y³x<¥²°05õÿ;~--;ÛÙÙßÙ²áÃy<&“ (J(ÌÍMK[±âĉ‚gçwÞqv¶´l½w�LVP{íZ~¾Ÿß€��ÖÖ\në“”(•Rif¦PÈåöêeaÁf?oöbµºýeRZjjøÕWáá\®RYX˜–öõך¼{xxz:9©T55G�^¿.÷éóÎ;ýûGDÚÀ‘TÚØ¨V³X �Þ½û�!žžîî¹¹�ED¼ÿ~PP@@ÓPk¹¼[7/¯ÿý¯{w&Sø’HŠŠ22¾ÿþôé‚‚ž=gÎtuµ¶n{ªèt™,..::9ùþ}ssÿåËÇ�x<’Ô¤J¥b2i4•*)éÕךZ-——76êè89s8ϯ5‘(;ûæÍû÷Åbw÷aÃ&LpwçóÅ⌌³gOž¼y37×Ê**ÊÝ]GGS>*•PX_/ 4lXP�®®JU^~ëÖ±c11©©uu½zGEõéS[{à@jj·n£FEEÙÚjBuryAÁ™3¤¦ÒhÚóH$Êξu+)I.÷ö1bâD77>_,ÎÌ}ìì¸ÜÎÈù"eÒ·¯««fM‚`0LLŒŒ e²üü†MÞ™L�À@¹üðácÇ++��''[[cc__[[>ÿù3*S”TZ^^Pð×_W®åæŠÅE£„JUY)ééUU)•amÝöT©T wî”–r¹C‡ðxÚò#IGS—ryUÕ›Pk*U]]QQ}½®®««��Ž�F$©£cgçꪧwÿ~^^]�Jåó'ÃÈÈÕÕÎNWWSò4š��¥%Ÿ/—§¥I$šòiïÑtu]\lm›Žnkëâbl—��K´ƒ$I²)X¢ý²™¦•Ÿþ´ù2íñ¥MÿÕþ¿f-&“NoÚ³¦ŸTkË›Ž«ùµšÁÐ×7î­·BBú÷×Ó{±0EÉåõõee••J¥¾¾½½�‹¥IEKk³X|¾‡‡®®Tš’RY©R…„ØÛ?ë=xM©}Ñ2ižÍR:�Çë×o„°0_ߊŠÚÚÌÌÂÂÚÚŒŒ›7ïÝÛ»76ÖÁáÿ›<ÙÎÎ̬µT(êê>y²¨H_øð��5kX,M,..þþûÍ›‹Š(ª½©z<¯O·­×Qk †¾¾¥%Ÿ/×Ö*•ÎΖ–ÏüûdÞš—Åã˵ÿ¦Ñ´m·õòy²UhËêéå$IQÚ7íéÉs³µÜh´ìk=xóô§L¦®®ŸŸ¡accllv¶LÖ«—¿?›]]�•UTTQaddaaeÅb‘$I*-ï¹µ#>½œÅÒ×ï×ÏÖV*Ý·ïæÍª*ÿˆSS�@ûî8¹¼¸¸¬L©42²´äñš¦Bx’\^R÷à�\Þ­[ddÿþ¶¶<Þ³úÅÑé|~Ÿ>¡¡Á²egÏfdܽ;i’§g��v‚ŽÆÆòò²²[·êê ÂÂ\\:¿LÔj™¬¤¤¡�F333336 2336îÞÝÑ‘ÁÈÎÞµ«ºº²R¥²·77'I:�Ë¥ÑTªÆF¥RÓÇN(mlT*ét==&“Á Iµº°099??/O.׆õà>¿F‚ƒ-,$’èèøøúú=ÂÂÆËbq¹¯«Öôô\\ÌÌNœ¸u+.îÑ£ˆˆàà¦7÷Q”\^SóèQNŽPhbâînc#ØÚêë …ÙÙ……b±““f mAAv¶P¨¯ïè(0$I’JåÓaAí¿_ÞÖ{Þš †��­­žžP˜“ST$;9¹ºòxbqaavvuµ¦ì€ç{*Ø×ôQKÁ¾'?¥Óy¼¾}GŒpwÏÎ>|øæÍùó¯_'’är��'N;ÖÖÖÄDS*[ÞskG|z9�ÆáŽÙ³g}ý¹s))+Vlܘ’¢ŠªV«Õ4š‰‰§§—×§ŸZZòù­‡EØl ‹��ÈHkë¶ÎšK’îî~~‹STVÖž=^»¶oŸLFš)2HRGÇÐpÀ€ˆ:�Ng2;·LB©‹¯\9v,1ñî݆Šb0èt’¤(�Ãñò ÷ñqwg³5ýà Âý¼(jëÖíÛ¯\9~œÏ72š1ã­·¼½G�îÛ—Ç[·î�?._¾|™Çc³­¬œœŒŒ,-Y¬††ö÷G£Ñ8œààQ£üýËËOœHJZ¼ø×_ÕjÍûù|gg_ß/¿ŒŠ22z]µÆãÙÛ‡†B÷ïߺuéÒáÃwîÈåšZS«I’Ç37÷ð¶²¢Ñèt6ÛÕ54´gÏòòK—N�Ú¿ÿÁÍq­¬zö8ÐÍMWWSk*Uçûèt==W×¾}ýüŠŠ.\8zôŸ<àóKK]]«±±y0 5¤Z­V«Õ(€×M¥R(T*íðiµZ,Îͽtéøñ„3³¡CG�òñÑ×þ»࿌�ÞBÿJeMÍýû§N]¹òè‘TªR)r9�fbâã3`€‡‡��¦_!À³�EQš©àM†A�]‚}]‚}]#>>>>>ð¦Ã]†ñtötötötötötötötötötötöt @W¢V …YYçÎíÝ»ÿÅ‹ EE"‘Z­ýÔÈhð௿^¶lðàêêõëçÍÛ¼9+Kû™«ëG­[·`�¯/—‹rx3ub°�¢¤Òòò””7._¾~=>>--'§°°²R,V((Š ‚ÉÔÕ53³±qròððó êÛ·o_kk�$ÿ+®P”•ݼyöìÍ›ee …¦TÌÍCB 17g2Ñ$¡#ç P˜�°{÷Æ�ÇŽ%%Éd(€ÿšNöQ”Xœ“söìöí›7;–ž.·¶¦B!–—çæ&$hþ–”˜›ÿ×z)•åå·n�:uî\óUNNƒ!Ø%“ݽ›™Ù<̧«0iÒ[o……YYq8Ž‘‘‰ ƒÁf “&-\¸`�X¬é÷G£ñùVVl6JàMÖÁ`ŸBQRrîÜúõk×;öèQÓ`A:ÝÜÜßРÐЀ''33]]:].¯¯¯¨((ÈÌ|ðàöíÛ·>üïôçx(J.‰ïÑgaäííæÆã5_Îf[Y9;»º¢Ôº’¢(J3ȶ½Ôêšš7~þù«¯þú+;[èãp||fÌøê«>èÝÛÔ”Áhm[±¸¤¤°P©43³³Ó×§Óÿ,??.îüùsç®]»?##?¿¢B,V* ‚ èt>ßÄÄÎÎÍ-  _¿Áƒ##ƒ‚ììø|ÚSS�H$‰‰k×>ùV²Ù³W¬;V(<þèÑ ââ23+*$Šb0ôõíì##'Mš8qÀGG>¿)IQryEEB¹s'N\¸pçNZZ~~e¥6MA£q8¦¦––ÖÖvvÎÎÝ»:bD¯^ffLfË)y¶Çßž¦V×Õ%'GG>|éRrr~~IIEEUU}½D¢Ri· Óy¼[7=½¦TQ”BQU•šzåÊ™3/Þº•””“SRÒР⬧geåê6tèС}ú89éêÒ:0­LÇRûolŸíi]MíŠ Z:zkïìS*kj’’¢£ÿùçðá+W’’ÊÊ$MWׯ& <|üø#LLN�úæ›­[ñ@€×ë…{ö©ÕµµII×®5…ù‚$�œÆ�›<¹W¯ÖÃ|4Ÿoiig÷øR…¢¼üÚµíÛ׬ùûïÄD¡°¥-U*±¸²27÷þýsçrsïß¿yóÁƒiÓ.œ5«o_33Æsò“—wðàÊ•?üP]ýä`c¥²¾>?ÿÎ�Ö®­­e2¿ûî³Ïúõ32¢Ñ(J(LJúóÏ~زåÞ½ÆÆ–KC*­«++ËÍÕü•Éüü¢¢´Á¾ŽR©êë32bcãâÒÓkk[[§±±ª*/ïÁƒóçóò}ž×žֱÔþ{Ûç˽@P”P˜’ò÷ß?þ¸iÓÍ›uuÍËJ(,,¼wïÈ‘ŸÎζ°x¼ß ¼/ìS(*+³³ËË›/³´ ôð00x‘„RY^~åÊöí�‡Qèt+«��Q£°¶æñ¤Òââû÷Ïž=v,6¶¨H©$¡01qÏžß¿rÅÕõË/ÃÃÍÌš÷|:ÕÕÕ‰•UïÞãÇ`n®Råå]¾¼ÿ©S©©š‹R™•uñâ�;åå!!C†XX(•±±‰‰ÍÃ|އÇС“'à⢯O§Ëå"QuuIÉ£GYY©©™™*Uó°›íì¬}?šD’“³o߯�Ç�k?·²9òã�'MrvÖ¼1íÉ·§Ñhzznnƒ�ehÈá zz|>—Ëb1$IQryccuu^^lì‘#ÑÑ))b1A”•]¼xäȃΟ߫׳êäEÊ„ÁP©4õõdàÌÀÀË+"bÈ�Þ½ÝÜLMÙl…¢¦¦  5õþý‡9’Ô„)ª±1=ýÀ�mÛóñùîî£F……¹»QTUUjê¥KGŽ\º”“#•ŠD‰‰{öìÜyýº›ÛÂ…ýû›˜<«®›ëXjÿÝí³í­‹ËmjWm›¼C¡(,<~ïÞÇÃ|ºº^^Ç�fgGùù11ž<™œŒË)Àë×�ž}O¿ŒÇ30àr_,ÔWQwýzaaÓ26ÛÇç½÷æÍ›1# @OOvñôìÖÍÝÝÈH(üê«­[µ3Ž\»W^Þ¿ÿðá––­÷Ÿb2==ß~{îÜiÓ‚‚ôõ5)õð05­¯¿ÿë¯.)ѬW]�—WS£T„…IÒé,–&D£\Jl¶¾¾‰‰™™¥¥��µµ‘‘Ž“I’¾¾=zôëõäQ5Á;ÍûÑ$‰„Ïüsí§®®-x¤Ó <<üü¬¬JJNŸ¾|ùÔ©ÔÔ¼¼’’º:©´õ!Øjuaajjy¹BÑ«—�Aë/¼H™Dyy\ܵkÍšÃÕuúôE‹æÌéÓçñgݺùû÷îÝôoŠjlÌξtéÞ½æý× —ÈÈ�½½ÍÍ9Í2 Ÿþý{÷NJÊɹt©²’ (*/ïêÕ{÷*+ûö�Š27oKÿ>¥²#©ý··O§c­ëÙå_]Ýü z÷Ý™3‡õôÔÌ¢íåå쬧WQñÕWëÖݹÓúô<ð*¼p°�Fc±tt4!$©T³L"©«“HÔj‚héeÏ¢TÖÔ|x¯^ŽŽFF\.ƒA’RiVÖ?ÿlÚtò¤¶ÿE=/Ð÷¢eÒ^¼¶_M¨ëMiŸ¯æÐø7ë@Ï>�À×74ÔÅeÿþÝ»5Ót¨Õ99‡íÛçæöþûÁÁÆÆ­ÏÆÛØXZZT¤P˜šÚÚêë3††ööFF‘’RQ¡Y§¸øþýœ‘(((  yÿ)Љ>¼¿é­dA††öö†† ÆË($Í|ÅŽ……¯ï AãÇûúÔô©J%fe;¶jÕÚµçÏWUAˆD ±±2™··‡‡vPªVÓ›ëšÊ£µ@’JU__Ròxï*GÇQ£¦L:4(H;|”¢¤Ò¬¬¸8‘è5›ê«°ðöí””ººààÞ½Ÿê¥Óõôll4SlTThû÷ñx={Î�»bÅ»ïjˆ¶Ürär™L­f2[˜Ü¹©mmÿžöùòjÙÀÀÚÚÀ€ ’“5íš ÊËÓÓKJd²ž=ÝÜ4횢¤Ò¢¢ÔÔ²2\N^¿h4� gÏ)SF�ºvíûï�yôH­&ˆÆÆ„„]»–.]¾<1qРÐЀ''ss:]. ++ 23nß¾y3+ËÈhöìµkçÏ÷õårMM{öìÛׯæÔ©Ã‡5oF“Éwìøõ׿þª®ŽŒ °¶ær¥Òââ„„³g�MLlÞÓÍÆ¦oßž=Û?Ck[H$ÉÉ›7¯\yâIº¹y{»º:8XY™šèè°X4šTZY™’’“óx@®µP4›­«ûøð碢›7oÜHHàrííy<ƒË53³··±Ñ×g0èt]]‚ íòò¢£�¹t‰ÁèÞÝÔT­.+KJºpáøñë×[žcö%4ÆÓõ¥TffîÞýã�¿ýVQ1dH¯^®®&&l¶BQWWT”ž~ÿ~NŽ�Á¨Q|eeÅ㹸 È秤hßóÖØ·uë† ΰaaaÞÞÖÖúú †B!×Ö–—ä䤥%&&&VVzz~úéÒ¥&ØÛ·m®ãަ–Áhiÿ–öù2kÙÌ, ÀÈèÌ™óç5àE¢›7wîܵËÄdܸ¾}mm)*??&æÀ�;wZž§^­˜LKˈˆO?�?_¥Ú¾ý·ßŽOO‹ B©,-½ÿìÙ}ûîßo}kCÃf a˜™õïÿþûS§fg¯^ý÷ßII"A(•……±±‡ýþ{llËûÐÑññ™:õý÷û÷73{Y¡Š’J+* ³²** ¯_ÞAPPX˜�ÝÓ½ÏX,KK??ãÞ½ôtM¯¶ÆÆ¤¤Ã‡û-)I³Ž�ÍäÉ+W.YfdÄdÚØ„… hgsäH~>EE•—ߺuðà¶m·n5•Ÿ¥¥««¾~IIVÖ“aÇ—ÒpZ¬/‚¨­MJ:{öï¿“’Ξ}| #£ÁƒGŽÔô`$IÏÓs„÷ß4¨¨hýú‹‹‹Õj‚�Jss¯];t(7÷ÚµÖŽl`àîþjSûooŸ/“ic9eJHH\܆ Ú9yëêŽÿý÷„ízzzšIUÊÊ´lx=hßIòù..C†Ì�»råwßÍ™3n\X˜¯¯ƒƒ‰ ŸÿxxƒNçñŒ�íí}|úõ5jÖ¬‰]\šqe2ÍÌúõûðÃï¾[²äí·##��y¼æƒ,i4ÏØØÑ1 2ò­·/þî»?ì×Ï̬m}¼^“im=`ÀÈ‘}ûúû»¸XYòxO†mh4G °±ñò �4iÁ‚%K&Mrski0*Iêé¼õÖûïGDxx˜˜<0*ƒaa>wîçŸ�dg×|ú6ÛÈÈÕ5,ì­·–,ùôÓÌÌ^eãi^_ï¼3xp` ““‰ �§™¹X›_MÉøøôìé⢧×ôIêèxxŒ·xñòåŸ~:~|¿~ݺY[<þÎ:’d³õôÌÍ�œüýCCG�z÷ÝY³"#­¬Ú8ëXjÿ½íóå"I/¯©S-ú裨(//33íK’|¾•U@ÀÈ‘óæÍ�eeõø¹ÎbÑéx¿À«FR”æ�t-Ñ\#HRû®É'?•HÒÓÿøã«¯Ö­»}[3$› Œ�£¢¾þzÙ²Q£¬­ß¼�o.<†À3ÈåyyGŽlÞ|ö,EôíÛ½»››µµ�‹%—×ÖæåÝ»wêÔÞ½Ma>‚ 3³ž=»w77G˜àUÃÃ8<EI$EE‰‰×¯§§'&^¸ð¬u™LGÇ‘#gÎ|±¡ÖÐQx€g Ó »uëÝÛß¿¸øÁƒœœÂÂòr¡P¡Ð|J£q¹FF66..>>½z 4xpß¾nnô¼xg@ACt ötötöt/8gf|||||<Šàe lïVŒWH€®'77777×ÑÑÑÑÑ¥¯®Ho|wÃÓ:ÒÍÃxºûºûºûºÆËܹD’˜¸ví¼y›7gei–ØØŒóÝw_~if¦P4ÿÔÕõ£�Ö­[°À×—ËEµ@×ôôU± Ir¹ÆÆöö=z 4bDTT` …›M’9"EI¥ùùçÏoÛ¶eˉ©©55J%�îìüÖ[+W.Xb`@£½ªü>y�ö§ðòPT}ýíÛ?ÿüå—»vee©Õff#F|ûí7ß jaÁ`hÖhh¸{wݺ/¿üý÷Œ •ÊÖvÒ¤+/îßߨ¸­W œã�ëYOñ_ø¶míî‚ žu×ñìg"ÿüü_~yyw,¯çŽˆA¼R……Ñч'&öêõᇞž8Uà¿ÌÜ<2òÝwG�²³#¡°´4==6öÂ…[·ÎœÙ¾=!!-­¢bÉ’ † ±±a³_ô2ÙÇÑÑ'Oj¾ÒÌÌú÷Ÿ1cìØÀ@ww>¿caDxs‘¤ž^·nýû{x0™YY©©åå·oß»WQ9|¸¥%ƒAQBazúµkYY*A�¤£c¿~~~††4Œx�Øl‡Ñ£ß?2òÏ?·o?w®¬LûL1z´£#—«§çàðâOo~ÉÌ™óᇠñd9<û™èÙÛ¾©:ì£(Š¢¨gÞüßt:AÈd‰‰‡�9“—·p¡«ëÓ{£(ŠR©ªªnÞܾ}ýúC‡’“+*U*‚Ðô{11qwïÓgìØ3F�òóèt‰$1ñçŸ?ûì·ß²²ÂÀÀÛÛß_ ÈÏOLÌËkh ÑŒ�}|¢¢† óðhhˆ�=>66;»¶– Œ�&Nüä“wßíÛ×ÌLû;¶LVVv÷îñãû÷Ÿø€ šï=€^Ö‰Ïwu ëÖ�ÍNMMH¨¨¸};.®¬lðà‘#--E¢ÔÔë×srÔj‚ ÑÜÜBC½½õõIR©¬¬Œ�mëãÉ£·|xþ•„ËÅü7<ïi‚$uu<=õô‚ ÊʴϾ¾MwÎÚo\±87÷Ê•C‡ºp!>>7·¶V.'‚`2õõmm{ô7îçMëÑCó¤ñ¤ÎŽ´””ܹsôèþýÑÑ·ogfj¿ÇÙl##W×ààaÃ&M3&8ØÒ²id“ZÝÐ�’rüøŽýuþ|ff}=ƒ¡¯ohؼ„´y•Jss�Ý´éÙwO>µ¶í‹Ü�´'ÍϪéŽ{…·CC† `fF55W®ìßófe¥æ±…dÑØlssŸððI“Þ}÷ý÷gΜ:52ÒÍ�¢*+ÓÓ/\ؽûèчe²§·”J¥R}}Ÿ�û÷wqa³•ʪª¤¤“'÷ïð€NïÖ-,,4T»üþýcÇΜÉËÓ쇢Äâ´´½{úiçÎË—=22êÛw„éÓ§OŸ0aÀ ‹šš¤¤“'ûmß¾´´ÆÆ—[-ðßÄ`˜˜ôíkgGAQII¥¥š/åö^£Øl‡Q£Þ{/2ÒÜ\»sóÈÈ÷Þ5ªù¯U/~½}q­¥íÝwgÏž={ΜÇS/IòùÎΡ¡¾¾<ADUU\\\\i©LV_Ÿž~ã†&ÔÇdzx„†zzêéin¼_Çwhí%“åä<¸iÓ�·nåæŠÅ¶¶ƒOŸþÞ{3gN›6lX` ±±\.ÊdjuÛö×±HKJÊž=k×þñÇÕ«™™R©‹Ë°a3fÌœ9cưa®®2YVVLÌŸþôÓ_¥¤ˆDšorŠjhxð`÷îmÛÎ�Ë̬¯×× =zêÔáÃ}|‚¶—C[Ÿ‰ZÒÞ;�ÎJsçx…=û˜LKËþýÇ�ëÑãöíM›îÞ=uêôéæƒÈš¢›4š®®½½‹‹¾~vvlìÍ›IIuuBam­&‚J��uu��*•æÍ�bk;pàˆƒûúR”…Ejê¥KÙÙYYac3`À°a‘‘¾¾en®]ÞØXS#kö#•æç߸ñà�H¤ÙSFÆ… dd<™«‚‚‡kj”Ênݸ\ƒøÿÚÛßùé_užÜš$›~­jï5ÊÊÊÁÁÓÓÁ¡ùï~š%ººÚýväzÛ”þö.×þ&ùdÚ¼¼žþM­ àe]‘‚ÃqpèÛ×ß_G'..6¶ºúîÝÛ·32‚¼¼‡)Š ØlOÏ~ý\]ù|ÍÞ^Ïwhð_Ѷûßç?SI²Xººšûj±X.øðæÍ+W gg{{7·¾}G� 13ÓôÄoé˜�/íh¤%!A,ÖìgĈɓ£¢zõh4µÚÉI¥JJJNþûï‚‚ÄÄ7 ¤R//‚(,Œ‹ËÌÔt{°³:tìØAƒ‚‚8œää›7÷ì¹zµ²²yÞÛûÜñø3QçÜ�89Õ×·'Í/÷Iç%ûÿ7IòxîîÆ joïÞÁƒ�µT¤ryaá‰?ÿ¼aÃ… ÕÕ††Ý»�9lXh¨™YMÍ©SÛ¶�:U\üìã·}¹6f¬Y®V«TÚÏML ˜9sÊ__=½æ]ZI’Å2222¢Ñð OjïU±ù×A„JU]��pófa!A�foïãcaÁdj®�/r�zöŠŽ\oU*…BóþtÇÿg_‡µ×Þg—¼Ü+A‹ekÛ»w÷îúú±±W¯ÖÔÄÄüóϱcJ%E—ëíݯŸ³3�§Ù×˹CkË•whðßÐÖ`_ËÛ5_Î`ØØ ¾`ÁüùffׯÇÅ¥¦>|˜ŸŸ——˜xãFFÆýû7n¤¥Äòå öíÛò[y[;J[Rýx¤åñ ¤¦Ë—ö3’¤Ñš½)'Í¿ýi4&“N:ÌùüTµå¹£õ}¶÷¤¶¶=i~£ƒ}�÷WÑÜÈ�Ó·ï… ?ÿ|íZ]Ý㙥(¥R(¬¨ ‚ èt}}3333­¡!#CQ}<úôQ^d9›mcâãÃã%%ÅÇWVÞ¼yõêƒ!!..�&§§?x�—gj:~üGEEYYáwch®}WÅúúÜܤ¤„¥’N‹+*²²nß¾xñÆ�‡•J:ÝÑqÔ¨ õõ5¿8±X/r�jé:Ü´¬½×[:�Ã10Ð ùkl,,¼zõܹ«W…B-%¥¨èéõ[»Þj—1™<‹E!——”ܹsëÖƒ4š……¡¡��««­­ŽÞ½ðò®H †¥e¯^AAFFW¯ž?_]]W§é¥§£ãç×·¯ƒ‡£ÝOgÝ¡µ÷J‚;4ø¯xÞw·Z-æå¥¦ææÖ×k—Õ×çæ&''$¨Õ\®žž££¦ÏšL–’²k×o¿]»¦§çïïëëç×·¯««³³¾þáÃÆÅÕ×S”J¥V·vjí9¢ý‘kë^½¼½¹ÜÄÄû÷óó�ß·ïÌ’ìÞ] ¨­��?qâÑ#‚ÐüÈØ«—µ5‹¥ÙÎÌÌÃÃÒ’ RR =:>:úúuµÚÈ(;»¬¬íG<ìØ¾OÛ{Ù¾4¿áÁ¾§×§(ÇÞ>"bäH�[·þü3=]¡xüSGÇ¡CGŽôñ)*:|8)):zÛ¶�;mmýý��éô§÷ÖYƒD‚ÏïÖmêÔ ÞyG 8zôܹ;wbc�ùë¯k×Bóòicc'§ž=}|x€Nƒ«"ü{àŠðfÁw7tû: ®Šðï�+À›ßÝÐY:ì+-----E!hàªÿ¸"¼YðÝ �¥CÁ>SS"@]]EE^®Šðo€+À›ßÝð´ÚÚÚÚÚÚÛ–†âèìè"ìè"ìè"ìè"ìè"/sçbqLÌÂ…C‡~þybâÓŸÒé�³sÿþï½7þüùãÇ;;óx$ùø: ENμõÖäÉ{÷–”h–ÙÙ½ýöŸþóÏ”)vvLfëÇâóÃÂþ÷¿Ý»ü±_?##íÉu¿ý6:zË–�‚èÌm[ff6eÊÞ½;vL�ZRòÕW­¯Ïᘘxy òÖ[ ~öYx¸�‡ód©tL^^fæýû(€—ÁÁÁÍ- å¯ ãÕÊÐ08xôè!C,-åòœœk×¢£oßÎɹxqÍš_~ñòêÛwýúQ£,,èô¦-äòG�NŸ¾xQæÓÈÏ?wîâÅüüñãgÎtvn ÷=I,¾zuݺ�¯]óöþúë°0� íi}Ñmµy´¶f±š/§Ó ýý ét‚hž›¦õ™L™¬ª*)éôé«WSRŽùé§ÿýÏÈÈÕuçÎY³ìí[Ï%¼Ї÷íûüóÏ>;~¼¸X©$µº±±ºº¦F,æñ ML¸\�  +« ¾ÿþÛo6mš>ýŸF�Þ¿Ë–�>ÿe¥MócưaŸž˜(ŒýÓOýµx±�ŸO’ÚO'N|‘”Èå��?èPJŠ‡Ç¸qÃ†ÙØh®���wï®^=þ©Sáá?ÿ¼l™¿?�÷ú먽9¥(™¬¸øêÕ­[·lÙ³çÂ…‡ëë•J¥R.W«ù|{ûàà!CÞ{oþü±c�œ¾øâÅÊðÍ¢)ÃáÃ?ÿ<;ÛÀ@ÛªµètkëáÃW®Ü¸qÊ'§Ç¿'ßÿÎÖûâ»f{xâããããã_öQ8œ»w¯^mhؽ{÷n¹<999™¢ …‚ÍöòòòÒÑ7nÜ8¹<<|Ô(ÔÈÓ+ ö98 üÞ{s熄ðxuuîîùùáá+W¦¤„B!“)•õøúRi^Þ™3gÏVTAðxööÖÖrù£GEE¥¥çÏGGççOŸ>w®³ó³D¢+W–/ߺ5<Ü×÷‹/|}Û“ÞÙ¶)�­=ȉŭ¯OQµµ^^åå£G/]šž^R’’R^.—‚}o>&ÓÉiÒ¤_=qbÒ$íÕ )´ô×_O€Ä☘W›B.×Î.,ÌÊêÆ�;Ξ-.^»vìXkkF‡¾%Šüü öî=uJGgäȦ`�ףǢE[¶ôèñ¦Ö§Z]S³téçŸoÙÂ`¼ûîêÕ|÷��—K’jµX\XxóæÁƒgÏÖ׫ÕNNÿ­ÖÎã.Zôt«î*¹{Ó[/¼Éh´�;7l(/_·nݺ§?•HnݺuKóWGgܸ[·èôo¾ùñG”ÛÍ+ öåå�=ûûï¿þš”¤TææÆÆ¦¤A˜™ öÖ[ƒYY=þH)•ffž9såJe%A„©éàÁŸ~úÙgµµ_½`Á•+ååçÎ�;÷ðá8;{xp8OËÅeĈ~ýjjNœ¸v­ºúòå5kÖ¯8pÓ¦ÐÐç§³#Ûjó˜’Ò<ÉbÙÛ<}úÈ‘¶¶OoCQAQrymmNNLÌ… šåffžž¦¦ojÏè JeIÉ•+ëÖ}ôѤ¦>zT]-r¹³gÿòË¢EC‡ÚÚ2™!——”\¾¼~ý/¿ìÚS\,‘(j5�çé9räÂ…ß~;i’··žÞó^@£÷îýÞ{~~{ö¬\ùûK—Òh-¥«´ôĉÏ?_¸pûöÔT…‚Å¢Ó)Š$��{ôx÷Ý•+çÍ2D HHX»öÇE¢ÊÊììéÓ‡ ár¹ÜnÝfÍZ¹rÈ�Ó§çÌÙ¿¿yO7µº¡áÁƒ~úiõê'’“ëêÔj’$I}}/¯ >þøóÏgÌðöÖÓkzµÂرkÖèèôêeeUX˜ž^PP[+óxO–O[R«Y³íõRXxþü_%%yyÍŸ¿oß Aš0Ÿ¶$ù|›>}Þ~ûý÷5KÄ⼼ίÍö­yñâ/¿üôÓ®]ׯ—•Édr¹\®Pp8ÖÖƒ-^¼lÙœ9½zéëÓh�Ó–ÚB[ƒ?ý¤«biYX˜–VXXS#1™îîcƼû®¿uutôÙ³ññEEµµB!‹åãóÁ¿þºxñˆvv,–fcÆüø#‡Ó½»¹yIINNaaM�Zíâ2~ü²e«WOžìé©££Ií‹´®Õ«ùü^½llJJ23KJÔj7·yóÖ¬™?ŸN_¹ròä¦~ššz<Ýsóé¾¢š5;’k\þ»¸Üøøk×ÊÊZó=M$:tèÐ!##____¥räÈ)SP†ÿ¯0ØWSsûöÑ£{ö<¾´ª*9ùæÍ„„òò  �-,4!¿ÆÆôô£G¯_¯­%‚05�ˆ4¨[·À@‘($DW÷Ê• *+cb¢£32fÎôððó{2ܧ§çç7kÖ‡ …ÌŸåJmí©Sk×nÝÊåüpùòU«¬­7mZ¾|Ð CCmùÐhFFƒ-]úå—ß|ÓRùx{s8mK­Ÿ_ÛëE&+*JI©ª²´0ÀÕUW·mÁ¯Î¬M;»¢¢¶Ö»RùàÁŽ›6%%õé³aÃÑ£cÆX[3™%—WTdg§§‹Åšo…"7·sÚRcc|üêÕšðnkÃxÍÍ5eB’††,]úÅ_miI’¹¹[¶ÌšõÞ{¿ÿnnþý÷[·nÚôùçšå[·~ôÑ_üñGffDÄÒ¥ÞÞš}’$‡co?aÂÏ?û­¯¯ž^]ÝåËŸ>gκuÇ�‡…mÞ¾ÜÍÍÀ@*MHh^³Á`˜šzx8;hÞùkhai¹~ý¹sÅÅ2ÙÔ©––$ù"­‹Á03ëÖÍݽ)Ì×¹çuGr ÿE2Yb¢v¢O‡‡>|öúyyNNNNryfff&—‹òûoa¼ŽƒÒhl¶±±‹‹——‘A=ZU%WW76ªTAQ œ:uë–H¤]_(LK»wïæÍ'÷#ÆÄDG§¦ …~~žž-Çа_¿Ï?ÿè£=nÝúé§»we²¶§ñÅ·m;Í;û¾ø"0pØ0#£¹sgÎܺ57·¤dÏžüóÏ6ož=ÛÛûéYŠ࿊F#I’Ô†š(Š¢T*’40 ]µj×®¥K{ôèød †­í˜1ï¼ãç7uêŽG�:;?Þ›H&KMݹsñâ ªª&O^³æ‡–,éÖÍØX¥º~]3h±ýÇԼЀ¢¢¥ë�6Ïjµf�¶–ÏËJ-‹eiÙ­›@P\|ëVf¦PØ¿pð‹ n}ÑÚT( ÛSïÖÖÇÿôÓ‘#ÉÉwî$$¤¦¦¥%$ìÛ7þæÍþ9qâºuëÖMœH�ß–Ú�$5eÒÚòçYU*¥R­&I:]»§Îj]ÏoÅõ"�ãÎÈ5@Ú«;”ö}vÛ¶mܸrå²e þøcf&A›íïéäÄåjB}‡Ý»'•AOš´aùswïž?õªæïÕ««Võï¯ vçαc…­ÝÓéff|öÙ¼y^^íým¾ýÛ6åñ·ßšÿݸñС»w««[û]žÍvrš1㫯æÌñöÖôM()Ù¿ÿ›oöìIKklÄM>´ŒÉ43 êӇNj‹ûí·ãÇÓÓ›®†Je}ýDZ±W¯ËåíÛ/Iêéùû¿ýö”),ÖéÓ»v¥¥I$MŸªTbquµ\ndäãÓ³§‡‡±1‡£V×Õee%%UV*•Í÷Ãáèê2 ÅŵµÚOZº¦q¹..ýû»»—–;vüxJŠH¤ š¨ÕBazúñãûö•—»»k¾)Ú——¶§¶=ång1mš§gbâÚµ?üpî\n®X¬Í—Z-‘”–Þ¹ó×_;wž>]PÐÖc´½6ÛWïRi^Þõë7näçs¹žž :eÊ'ŸÌ�;þäɦ¦%%ÉÉ•• ÅËjK/E)R©\® èÉd—.=Z^îâ2h�££&LýòZ—�®«kbÂfWT¤¥K$š7ñVU%'ß»WV¦�ð&‘ÉRR~ø!4ÔÁÁêÿXXèëkB�úúÚ¥¡¡?ü�’RQqüxÿþAÿþÇ�×׿ܴÕ×?Þ¿¿&PnañÁ11 M×ͧ/–©4#ã�?V­úã�Œ Í]¨†PxéÒŒ={Θqé’vÌÉë×ÞœR”D’�}àÀ¢EÆùúÚÙY[[[[Zš™››»ºöë7cÆO?]¿^]­Ru¤ ß,šœ2�·j-{û~ý–/OJjÞÞ<ÿÎÖ /ŽÍöõÕNªéµ÷쿚5Y,7777”ÞÍk|g�&8;ðá‡~8wnd¤‘AÔÖ^¹rüx\œæaÂÀ 8xÐ ;;6»ù~x½lÙ† ?þXUE\.—Ëbq¹66!!Ÿ~úÍ7>><A0�Õ–ÚþÎ¾Ž“É22þüsÞ¼±cwîlh ÕjKËÁƒ?ùdÙ²æ­åeµ. ÇËkòä3œ�ÿ÷¿ †½tIGÇÐÐÒÒÃÃÁA­~¿¹Ùl/¯Å‹cbòò/Ö,Ñ„Eìí×­›7/#ãÊ•‘#õõ›Ö¯¯?~üÕ¦PGÇÍmÒ$gçS§¾üò�?rs¯]ûôS'§Ž½çX&ËÌüóÏüóO�àÃß~ÛÝ],ÖÕ8p÷�ßÔï •ª´ôС3ÂÂ-b2W®Ÿ$Uª††ÌÌS§6mÚ±#+«ºZ¥òñySóùbtuÃÂvï~ºUw•ܽé­ž<«§M›6­±ñÊ•+WÚ»Õ‹ýÄ o.’¢^dØJ|||||¼ƒƒ›[@  ®®¢"/ÏÀÀÔÔÁ¥ðò==×mkïü/jË)//3óþýÀÀÀÀÀÀçí¯y°ïÑ£–‚}ýû[ZÎ�kj:fŒ³sZÚÍ›éé%%µµ::ƒ­^}êÔîÝï½çâÂf„Túðáþý‹½÷Þ7ß?ž“#Éå*•ž^÷î¼}ûŸ~ùeŸ>Ï »6¥döìãÇûöýþûéÓóóþ9!á�?""h´'žL§B‘›»uë¨Qýû/Y'“±Ù EÑhƒ÷ÝþýÛ¶½û®‰ÉåË“'wë6dȾ}%%zzææ––::::AA?ýtâÄ´iÛ¶…„lØÐ<ï*UuõµkkÖÌšõñÇ7ÆÆVU©Õ$I£‡„|üñÆ�Û¶-\دŸ‘�ÞT>Ÿ|" î蘑qçNVVyy}½®î“åÓ–Ôº¸°ÙÏ®‘æŠÌÌU«ºw0 !aÕª””þ‰ŠZB¿œÚlûš2ÙÇ{÷ΟÿÎ;K—ž<™—ר(“I$r9�çæ6eÊo¿;öË/ƒ›˜Ðh�×–žW†Ú2ùôSCÃ#22nßÎÎ.+««c³ƒ‚>ù䫯,)Ù½{ÇŽK—rrÊËkk9œÐÐU«NŸÞ»wöl77G³ ‹�>âó#"ìì>|ð ;»´T­öõýüóƒþê«à`}}M½¼Hëúøc}ýaÜ�>Œ�ÏËS©ºw_»öÊ•ß~c0&MrtlÊ#A´œë§KC³fGr�+1üw½X´¤íO$ùûï¿þZW·~ýúõÏ^SGgôèÑ£éôeË~ú õò&jÏÝÚ“h]±@àU¢(‚01?þ÷ßãârrŠ‹ /\=úÁƒ¥KW¬ˆ‹‹ B&KN^µjÒ¤Ÿ~ÒÑùî»[·rs ‹‹KJ22~ÿÝÕu÷î÷Þ[µ*1±­Ã&9/¯O>Y¾¼gÏË——,Ù¾=+«åAæ †¥å˜1¿ÿž˜XVVYYVVRRZšŸöì˜1ññ_}õÍ7±±"‘�ATÔ–-›7‡…húygg92y²¥å“½sÕ겲Çßygòä<=û-9¹¤¤¤¤¤¤¸89ù·ß<<öïŸ<ù�w.+kþGŠ¢Ñ,-§Lùã�øøÜÜ’’§Ë§­©m_½H$^¿^Râà0|x` �AÛgï¬ÚlO½ …×®}ñÅgŸÝ¼9zôéÓ99……ÅÅåå••……7nüúë˜1Úñ>�×–„«WgÌpwþ0^Š"I ‹I“víºw/7·¸¸¸8&fÚ´ìì+¾ûîáÃaÃ45[\\\|íÚ´iË—ÿðCSÍA�$�çåõÙg'N$'çç—•¥¥­YÃãmÞ?4tÍšS§BCQÿl¶ƒCxxh¨½=—« (9:YZ66nØPS£T*ùù§O<ÿàAYÙ„ ÞÞþÙ4ã³\^_¯RõéS^® Ùµ¥W�nb2dÈwß}ôÑwßED,]zîÜØ±'>½žBQVvኳgoÝzúô½{¹¹ÕÕ2YccyyUU@@ûg:‹ÿþûÎ[ÛO>ÙºµW/�@›� W¯�>úôS[[ÿ½{Åâ ´CÖŸW>/+µšò}}µ©Pðùm¯w.×ÑqÀoï’’+Þyç�w "#ƒƒ{ôè×/2rÀ__==M¡ÈÊꬶԖa¼šwjÊ$,ÌÁA[&¶¶þþVVzzׯGFöïß|¹ŸŸ©©X|ýzÓÛj ‚ X,kë^½üüŒ�5ï·°8pÒ$'§O?Ý»7;["™?ßÙ™$_¤u±X66½{wïnnþì¹Ò_ô¼îH®àe‘Jƒƒd±4ŸüôM|g0t.û ÓÑh4Zóù¦Õj¥’$MMÇ�;p +kß¾�uu;z ËÝ}öì•+ÃÂüü¾øbÓ&oïLJsK$qq‹þÅÅÅ‹=zþüîÝ=zXY)•§Ni-¶ÿ˜¥} NGgo^>/+µŽƒCïÞff¹¹'NÜ»WW7eÊ�!ÏÆÛÙµ)—gd´§Þ�œ>úèܹ¬¬ððØØ ._¾yóîÝ˗׬1bîÜï¾ûä“ØØ«W?ý” :¿-µIjʤµåÏ ²R”R)—«T4ƒ¡ÝSgµ®ç·bµúEgïX®àÕÀ0Þ—@¡¨­MM=}úôéC‡ö쉎>sæÆ�ªªÖ~ãR©êê=JKËÊzü×Z…"??&æÐ¡S§JJþ qù[zàÍÁbÙÛGE ª§wöì×_oØpçŽfš‚ …¢º:!áĉcDz³Û;û)I .]úÙg\îŽ+WÞºÕ| «RÙÐPR"‘˜›‡†FFZYñùjuEÅýû7o6øK£ñx††LfMMvvE…öN§¥ÀŸïë;yr�ùùë×ÿöÛíÛÚ|¨Õuu·oÿöÛúõùù=zLžìëÛÞ÷H¶=µí)wW×·ß^²¤G�«W?ø`öì�;““´%¯V‹Å¹¹gάY³xñÎ�­ …îHm¶¯ÞÅⴴÇ�ÏÌÔÕ ;öƒ¾üòÇ·lÙ²eÞ<[ÛÜÜÛ·‹Šd²—Õ–^6Š’ËÅb™L3{¸D’žþÏ?Û·ççûûO˜ ™(ée¶. ÃÀÀƆË-*ºy3'G,ÖÌc^T{ùò£G2®T]‚}�N¡(/OOÏÈ�ËÝÜ¢¢¦N:4*ªOm‡ý§©Õõõùùš`ß‹ýÂðoÇá|ûmtô–-={^º4mš››™™™™¹¹‰‰™™§ç¤I[·¦¥‰Å/Ò/ˆÉtrz÷Ý~˜0AG‡ šÿ¼ª«Û»÷²eK–ØØüúk` ¹¹••ƒƒ¯ïøñ[¶dg?ØÒÑ Z¸p΃µkÍÌÌÍ]\F�þ矒’'ÎÒé&üñÇß�›œüÁݺYZZXXXXZzzΚ•”4vìßÿñÇ„ mŸ‹»½©m:ÝÊjòä¿ÿ¾tiùr“¿ÿ7ÎÍM“bSS‡ˆˆ+’“��]]�ŒŒÎ¯ÍöÔ;E‰Åii;v¼ý¶¯¯©©‘‘™™••��‹Ë°a'Nôë·~ýO?õë§«Û™m©íïìë¸ÆÆøø¯¿ suµ³³°°µ�Œ>GŽ„‡ÛØXZ:8øúNžüÇ……*Õ›8{8´ì¥ÎÆ«é vôhl,‹ee¥§×ÐPUU_/“)L¦©©¿ddÏžNNúú4A¨Õ"QVÖ¹s×®%'WW«Õ4šæ×TÏÜÜÇgàÀÀ@ÍšÚ}Þ¸A§›šòù UU ,–­­ŸŸ¹¹Tš“SPP^.²X..ƒ‡…ùù™™in\(J"),LI¹uëÎ�†©T¥"I{û  Aƒ¼¼ÌÍ9œgÅ©Õ"QN΃7nÄÅ•”46ªÕ4�gkëíݧOïÞ¶¶::UUwèÐÕ«99B!“ÉáðxŽ¥eÿþ[Z>y ¥T>½>“©£ãæÒ¯Ÿ‰Ibâ‰7o²ÙÖÖzzBaUU}½D"—3O–^Çs§TÖ×gf^¹rëVjjY™RIt:—kcÓ·oŸ>Ý»ÛØ°ÙJ¥¦äÛ–žç—�®m!GŽÜ¸Á`XXèêŠDµµõõR©ZmhèâÒ·ox¸‡‡±1‹…[ø÷Ãl¼ðïÑÙ³ñ@G´}æ_è ^öl¼ðßÑ‘»µWðÎ>Š"I.×Þ>,¬gÏÞ½ut¢¦æÎ� bbjjììBC�� �˵³ ‹ˆpqár ’Ô p}ðàҥ˗ã㫪ìì 05ýÿ gxy Âfgd\¼�žNƒ�=x°¿FÆ¥K/¦¤ÔÔxy™™™šÒé��……wï>x@Q^^ÇGE™š²Ù*UuõÝ»—.ݼ™˜hc3dHP��Ak¿’Q”H”Ÿûö�;b±³óðáC‡:;ëèH$99gÎ\¹rûvv¶±ñÀ�îîÆÆþþ½z™˜$'»¸ >l˜¥%“Ùjñ3Z__¡ÈÏ×<9;»ÐÐ=BBZ.½ŽçN¡(+KMMM‹]]##§Mstäñ ¡°¦F"y|¯¶¥§me¥®@$É`èé¹»GD„„˜š²Ù2Y~þ… /Þ½›‘aké㣣CCT€çzÁ>:]W×ÔÔÔTÓ—‹ BGÇÌŒËU©RSåò¦^…%‘¥¥ÅÅ¥§—–ÖÔˆDJ¥J¥PH$2™‘‘RÙ¼ÿ!�Æçêë³X4Eéé±Ù ÆãKX,•ª @»•RYSSTT^^QQYYUuèPa¡¶¯˜Z­PP”¹¹TªBÛZ8L©¬­-)©¯×Õµ··¶ÖÓÓŸø|kkWW>?117·¡A©l `½ÊÒëxî4åÉåÖ×gdÄÅݼ)“ÙØØØXYÙÚÚØ0™ÍûÕµ-=í++�Ï72´}9ss''�{÷²³E"• Á>xSéë�yåJmíÈ‘( x5^Ël¼š™¤š¿ôX¥ª¬LI‰‰‰‹“H<=#"BCCBLL8µ:??&æøñääçïíYK4s[1VVÝ»eeÕÙÓ¿ÎÒëxîèt�ÀÝ=<|ôh#£ÂÂG�JJ nß¾|93³¢¢ÿÁƒ»wo}°AKééŠR«U*Šjm¶/hÙ¿¤¿”Z-—K$*—kllaadÄáÐéjµLVSS^ÞØØñi+ CC++}ýêêÌÌììÊʦ9Öäò††’’ü|‘èÉ×/?¹�ÀÂBOO(ÌÏ/.ÖL¥¡V‹ÅEEYYb±žž­­žãC§t:‹E’r¹H$“½H^;ž;¥²®®¬¬ºš$ ||||ºw÷òÒÕ•Éjkµ½Û“žö•E©ÕJ¥6…J¥P˜ŸŸ™ÙØ(8:êë3^K@àMô/ ¤0™ffžžÝºñù±±gÎìÙ“•Åfóx::FFúú�ÑcŒ$ù|;»^½BCE¢Û·¯\9w.3S.×LNÂdzy›™ÙÙ=k::vvÁÁ=zÔÔܸqâDttb¢ZM’\®fÒ WWïÅz¡1™¦¦..NNtúÍ›§OïÙ“�m`àéÙ»w¿~&&¯*w!—WW§¤\¾|÷î�J%A�$‡cbâí=p ±±ö‚m/íö••JUU•šzñâþý))r¹\NQ::ÎÎþþ}ú8:>oÒhòRgãx6Íl¼Ç�''»¸ŒùìÉLþÝ0ü{`6^€×³ñBgéÈݦ=è"ð>4x�˜L;»ÐÐqãž=ÄÚ=ûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºûºŠà¿)>>>>>åЕ ØðŸãààæ€rèz0Œ ‹@° ‹@° ‹@° ‹@° ‹@° ‹`tdc’T(êêPˆ¸*À¿®Ho|wCgéP°O "@mmmmm-®Šðo€+À›ßÝй0Œ ‹@° ‹@° ‹@° ‹@° ‹`  kˆ�����G9@kQе½Ô`ŸHóÁaa <}ÓE§ëéÙÚúù……�óÞ{~8iR�&&ŒÓ#—çäüöÛ„ “&íØQT¤Yfg÷á‡îÛ7s¦ƒ“Ù–#¶,0píÚ«W·n %ˆ¶mkiùöÛ{÷:ôñÇ®®,Vg–IëGQ(**nÝúûï�·o?|øêÕ¤¤ÒÒÆF‚ ’äñLM==ƒ‚ÆŽ]´hñâ åò/�Î/y:ÝÐÐÝ="â½÷¾þzñâñãÝÜx<’|r«–ÓОÒ{r+·–ÓÓZšÛÛ~Þ~»´ô“O:~vÀ“Ð�§! ÿ¯0|`hد߄ QQVV*Uuuvö­[çÏß»wâĺuññ×®%'¯X±wïÊ•‘‘¦¦tzóíäò¼¼Ó§££µÁ&�üü'ΞÍÏŸ>ýÃ��µA7ËÖvÈ�wß�6ÍÃC.×l]Ppúô�×®ÕÔhÓ0dˆ­­f ËÁAûÿšõOmÓšZtº±q÷îFF�§±ceò¬£¨Õuu±±Ë—O›6wnttM Ax{‡‡�ãéi` P”—gdÄÅ]¿~ïÞ©S—..X0a‚£cGÊáÅJ¾å|Y[Ëå99/;vûvFÆ™3«W/_îãÓ·ïŒoaÑþ2lž/‘èÉMŸÞ”£+·ÎníÏn?¥¥?;àM£ ÷ï¿`A|¼¡á˜1Û¶9²xq@Ÿßþ>|ïÞ)SNž|VàûÕS«kjbcùåË/úéÂ…G�)Š¢”J:ÝÂ"4töìï¿ÿâ‹#ììØl‚ ¥²¬ìÂ…Õ«.\»öÊ•òrµšÁ03 ›?ÿ§Ÿ- 73ëHðš¢d²üü£GçÍ›5ëâÅI“Þ¼ùé²R*«ªnÞܹsåÊ_=~<=½¡A¥Ò¤#8xƌŋ-9RGçÌ™Ï?Ÿ3gÇŽììÇ¿Ôj‰¤²²®ÎÍmíÚK—¶n åñª«cbV¯þüóŸ~ºt©°P&£Ñh4‚àóÝÝÇ�ûòËeËÞ{¯gO�€ö^�ÑØ÷Í7³gŸ:5lØ–-ß}׳'�÷¬øbm¬íGéÈqåòG�NžÜ»79ÙÛ{Ê”áÃííÛÿ³@WרxåÊ•+ »wïÞ-—''''S”B¡P°Ù^^^^::ãÆ�§£3zôèÑ(«ÿ¦Wìsp9ò£�>ûL{‹¯T~òÉùó_=eÊ’%.\ºôã�ë×ß½òý÷ÁÁºº$©ÙJ*ÍÎ>qâÌ™²2‚ >ßÑÑÚZ&ËÍ-**.ŽŽŽŽÎÍ�9sî\w÷¦ ÕÈ‘}4ožö¨"QLLzº6$¤ý´¥ÇŒÇôžµfg—ɳŽ"ß¼yèÐ… 55A FÏž_|ñóÏ+W†‡ Ú2R«Å✜ë×ïÜa±:^Zí+ùÖòÅç×Õyyåå ´dIb"AÈå2™RIQ/V^ÍóõtŽfÎ9ÒÁA“±8-­ýåöxx¹5+uüì€7—kgacsýú† 'OΘ±}û¤IÖÖÿö¾›,–…EÿþŸ}¶mÛ'Ÿ¸»›˜°X$)“eemß>mÚGmܘ�0hК5AA\®P·~ý† ¹¹Ã†mÛvúôĉ¥¥ûöÍžýñÇ¿þ¼bEHÈ‹´äÆÆ»w—.�4iÁ‚sçJJÕêÀÀI“ž^O©,.>räË/—/ONúÈßÿ÷ß¿ùfûö{÷† ùöÛàà–û„©Õõõ÷ïïÞ½bÅêÕ$$ÔÕ©Õ$I£øúN˜°hÑW_͘ ¯¯RåænÛ6uê‡ææÎ›wðà?„„èé‘$A¨T¥¥‡½ÿþ;ïÄÆN›öÏ?k×ÐÞh4šŽŽ£c@€‡‡v EÉåÕÕùùÅÅ4š«k÷î––šÖ.“=z“–&DF~ò‰µ5“I4š•Uh×¥*}}/IDAThd¤@°o_LÌ£G2YHˆ·wû¯Ú<^�Ë–�8qíÚÂ…Úžb-­'•ææ^»–›ki9bÄ AŽæg0ŒŒ‚ƒg̘<ÙÐpæÌèè¬,©´gÏ€€æç”\ž“óÏ?ë×ß¿ïï?wîÂ…M/,Ð×wwïÕËǧi]’ÔÑqròò26–É.]‰Tªiš^oƒ¯Y£¯ß¯Ÿ¥å£G))……UUB!“éå5qâÌ™={VVFG?~÷naauuC‹øÉ'ëׯX1v¬�‹Õr¿9•ªªêÚµ_ýâ‹U«¢£ IRWWW—N¯¨�H,,ž<úêÕzzýúYY=z”’RPP]-ñxAAŸ~ºnݲe£FÙÚ>ë(ׯoذdÉêÕÑÑùù*�§£Ãf×Ô”–š™-_~þü–-¡¡šc)%%gÏ®^ýÑG›7'&æåUV …\nÓQ‚{÷V®\µ*>^(¬¬ÌÈ;6*ŠÇãñ¼¼æÍÛ¶íÇ##MLh˜X à5Ó„ùüü xüDzöª¬lh�ÉÖ¬Y½zõê… -Z´e ðlILLLLka¾§‰D‡:ÄfûúúúêéM�:u*Êð¿ã5÷$a2�Œ�� ‚ „Âòò††¦‡¥ÆÆÔÔ#G.]ÒôÎ23‹ˆ2ÄÇ'0P(ì×OOïܹӧ++/^<~<#ã“O¼¼:7RSsíÚ�{ö<¹<0ÐßƌΠö=ÿ(::ݺ͜¹páðáqq‹ŸgÎæÍk×¾ý¶§§fHà‹ëhÉ·œ¯ÊÊä䘘øøòò¾}##-,^fÈ�Ãy‘rk[ítNÍvôì€7›íì|ܸˆ//�àE¿µ›ÅÆfòä•+—, 32Bàþ=T*¡ðĉ¿þŠ‹»p¡¦F­&ŠR*kkE"¥’ËÕÑáp4?1††öö LŸîâbmÝñW«Èdee+V¬[—›;xð¼y!!��Ú¾ä¯2ïuuYYÉɦ¦ÞÞ®®íË—&`7th@€……J¥Ri¿WÏžML¬¨Ð,×,‰Ž¾¿ùKl4?FjºDNNNNN޳³³³³3Z$@k4ƒvÛ»•P¸gÏž=öý×¼æ`ŸBQ[›Ÿ¯ )„¾¾……¾¾öfR$JLûÌϯG�¶¿?èyž?YÄ«: �nf¾té¡CÝ»Ož{áÂÅ‹±±ññII wïÞ»—›ÛÐ@"ѽ{›6­ZuölTÔŒknþâ7Ñ/Rò­å‹Ç“ÉÊËcc×®}ç�Ï>;räÔ©5k¶o5ª_¿o¾ñó{™mëEÊ-2òùµÓ¶)VÚ2ÙHGÎè h4� wïÙ³Ç�ß¾}Μ­[oÝ0 <üñu¤Ò¬¬3gRRÌÍ'N\²ÄÛ[OO{fëéy{�?i’¹ùÔ©gÏfeI¥áá½z™›8y²»ûï¿ïÛwõjEÅÀ�ÆÕ×?~ò¤L¾r¥·wǯ’l¶‡Çœ9»w_¾üþû¥¥çÏ/]úÁ‹íÝëæ¶cÇŒvvAQõ¢ƒõ;‹åèøÖ[k×.\øÛo«WÿïãÇïÜ)ÑhŽžž••½=Ÿ_UÅf[XðxMWJŠjlLJÚµkË–G�¾ývÕªg ­V©**._^¹rþüC‡,-çÍÛ¾}Ð cãŽ/X,KË  À@KK6[`47ws31áó<èÕ«{÷æË]]‰äÁ¡°µ¤Ò¬¬sçRSÍÍÇŒùê+GGmßF&“Ãa2Ÿ`jŽÐt++//cc©tÿþÖfh:ÊèÑ_}ÕÔƒ’Åâó;ï(𪘛GF¾óΈõõ©©—.�8{üø† 99ÕÕsç~óÍ´iÿ1à߉N×Õ1âí·1B³¤yîã�_OîMòt ïÙËàÅÈd‰‰Ú¹ƒÃÇ>{ý¼<''''¹<333¥÷_óC*Ueåõë¿ÿþÏ?�Aýú ââÂáhÞ˜tÿþáÃׯ7…DÒÒnÞ¼víÉý44ÄÄ9’˜(öèÑ«—®nת ‰$-mïÞƒ«ªúõ›8±oßÐÐ \]CC'LДÒ�_|1lؼy—/76„T*Ëåj5AÄ‹=t¾hÉ{{·¼?�Í61qw÷ò26&ˆ#G**Äâêj±X“Æc¹ý;<ë쀮„É´·;vΜîݧLÙ°áÀgçÇû†i‚fE-=`“$IjCkšà�&ôê5eJ�ëÖ­_î\V–�Mqñ•+|þ˜1ë×;9uîÛÏX,sóž=û÷73ûñÇ' d²3ìì }}KK]]¹¼¦¦±Q­&==M¥‰ªª¤R>ßÂBOï凭I’Ͷ± Ÿ7oÆðð¦÷—ªÕ55—.}þùŒJeß¾ÚsJ©,,½`Áøñsç8�™)i®tº�݈ß¿gϦMï¼ã榩…¢ àÀ��?3fÞ¼£G U*&ÓÆfÔ¨_~9ztãÆ ll´/Hxs©ÕIuutôÉ“))çÏÈåE’¡«kcãàðÖ[Ç;9¹»7½bG¥‰ÊË÷í;p )éúõš’äñ8’¬­•Ë›·bµº¢">þîÝ+.]ª«S*ét’¤(’äp||ú÷ œ9Óߟ˭¬¼~ýúõ+JJÜÜ~üqâDssÍÏË*Ummnî²e»vUW�9{v����×âq¹¹÷î�?�k'À¿Ó+ öiÞböË/ññjummNN\ÜÅ‹qqr9A²dÉGùùñxAQµµ÷î:tó¦DBab2iÒîÝÇŽ}þ¹§§öW5±øÎ�%K† ™3çüy±øÆ�C‡}zþ|oï5k\\•WiÇÒrÊ”~¸r准 ³´TW§¨ææØØwÞññY¸ð³ÏÆŽ½uë·ßô3_Õ£èÏ’÷?'ÔÔ¬­##ø!>~Æ!CÌÌÔÕé@wýzŸÐÐÕ«fÌ8r$,ŒÇ /\øè£U«rs_{->þöíeˬ­ËË·l ÿè£ØØðð""tu_Ü÷4Euu¥¦;výzr²‰‰�φ 3gêëki„LVPwõêÏ?;¦©ùå—¯¼bf¦£C]]©©'NܸQXèââï¿eËðá|>‡#UU­_¿eKyyÏ« žž«ë Aß|ãí­®NW Ëdõõ……Ÿ~ºwo~~a¡»û�!..ƒÛÛ››_»V\œ’ÒÚjl¢£C’©©eennÞÞ�£šÿ~^yå•Wz›$‹Öó–^úk¹\.¿ó'ƒL&“ÉdÛ·oÛ¶m®»ª GߣçᥫöTY‹ËuvvvFï½lžbØG�b¶?ý‹Åç[YùúFFFE-Z´dÉÌ™C†(ÿìjnNI‰ŽNI¡oÕ×92,LùK¡’¦¦«kD„��šÚ¹s—/‹ÅIIÑÑ©©Bá¨QcÆtÏ·ú¨­ím¼6336{ìØ¨¨Çö=x/>>ööS§Î›"“]º”––•uñâñãÊ·­��“ÓèÑóçÏŸÿÎ;o¾ùh“_�dKËÃ÷|ÏÑÆî>.&SOÏÙ9<|É’•+?ú("¢÷!ÖŠêê‚‚[·ª«ï~¦µÕß_,¦(©´¦æôé�;÷í»ûÚææË—÷î=}Z9'¯šÚÃôÛƒo­}|¯ì�aŸªï b¶…Å”)ï¼¶k×[o;Öý “ihöùç_~YUõÕW_}!’$A0::žžQQ_~ùùçaawÞ@ª®îì<}ú¤IZZÿ½w¯ƒÃæÍ#Göv‹©DrëÖÖ­¯¿¾hQIÉûïŸ9³sçĉ}�£EQ"QAÁ¡CkÖ|ýõúõíí ‹EQ,–™Ypð²e_~9k–••òêÃçöÞ{ï¼÷á‡o¾9nÜÈdL¦@ôÁ›6ùûw$#‘:ôë¯û÷WW¯X1}úë¯ß¿§D¢ë׿ýö­·¾ú*9¹¶¶¼¼½½¹¹´tþü3þïÿ†ÿôÓß~ûðCe��H”‘ñå—sæ¬Z•� •r¹jj<žµõð᯼²aÃo¿M�êæ¦©É`DWWAÁþý?ý”žîî¾téG9;÷^A+efnÛ¶cGFFc£¶vEņ ¯½·aC÷µÑÏoíÚݻׯ6ŒÍîßq=‰sÊÜ|êÔ�?ž=;9yÕª¨¨ððÍ›uuÍÌ-,èJ¬Ç³KËÈÈÕ«gμvíã�gÌ߸QKËÈÈÎÎÒ²³“Áè®÷S}�óçÏœ©­½rå¼yáá{÷ ²bÅöí7öõÓžå{µû÷*C]ÝÄÄÁA_?==6öò匌ººææ¦&åÇw��R)E„–“ÉçÛÙ¹»ÛÛóùAµµ<ž••»»§§��rP‰¤²2%åÖ-ú7©4;ûÔ©²³»ÛPSS^.*ƒyy½ûî/¿üò ]KÈf?lm˜¶vHÈîÝ%%íí[¶ÄÄÛØô>̹H”›{âDn®�Íë¯ÏœéêJ_3‚Ã11 ûðÃwß569rÏž¬,‘h̘ѣµ´z^_³³úiժ˗G�Ú¸qËWWeÜo`0dÈØ±={YW×ÝÝßßÔT,ŽŽVuTSU�¢?KÞ“©£ãîèãÓýsA"©­ÍÏ/)a±VÖJ‹ÅyyÇ�§¥ ³gû­�—K,–­í”)³g [¶ÄÄäå‰Åƽ¸bÊdmm‰‰UUcÇNš¤§×] Îá8:úùÙÙlÛVUU^.“™™yyD[[RRU•†FHȤIÚÚʳ�Åb±Øì»ÿn¡kácbNž,+Ë̬®‰::är…¢µU$ÒÑ‹)Š ˜LmmcãI“ììnÝÚ»÷úõööQ£$’S§nÞäpÂÂ^{MGçé|Ätüø©Sÿþ;yò„ ô×&DD„…�:õï¿çÏGD„……„üûïùó/††�}áB\œò~¡ÀÀ‘#{¾àA´µçÍ›7�ûú»zï¥C=”´´´´´4ê%ÕÕURrìØÖ­?öËÖ­ÇŽ•”tu½¬½0°£úC*-.Þ¶mæÌI“¾úêúõÎN׋H¡�H:;»º ú{’ìèÈÊúᇙ3çÍû¿ÿ++“Ép¦? ª]‘D¢ÌÌM›ýϦM™™"QÏ×qÛ¶wߥŸûú븸ÆF…B¡hiIJúþû%K-zç�o¿ýçŸøøôôôô“'ׯ§—üê«Ë—››•çÃÝ{ùâ z;ÝÏŠÅ99¿ü²t)ýüÿ�žž——ÿÿ”•56J$$IQ …T*ù©´çVzêÏï¨Bá±cAAººAAÇŽ …w?+‘dg¯]ëî.„‡oÙrýºP(—+ßå��))_}åá¡­=fLLÌ�ëÊå·oïÙ3v¬‰É°a›7J¥÷o…Xœ“³aƒŸŸ‰IDÄþýÕÕʽ¨êþGñpKÞÿ¼IK[½Ú×WO�Éd³íí,8x°¼¼û(…“'ÇŽ54 ŒŽnnî^¯¹9::0ÐÐpìØ“'~ï�¢?ç†DRSóå—6$&¦¥I$÷>[W÷ý÷7^¹ßÞN’=Ÿ#ÉööŠŠµk7n¼v-+‹^W"©«ûî»�;:î·™¬¹y×®<{ö‡RRš›››¥R’ì«=RiMMnîüðÃõë—/_½š˜¸lÙ�ÕÕ­­w¶é~äò––üüŒŒüü–UϽmÛ¶mÛ¶íAÛ•Ëåò®:{ …B¡°©©©©©iãÆo¿ýöÛÂÂÂÂÂB\½áe¦ú_ÍÍ?þøã�%%¶¶¶¶÷ÿ·¾~ÅŠ+з/®GIÞ0ÜÿC�J+*z¯/»¿Ç;“/À‹Œ$›š22ÒÒ44¦N]½ÚÃc Ü¦>P�«¯£ ¯\ùüó 6lÈÌ”J9‹ÉÔÖvwŸ=ûóÏ-ê®÷„ç_[[qñ�ׯK$ííyyqq11ééb1AOŸ>t¨¾>“IQõõ--­­ A¨«™šp¹%%}ׯ3l¶º:]…×ÔtófFFNŽ­­¿¿‡‡µµ¶6—knîëëäÄáܸ‘›[_Ÿ’ríZa¡‰‰¯¯‘‘TZQ‘‘që––Ö¤I‹66nÙòøÆì{055w÷U«ŽûùçO?}ç�¹sÝܾþºµ•ÅÒÔÔ×·µuqáókjÔÕ­­µµ»ïô ¨öö¤¤¯¿þì³¼¼©Swï>tÈÞ¾ï D¹¼ªêðá·Þš2å·ßll6mº|yæLSS‹ ¤Ò[·6m7n„ÌËëê¢Ûãâòþû§N�9³rew¥àã¢ú54||¾þ:5µ¹yíÚòòƒ_}5(hÆŒ<8!á£�œ�»§iº³bm �ÍÉåòùææbñÉ“©©--¾¾AAúúšš!“¥¦––66š›ÛÛ[YѽFQZZî"Qrri©D2l˜‡‡ššXÜÐ ¶´ôœžˆ$åò–¹\MÍÕÕÁAGGO�ᨶ¶²²––¦¦»'2âpkë±cùü¸¸mÛÎ�ãrCC.42z<ƒ©Fõj¾{+ûFŽ :W_€þÒÓ{ï½÷ÞSSóðððhkÛ¿ÿ~©4;;;›¢d2™ŒËusssãñ"###µµ£¢¢¢Ðc/'üþx¼ÀÀmÛÒÒÒÒЇÉ41™>}ûöÝ»q\/òÑê뇆nÜxüxh(Îé]míÙ³;wÒ#gq¹zz¶¶þþÓ¦M™2}zh¨““òV#£aÃ&M HJÚ¹3)iÏžmÛÊ˽¼\\úŽÔÔllBBF�¶´ÌÏ��½qãŸþü“É45ýöÛ%KÜÝÕÕuuýü.\¶¬¹ùÀ�C‡.\ÈÈ8qbÏžÌL‚`³ù|›áÃgϦÇml|Ú}Â`hh88DEmÚôï¿QQ›6)'Éúúèè©Sýüd²‰]]•¡¾LVT´cÇDGÛÛ¯]›–6bDï¡ E‰Å»w¿ñÆ„ ë×ËdkÖœ?¿wïܹVVÊ`�Ëuu]¹26¶¤dåʧs¬³G55+«ÐÐiÓ,-ßygçÎÂB±ø£�œ�9[[]]‰¤®Žž3\O�Å’ËÛÚjjD">߯F_ÿEŸžƒÁPW÷÷Ÿ:ÕÛ»¾þĉ7V­úþ{zª7C[ÛÂÂÆféÒ©SÍÌ”ã2ZZÁÁ&ØÚfg:téÒÛoŸ?ÏåkjJ$=cP.W_úôQ£44~øaÛ¶sçN�ÒÐPS3667çñz›³œÉÔÐðññöæóÿúK*55;öQFï~ô ¼ô¨|täGMG{ô2ô×ô}ô­»}�âªÓÔ ¥ÿEoÀ½ö¼„44 úàƒþ �Ïdêéùú.X°~½¯ï‚ª¬Á`hi¹ºFEý÷¿[¶ô^]Àdjk;;�·xñ§ŸŽ×÷–tuûÛÚÇ�$ ’d2éYsåòÆÆääŸ~ã�?¼}{Þ¼C‡&O¦§ô!ÉÆÆ3g>ùdãÆ–– Nžœ5ËÒòÞ±�Ц¦øøõë##çÎ�‰ñòÚºõòåœ2ÅÂây�¿$’‚‚cÇΞíì5jÂooK,.(ˆŽÞ¹³°ÐÆ†Ž„ ‚ Øl—I“¼½kk¿ýöĉ²²ÿüçí·--ËËOžüûïúzŸÕ«]\ž\=æã¢¦fbòÉ'«V™˜ôý^ÐÔ44œ2eÖ¬  )S¼Euu33‡�>zï=‡û-ÇbYZúûúã�þþªœŸÉ�7nH$..#GZX˜˜<êˆâýÕWeß½Õ|t_BBRRJ ½î°a~~C†¼¤`€'ì‰t­P…™™BaÏÏ¢¤Ò²²“'wí:y²¬Œ�ùÙz–íQ(ššRR:x0)éaç.¼“LÖÐ�‘qêÔ�»wïÝ»ÿáà 55"QUU\ÜÑ£qqUU�>c_¯ì‹N&{œ½ÏŸŽŽË—ß~{èP_ßÅ‹/] /]Z¼Ø××ÕuذåË;;ï\öÍ7�œŒŒLL,,,-?ù¤ `âÄC‡bcׯ÷óãñ ‚‹³²~øá£�.]:ô«¯V¬ðöîýþŽŽ„„Ï?ÿâ‹+Wjk+*^ºÔß߯Æüÿsq™?ÿܹÖÖÇyý9Þ¾QTGGVÖÖ­³f¹¹™˜¦¦&&VV¡¡»vY[oذÿ;ï89Ñ�%ƒ¡§¾iÓ7ß89íÜ9z´¹¹@`n>jÔÎ�..6|÷]XØã˜J‚ ¤Òºº²²“'[Z‚I“œ�ÕÕŸfÏÞ[ÍGÇ|t½Þ½Õ|ôZtäGÇ|$I’$‰×àIx¢•}Ý‘—ëææä¤«û´?kzþ1\®¦¦ššººBÁá<ú,ƒ2Y]ÝõëÙÙRéàÁÓ§Ï›çæFÿJajinŽW¶o޹ypðãê%xþðx£GoÝš’’—§Ê²AA;w54Ü9 oﯿÎË«¯¿ÿr::S¦ÄÅ‘ä£�_§úQôçxïw„C†¬]{î\AÁÚµ^šÃ±¶ž={ÇŽôôÙ³qÆ=9\®©©«ë¦Mï¿ÿðÛ`±tu�œÖÕ}¸õé˜�þº¯ÛrUY¯'öÉåµµ×®%'WWK¥mm��‡íÛÇåjk»¸„‡‡…I¶µ%&ž:•–VW'ŠD]]l¶™™¿DDp°‹‹Ž ‘¤HTQ‘–véÒµkEE--]]r9ƒ¡£ãâ2ztX˜ŸŸ…Åý?Å’Ë››sr.^<>#£¶V*%KKËÖvÔ¨ñãGŒ°²RW§—Sµ= E[[aabâ… ))åå��$ÉdjiY[:b„£#ŸO¬\Vvòä¾} l¶……¶v[[cck«HDQúú^^¡¡ááÞÞÆÆ\.“Éáhjª©q¹ —«<Ž·¹·#–Ëkk““ÊË%’¶¶ššcÇöïÏɱ´ ‰Œ �JÏž=p 'ÇÃcΜ‰ml‚nab¢²…MMb±¶¶—WX˜ŸE¥¥ÅÆö¶w33¡°¯WvÂ__CÃÞG y¼ýF¿jç†r›W¯r¹ÖÖÚÚBa}}k«X,•r8ææ=__zÉÞzéÁëv·'%%66!¡  µ•¢¸\.—ʼnÚÛy¼��ùó'N´±Áô,ð¢¢gÏþî»�7n|ô­EFN›6mšƒƒƒÃýop€þxba›mbâï?l˜™YqqNއGTTwÌ!•–•ÑKihØÙóxÕÐpíZtôÙ³ AA&&,–HTR’��‘A’ÞÞ“'Ïžmjª¦&—74¤¤=zñbf¦�ÝŒÆöUY&“ÕÖ¦¥edtvzzFFÎŸïæ¦¥%“µ¶66ŠÅ÷ÖÑ=¨=LfGGaa|üµkíínn“'Ï�ëâÂãutܺuæÌñãññ……¦¦&¸¸ðxÝý ¯ïé9vlx¸©©ššDR\|þü‘#‰‰yyööS§zyé訫ÛÙEDŒÿ°mîoŸß�ÅÒ×÷ò ;ÖÄD]�Á�Énß>wîàÁ¾ö~¿½ô…$¿1ªžÝíÐÒrp3f̘Þ_ßûÃÖe±D¢ÒÒË—¯\©«³¶7nÎ//}}6[,.*ЉùûïGû<àùñá‡}ôÑGô×EEEEEE·Ä|OÂ3œ ƒÉäóÍÍMMyƒÍær™Ì®®¶6ºþ‰Ëíß޾¾­­®nssffnnCƒ™™··‘—KQR©PxûvM “iffmM�÷Ö¹\(¬¨¨¯g±ÌÌ 2555åpjk�½}[$"I‚èOtÅfëéY[ëê¶·VT´·ÛÛ;;kiut”—´·ëêÚÙ)+º”Ï*ry÷€´­­ÅÅyy††nnúúlö÷ùÉ…}ªì½¿¯ì“é7UÏ�§ôVbë蘛óxmm%%ÕÕ"‘½½��††L&65ÑýOGzzzzz:ú^NO4ìãp//ëܹÇ÷îÍÎÖ×ww;–ž C =RÚèÑ­­ññqq‡íÝ+•RE‘$—khèájjjmÝ÷(J*­¯¿~=6öÒ¥ ärƒÉTW75õñ 51áp¢ïʯ{1™<ž“S`àСuu±±11ûö¥¤�$“©©ie5thh¨“�×3ô’ËÓÒNœ8x0%¥«K*¥(>ßÁaĈÐP;;M;ª¿Tió“£ÚÞ{eûš ãÉôÛ£ž�“©£ãâ2r¤»ûíÛ.:´wïÕ«\®¦¦žžŽŽTÊ`°X�>×2ÜŸ�����ú^f úfÈþ®F^Š_§úvï¼®˜ƒuà÷EÉå2I²X‹Å`„TZW—œ“˜¨®6uêàÁººý¦’’’’’;;;;;;ô<[/ó 5} þÖ€—þš€{=Jòö\�Ùð"£(‰¤¢âÒ¥cÇjk z|A55�ÀÓ3(hÈÄ|OB¢B[€G�°ï árml&N\°ÀÆ}ñ²ôƒ¡¡ag6{6>‹€ÇoûöíÛ·oWeÉE‹-Z´=ðrBØð\£c¾ÁƒCBfÌ éЉþŠÁ ˆî!šèåù¼œö<§î�ùJJÒÒÎ�ëkykë!CÂÂ<<§OGäðrBØð‹³²¾ÿþ½÷~ýµ  ï¥œœ–.ݼùƒ "ˆžËw?®¡�¾„§­««¶ö«¯6lHM½u‹Ç³¶þä“wÞñòruUSë~vóæ’’ˆˆ÷Þ>ÜLJ~üÙR(„‚‚ììúz�ÀÓÓÉIW—ÅzÐ:}Å|¯¼òÊ+¯¼ÒsIŠêþïï¿ÿþûï¿--½½ÃÂÜÝG�FäðòÁ„/!55[ÛÈÈ·ß^²dÉ’… ÇŽ51Q>cb2vìÂ…K–,YòöÛ‘‘¶¶tPÒsùž�<+\.� ¯_W÷÷ß™™�� ÅÀ96Ub>ú–ÝãÇO�ú÷_’T(Èÿ™1cæÌ™3++32Ο'IŠ"I—Ñ£§MS}¼?xÑ!ìx 1™|¾��—×àÁƒ{yÙÛóùÊgø|{{åãvv|>=›|WWiéÑ£[·þöÛo¿mÝzôhiiWA(+ÿχþøã¦MkÖ,Y²xñòå_}ðàÅ‹'Nüúë'Ÿ,]ºxñ²e_SZ*‘t�6F’gÎüúëÚµï¾Koå­·V®üæ›ýûÓÒššäò{÷òý÷YYbñùLVQqèÐâž¾ff�±±±±¹¹¯ïâŇUVÊd·$öølÎa55Ÿ�++‰$)©  7W"é{i’‹›šNœ8t(>~Ù²M›ÎŸç�ï¿?~õêC‡ŠŠòòÄbú¬¤¨¦¦ôô„„·ß>x°®N$¢×mk«¨X·îë¯/]:q¢©‰$ ‚ äò[·Ž½pá³ÏRRD¢'5ÞóÑ!IÒÿ*�Ž$ÿŸ\®PL›6mÚ´iUUYY±±ôÒô’EEEEEE¸ lûà1!IÇÔÔÌLS“ ººÊËãâNŸ®¨ÐÑ¡‘JËË/^¼p¡¬L*%‚ ¨¶¶ë×wïþùçcDz²ÚÛ'L˜3gîÜI“šš._Þ½ûàÁ›7;;»£ÁGEQBá… }´jUnîk¯ÅÇß¾]WWYyéÒ«¯Þ¼¹jÕGÅÆ …ÊÈGÕ%±Çg‰ÃlmgÎ47olÜßÿòåÄĤ$åcÝ7ùÀËa<L¦®®ŸßÂ…Ë–M�:dˆ……\ž•uâÄž=;vìÜ�œÜÒ¢£#hi=¾[$9[[]]‰¤®®½]9¢™\ÞÖVS#ñù66úútô¨ú’Øãó‚ÅÒ×·²š5ËÝ�Í&ˆžAƒ¡®îï?uª·÷Сõõ7n¬Zõý÷çνýö÷ß_ºôçŸuu¦¦K—N�jf¦«Û{jiYXëëK¥\®ŸŸ««¦&›ML¦@àêjn®£#s¹ƒ[Zr¹O²~Ž¢ÂÚzÈ�°°þùçŸþQVÂ*ëéÀO êãCG}çÏŸ?¦¶¶®.®5/ꡤ¥¥¥¥¥Q@QE£àyð2_‘ô;*I67Ÿ<9c†µõ¨Q¿üRT$•RTWWaáÏ?�im=sæ©SÍÍʪ)U—¤—‰jj òòÊÊZ[åò�¸Ççéu|Éå--ùùùù--=_¯ûÙ¶mÛ¶mÛ®^ÍÏolLHÈËklü믿þúë/‘H$êìlkkkkkknniiiihhhhh¨««««­¥—‰‰9yòÌ™“'ÓÓ««éíâ ð|Â_p¯Gùiˆ1ûà%Á`èé…‡oÚôÍ7G�N›6z´¹ùºuR)‹ea1uê�?=¦§§¬ÓR}I‚‰ÒÓ׬  ÊÍýþûÛ·¯^}í5&s íž¾E‹-Z´hûöíÛ·oww=zÚ4sóÁƒCCcbbbŽïk-ºš�Å254¨ª*-íĉ�����ô*ÀË€Ag~ý]-=====ÝÇÇÇÇÇ�PRRRRRbggggg‡Þ€gëe¾"áwT¼Žù¹ºŽ5m]oÙÑ!‘ô=ß.b>€ þš€{=ÊOCTö<×zVù©²€—Â>€ùÝIÄ|/3„}/ ypLtÀÀ€Ê>Pè­ÑðrBØæoÀm¼Â>€aÀ�°`€x¤ :ZZZZZZЉ4\àù�+À‹?»áqy¤°�¢8]]t"ADK ®Šð¼À àÅ‚ŸÝðøà6^€aÀ�°`€@Ø0@°Ñ/›ÒÒüüë×ÑÐ[[gç!CÐ/"„}/)ôô”žžžžžŽ~xq=Ѱ¯³3>þÃ'LX¹2+‹~ÄÅåÃwïÞº5*ÊÔ”Å"‚ ¨¶¶k×Ö¯Ÿ2åÓOãâ::‚ ||>ÿüÔ©ß>œ zn¡ûq-­¾ör÷22Y}}ZÚ?ÿüò˾}11ññ¹¹uu"Aƒ¡¡ahhgçääë;}ú;ï¬X1s¦µµLvw›ïto(J*­©IH8thûö;NŸ¾z57·±±«‹ ‚ÍÖÕµ°pw1bܸ7ÞxÿýéÓíí33W­êÏöé£(*Úµëµ×fÏ>p ºš~ÌÚzÁ‚Ý»ÿúkÎkk§ï>×Ò úï÷ìùöÛÑ£ ˜ÌÇÝc\.ÞFχ§M*-+;}úÂeÌG+/?{öÂ…òrºÝ}ëì¼tióæ_~¹|¹¥…$�À£‘J++�{ûíI“ÆŒyë­«W;;Ñ'Ð7uõÔÔK—¤ÒeË.ìè òóko1bð`©tñâyó¸Üóç�C/½Ìžúm¼õõgÎìÛ—”4gÎ_ÔÖ9²{w~>E=þ=‰Å·n�8qñbK A›=dÈ{ï}ÿýš5ÁÁzz ½ Ivv––^½š’ÂáЗkkñÆï¾KWÀ=zåJK AØÚFD¼úê”)VVt-Eµµ¥§ÿþûO?¥§‹Å¡£¶iÓöí«Wêé1™Ê=,Y’“S[«©Édr¹ææªm_I")-=sæßëë ‚ 45ml,,¤Ò²²Û·kjÎ�;uª¼|þüwßup¸_…]GG\Ü—_nÛ6hЪUƒ=΀‡E’BaRÒwß}ñEttK ‡Ãbñùèè “¹sçÏ?×ÕmÞ¼yó½ÏŠÅIIIIô¿<ÞŒII,ÖgŸ}û-úí¥;OžÞ®ŒŒÜÝ�� B"ILüã�³gss¯\9pàĉº:‚064ÈÀàñî�Åâñ x<ú;¹üúõuë&L˜;wÑ¢åË¿ýö—_ví:>+K(451"8ØÂ¢¿·£vu_¾œ‘Aßâjm9þðáÝ1Ÿò�Èç{xDEÍŸjnÎîw¸*‘äçŸ9×Ð@!DD,_þþû£FÑ·ÜÖÕ�={ölq±²RònŽŽ“'�M÷lkëÅ‹ß}·eKZZkkïáê“î1P¢(‰¤°pÏžõëýµ£cÖ¬uë.´·Ç{ z§¡‘ž~ùrKKï1ß½::¢£££Ù옘Ð{/›§öYXŒ3wî°aAîÚõé§ë×'%I$\®¯ïܹãÇ›™=Þý©«»¹Í�»|ùĉÝ[–É ’’bc££·nýî»÷ߟ:uöì+víÊÏïìì_}¡BÑÞÞØ¨¼ýX_ßÊJOïq׺‰D·n)+ÿB  uw÷ñ7nøpmm‚ ˆ††øøS§òòz�ûøüÁƒ-úä“à`zé––“'¿ÿ~Û¶””öö§ßc@_;êëÏ�ûá‡õë||þûßO? 11A¥,ô­­mÏž={ú»V{û¾}ûö¡÷^6O1ìc2õõ‡ [° $DW— äò–‰„ tu#"fÍ6L_ŸÙg[ºo!%É{G�#I…Bù(ƒ¡\– ‚Å22 ^»vÇŽcÇöïÿüówß]¸püøÀ@__kk:ü"‚èìÌÈØºõ—_bcëêî?¦ÝÝÇÃãéë+§Ñ «ª„B¹üqöXGGnîñãññÊ`N,¾~}çÎuë>ýôï¿KJ”#æ …ññG�æåц÷¶ROoĈŋW¬ ÒÔ$‚hnŽ�ÿ矒’Þ–~²=Riaá�k×þù§šÚâÅß~;{¶³³¦fÏëÀóK"ÉÏÿõ×°0--'88&¦µ}ð´tuee)'â´µ-...¾ÿ¿ô’Ri~~~>zïeó”'èÐÒòôœ1#*jð`}}mmCC�¨¨yóÜÜ´´zÿc—ÅâóML”1SiiRRnnÏ›PI²­-7÷òåŠ ú{mmSS>ŸžçW"¹uëÈ‘¿þº|¹¾^CÃÑqøðÈÈ%KÞ{oݺß~Û¾ýÀ�9RCC¹%‰¤³S*íOt¥¦fk;|¸‡‡šݶ#GHNnm½s$ÙÙYPó×_/VW«RT[[FÆÉ“IIÝS—´·ç榥]½zåJzzÏ[wÛÛããO�ºy³·j=‚ cÖÑ£W®\ºÔÏ�nmïžt�€LÖÐP\\Wר˜’²yóüùsçΘ1~üìÙóç¯]›•ÕÑ‘ž¾qãë¯/^|ð`Y™TŠþ€ç…BQ_òäòå3flÞ\_¯¦Æf£OžWOýW5.×ÚzΜ?þ8xpΜîG;;•qÝ�ÔÕ]\&N?ÞÒòìÙƒ++ÏŸ_µjüø¬,{{''}}‚hl,)).®­¥ÿ(67Ÿ0aÆ uuz›õõIIÛ¶­\ùûï»vYXxxøû{{ÛÙ êê"QIÉ•+ b1A‡ãã3yr` ™›-•––þûïÞ½Ç�WTtt”—w·«´ôß÷쉉±°xã z ==ÿ%K–,qwOKÛ¼ùæÍ3gÞÿõ׋Ђ‚F�òð02’Ë««ÓÓ®]+)áóçÌ9p`úô‘#UÙþäÉ::ÑÑiit¤ghøÊ+?ÿ¼ÿ8;+ãºÎNzL½�?Ž‹ëì¼víØ±ŒŒövW×Þû�Å26 yÿý÷Þû÷ßW_ýöÛœœ{ƒG…âaz o"Õii þùçGŽ\¼xçã��ññ~8kÖ_EFþßÿýþûðáʪa€g‹¢:;326nœ1ツÂU«¢£ÿûß?ÿ üõWô ÀÓ¤¦6hРA"Q\\\\i©½½½½*kq¹ÎÎÎÎè½— óyo ††³óþ³mÛ7ß¼ùfD„‹‹±1‹U__\œ““—W\\[Ë`ÁÁo¾ùóÏ›6½ñ†›õ—ko?nܬYÁÁnn­­¹¹—/Ÿ8qüxtô?ÿ\¸��-“ÙÙ >oÞÇÿñÇ?LœHW2YEElìÞ½ûöEGw�–GÑÜœœü÷ßgÎܾ­¼�V]~óÍÎ�ß|óÊ+#G:9q8¥¥—.�9sôè?ÿœ?Ÿ’ÒÔdh8dÈ”)Ó¦ nlÌᨲý®®––ë×cbRRèSWwذÐPkë;«ò45�œÂ† ¡•HRSÎÌìkò ‚ 6ÛÌlÊ”÷ßóM7·Þ*)®Ç``’Ë++üÏfÏ>u*,lÿþ�;çε¶ÆdBÏ‚¶ö¼yóæ=�µàEÇ (Šêÿ4 éééééé¶¶ÎÎC† „ÂúúÒR]]�Àֽϖ*W¤ÒÒüüë×}|||||z{^"¹qãóÏgÏ®¨X³&6ö�?¦L11a±Z[cb‚ƒml6o~ï½²²¸¸)SttÐã ~ÏxúTÿk‚Áر㧟„Â-[¶l¹ÿ’<^ddd$‹µnݦMèáу~[»TfÀ]¤ÒªªÌÌŠŠššää¥K}|¬¬–.%‚ ÉŽŽêj¡°­íúuOÏ  Õ«Ïœ9{öã�ÝÝï7.0<õŸÿ¼û®±±‡‡‡G[Ûþýû÷K¥ÙÙÙÙ%“Éd\®›››óÉdãÇÏœ‰{9!쀻ðùãÆ;VSÓÜ|çã=+û²³QÙð,H$Æ�ÃåÒÿÞýl÷Ðcð²zîÇìÕ¼$aŸLV^}òduõ‹�qËd--7ož>}úttô¾}§N�9“�ÐØxïü¹ÏS‹«ªRSÏž½t©¾^õ~¸µžÿãzÛç°B!–•åæ´·+ÏïûåE{ÿ d::S¦Äŵ´ ¦ày…ÛxŸC2Y]Ý­[yyR©³ó¸qsçÚÛkiõ6{îó„Ã17÷ó;ÖÜüɯõü׋Ò$ÙÚZ^ž›[PÀå:::9ik³Xxÿ¼ÈžhØG×ã=š˜Èåš›óùmm��­­]]2‡#x{�;t¨½½Ž“I/“�íè8eÊĉffN÷z>Nô6X,�@K«­­±±­�˵²<ØÄD")*ª¨¨«koçr#"‚‚66fÿï(I²­­¬,9ùøñììææÖV‰„¢ œ�G� uq10PÞéNQbqeeNNRÒµk••mm‰BÁ`èêÚØøû‡†zx˜˜¨«3ÝGÇf›šòx--‰––‹KDÄðáNN|>óžºI’ìè(*ÊÈHHHI©®‰H’ÉÔÔ´²òô9rÄ++�¢32ª«»ºÚÛΞ=p ¨ÈÌ,8x̘aÃÌÌØì¾û¹}¢zŸs8rykk~~\\RÒÍ›µµr9A°X––£F�éëki©¦&—÷¶5’‰ÊËsr’’’“+*Z[e2Šb045MM}|ÆŽõñ15ml¼|ùεH²££ àìÙË—³³›šH’É$Š"MM/¯1c||zž3ªœ]ñàö÷¦ö}>žýöÞo%——߸‘��˜X^ÞÞN\.—ËdŠDr9�çèØÝ>ÕÎÕÞŽL.olÌÈHJjh�JÛÛ…Â'öïçpxÆGiƒ²%ê%å»Gµö<ø]Æb)_Ù#GØlSSm펎––ÖV‰„$õõG� su54ärŸ÷˜àNO¡²�¢ ›  ¡CGŒàñ¢¹ùÚµóçãã33››­­ ¢ál]]�ñã‡WSËË»páÔ©[·"4422"ÂÛ;//6öÂ…œœæfcc� {->ßÙ9<< @ PSëꪨ¸p!6655/ÏÂ"<ÜË‹Çc0D¢ÊÊÔÔŒ Šòð˜4iÜ8�@MM¡hjJM��½z5+ËÒrüx]]å6Y,z›Ã‡ ÷ 5(ª££¼<9ùÚµÎN‡I“&LppàñÄ⢢3gââ’“ ÇŒqq14ôö02ª¨ÈÎvtœ4©;†{2}ò`2YmíÍ›7ovv:9�;ož��¦¦LÖÞÞÜ,3™}oggeerò•+MMffãÇ�7fŒ¾>“)•6774´·÷µ&“©¡amî訡Áf3ôí¦±±/¦§76Z[‡„(Û¯ÊÙEQÓþG?«n¿%ݾ}íZbbS“…EDÄ„ ®®úú,VWWqq\Üñã·nõ\Rµsµ·z=6»ï3M&+/§#VuukëÀ@?¿áÃ{?FëQÚп³Kµö¨ö.ÓÔ¤·É`°Ù|¾‹ ýþUSëê*/?þÂ…ÔÔ¼<+«±c½¼x<&F6€ÈSûX,mm�@  ëi‚ x:‹Ëe0¤ÒŽŽ®.’$§ul�Þ†‡ë¥Çõ.»óš —··——çç‹Dzz=¯ ‘­­³ó�!è€�ç9ùc–Í\]== .^Àó‡AÿYÞßÕè›>ð™0¼˜úš�úá …õõ¥¥ºº�­-úž-\‘^,øÙ ÷*-ÍÏ¿~ÝÇÇÇÇǧ¿ëbèy€cRÀKˆÃ±¶ œ1ãanÉx~¡²`€@Ø0@ ì ö ûˆGš�—Á�É„Bt" WEx~àŠðbÁÏnx\)ìÓÓÓÓÓÓC'´´´´´´àªÏ\‘^,øÙ �nã ö û„}Â>€aÀÁ~:»‘Jkjöîýùç;ŽMHÈÉ©­‰‚ 45MM==G�ŠŒüÏ–-›7oÄ.—^§¤dûöY³¢¢¶o¯¬$“W_Ý¿ÿÈ‘åË��¹\¹¼²rïÞ9s"#ú©¤„ ÂËkíÚ3gvì˜4ÉȈɉÒÒ>ýt„·Þ:s¦½�É2ä‹/Μùý÷qãÔÔ®\Y¼8(èƒÒÓéÖñxÁÁ6DGûmh¨¡!“IññÝËøø|ÿý¥KÛ¶DÏÇûff¶`Á�ÑÑ ÔÔ,[Ö÷òêêÆÆƒ�ÿÖ[ÿýïŠ'ÚÚª«38-àÉJOOO¿ÿo3ð°||||||Ðð¬<ñ°O.¯®Ž‰ùðÃÙ³¿ø"1±³“ ôô<=CC§Msuåó[[ssãâbcÏžýõ×wßML¼qã³ÏØ´iòd33.×ÌlØ0??}ýíÛ++››kkÓÓoÝ IÒÙÙÐP(ÌÎŽ�/-íÞSAA||a¡H4i’‘—[S“žž“ÓÞN?ggäé©«Ëd„X|w;:ââ¾þúûïã㇠ùê«à`}}ÕŽM_ô訨ñã­¬èxR‰Å24ôõ50`±¢¦¦·å9©´¾>#ãÔ©sç²²ŽY·nåJCC7·¿þZ´ÈÞþέÀÀÑýAÂ�C‡®[óË/&L&AI …ññ«VMžüÁqq®®Ê�x¼Ç߆I“˜3çĉ»·ÿg_$ÙØxúôâÅQQGŽL�zèÐöí&Ðãtuݼ¹qã”) ¤¤Ìš}ðàë¯[[³Ù!—WTìÙóê«o½U\¼retôºuC†hj>ëóäY½ �²÷ÿüyùàO€Çëé|œVZÚÚZZšœ\[›””˜XS“�Àd“9b„™ÙÈ‘C‡ûûÛÚêèØÚây9=Ѱ�¢ÚÚ®]Û²å‡è˜ÏÐpêÔŸ>xpÍš€:x#’|ûí¤¤¯¿ž5kÙ²cǾÿ~Ë–kׂ‚¾ùfÄ>ßÖ6(ÈËKCãÈ‘K—ÄâÒÒääÒR‰dÊ �üüØØ¼<ŠêÞ›D’““�PVÖÕ5l˜‰É­[×®UVÒϘšîë+°X}·µ­íüù/¿üñÇqã^·nÈUŽÏÖvÊ”¥Kß{¯ï?f::ú^ž¢š›=¾ÒÒ˜˜_Ý|—_�‰)-•J{_‡¢‚$¥Ò––¼¼ØØs电‰¦¦nnʘ``ãríì¦OŸ7ÏÈèìÙ;®^ IR,ÎÍ=xðäIoΜiÓììz^ èj©‘#Ç�ÿàƒÕ«/ž5kêÔI“fÎ\³æâÅæfU®V�F*½}ûܹï¾[¼xÚ´‰Ç�7nìØˆˆ3ÞçÎÌ̶6e ”íœ0áÃ׬Y´hæÌÉ“#"BBBB¦M[½úØ±Š å•±ï#:{6=}ÇŽW^ ÿðÃË—[[»?Ò‘Ëoß>pàÕWÇ�ûðÃøx¡°ç‡=wb³��}}‡ ãñòò._.-•H‚$[[srRRØìÉ“.4¨±ñòå‚z8 ±¸°0>þömsó�77�Láñ5½ÍÿýöÛ7ߌŒœ0aüøqãÂÃÇŒ �thß¾»÷ññö~õÕ)Sî.˜MOÿàƒà`_ß;år]],øôÓ¹sïüó.&ÓÈ(8xáBÿ¿ÿþñÇ£G33;;³³32^¸ðµ×ŒŒ~ýõ·ßî]‹Ëµ¶ŽŒüúë ¾ùÆÀ ·«©ê:;ÓÓ?ûlúôqã45{þ‚@’"QCCg§‹Ëœ9ARiQÑ®]kÖìÛ§©ùþû»v?îíÍç3��YY›7¿úêúõ»v�ñ×_Ë—;;Ó[ (&ÓÈ("âóÏ¿üò«¯ÌÌ(*/ïçŸçÌY±bçΛ7ÇŽýüsoïûIÖ×ËåööóæíÞ[[;bÄ´i¦¦ryAÁ?ÿüóO[Û˜1_|áﯫ{¿ñM¹\KË‘#½¼Š RSkjd2[ÛÚÚ„„–—™3ƒ‚.(8~ ‡à`''¡p÷îììÚZ?¿ÂÂòrsó‘#íí��mlÔÕLK«ªªªb2¯_'IoïÕ«ÍÌ8z¿'qÔêêææ~~��;w®[·n]mm@€§§‹‹››·w@€ŸŸ““–Vo?úó*8;«Þ{IAÁÙ³7oš˜L›öÉ'vvÊ ¢8uuçrySÓµk;w~óÍáɉ¹¹UUB¡T*‘47 ….."Qoõyý;öÜÏ•+ÕÕ—/Ó_,x{_¿^_¯pãÆ‘#¿ûŽÉd0˜Ì5k®^]½š~ïÞÓ§ÛÚ²².\ 3j}}?¿aÃÌÌtuÍÍÇŒque2ãâ®_¿qãüù²2z]këÀÀÁƒéJ�ûa2 CB>üpåÊaÃ’’Ö­KNîêzüýA�Ù÷ñÇþþS§.\8gÎï¿ܾ½wï§ŸþñGHÈ�Ë—{zjjb>^€—›ma1qâ’%ãǧ¤$&�onÎfDïaß³@Q$©P„ž^hè† G�~ýõС}O\qçÇݘL&“Á ‡1Pƒ¡­=dȼyãÇ“äûïïÙsþ<Ÿ_[›™éäôÚk+WÒ}ôàmðx®®!!UU.\¾|ùr[›–V@À¼y<‡£«ëë;xpW×–-‰‰W¯’dk«‹ËÂ…¶¶ôH‰2Ù“9j6ÛÂbæÌ_~9rÄß?3óÚµ´´7nÞLKÛ»wÅŠM›vì˜5kçÎmÛæÏ§µxØW� Tí=Š¢(¹œ¢ ‡nk_ººnÞüå—÷ßß²¥¾þÕW7múå—uëÜÝ…âÊ•îhøQϸºšÏÜ\KËÜüóÏýýׯ?¾¢âܹììÆÆ7\]õõÝÜþø#'gÛ¶ÖÖ®.e9Õ½·úÂKq¶<É�3|¾¿ÿòå+VŒ¡¥E G�.[6cÆÂ…üõ×7~ýõÇ¿ñƌ˖=ÚØH<ÞÈ‘+V,[6t(ŸßóϺBÃÃãÎj �AƒmmÕÕ™L/¯1cîÜ¿¡¡¿¿¿¿©©jU0,–±ñ¸q«W¿÷ž·7[¥´÷1û6oþùç}û’“›šzV°ô¤®îäôê«ß|³b…—]ÁH~Û·gg‹Dªþ9 />&SG' à£�~úiåÊ»¯|ýAQ]]MMåå55���o 4ÇÌløð‘#y¼«WÿýàÁœœövåÖåòÖÖ‚‚Ë—/]ºÿˆm‡Ëup˜>ý•WŒ�/\X»öÇoß=zÁ‚€}}UUa2õõ Ô×ÏÌüóÏÇE"ŸAƒèÔÔ¬­GŒpp¨¨Ø³g×®âb+«à`''åpOê¨Åâ’’‹/]*/×Òòð ™6mÁ‚åËW¯^½zΓ۷³³ëë{V >ü« Zï©©YYùû›šVU�;—–ÖØHßÒÖVR’›ÛÐг% EggCCW—�Á�!®®�†I67çåeeÕÕõÖæg}þÀãÐÚÌ`0jj¡¡{öTUõü½†$cb¦Mãó #88&¦µõñî_(¾¯�$^”x–{º-8ç<<)¦¦Ã‡WUuvVUýýwAÁÁƒS§ÚÛGF®Zåë»zuuuggUUttQÑ?ÿô\‹žŸ½÷²yâù.›mf6eÊwßýõ×�?¾õVx¸»;—[Ttá‘#‡9ráBQ‘šš‡Gxø’%›7ÿõׯ�“'›™Ýµ1ÚÚnnÁÁŽŽ=Îtq vv¦kX,CCŸQ£,,º—ÐÔþxöl¹ü矣¢ÂÃÇ�Ÿ>ý�wþü37÷ΟCššƒ-^üúë&&Λ>nÜäɳgüñáÕ•}G}Ïöü�ÇECÃÀÀÙ¹°ð»ï~ýU95AÈd%%ûö}÷Ý�¶¶Ofk]ÝqãöïÏË«ªÊËÛ³'(ˆžŒíåÃdL�jeUPpìXff{»ò5¨¬Œ‹KH‹õôΞ½v�Žìéñ8¯];}º®ÎÓ32ÒÓSK çñ‹ç<<Ȱa&&t¥Þ_åçïßwûöÅ‹ E)ŸžœüÙgô’=«ùüý1ïK‰z(iiiiiiPEQÅÅÅÅÅÅè‡ÇG.¯©9zôÍ7#"ÞyçÌ™†…}‚ÞU©rEzÐïrBá±cAAºº£FmÝúÓOáávvÓ§=ÚРPtt$&.[6xðøñ?ÿ¼uë¨QººAAÇŽ …ÝkijZXL™äáaoomíê:kVttmíÃ�‰Ê–tï…>Ëãã¿ùfÚ4OOssccSSSSssOÏiÓ¾ù&>¾±Q.ïÞ†DRT´ÿÒ¥£F98XZZX˜™ ††66Ç/_~æL}½²mbqQÑÁƒË–99YYYX˜™™˜˜šº¸LœøÕWW®45)—£[¥¡af6a”)..VV††::cÆ|ñER’PH’=—ÔÔ´¶ŽŠš7oÌ;;33}}=='§¨¨?þ((�Hîßbñõë~èèhm½dIRRg'E)55{ö„…ùù­X±iÓ¤IŽŽ3fœ9C÷NkëÙ³3f˜›üñGe¥²úw\šš––‘‘³fººÚؘšöÞZU{µ·WP•ötuåæ~ù¥§§±qddLLc£²G)J*-*úñG??3³ÈÈcÇ”ÏÈdÕÕÇ�¿ÿþˆÆÆ––ööÎζ¶jjNϽK¥ÅÅ¿ÿ>~¼••¾¾¡¡±±©©‰‰@`nîíýê«þÙ}ŒÛòGuï~ŸÄ; ž”‡KKTÿkâÚµššääÐÐǃƒ{þrøp` ý/ýHrrMMR^‘—í\¢áÎmxîtu:´gOUU@À‚£Gb¤ô<,–™Ydä'Ÿ„†fe}ùå\¹røð·ß^¹2bÄš5S§š™)§T»“šš“Ó¢Eý•ž^TTV–›{ðàô鯯�ëL$ÉÚÚÇ_}öìC‡ÜÜ~û-;»ººººººª*;û·ß\]ÿþ{öì×_?|¸¶–®uko¿|yÕª÷ß¿z52òô颢Êʪªºº††ÊÊ„„Ÿ~š6ÍÚš¾Ë¢«+;{ÆW^Ù´‰Ç[¿>)©¤¤²²ªªº:/oÇ'§={þóŸ ²²”õ�A †¦¦‡Çûï?ž�]^^[››ûÝwšš¿þúöÛþY\ܳò•¢ÂÈhæÌ;RRŠŠªª*+ÏŸ�ŒÌÈX»ö«¯RR:;ïw¼êꎎ'®P\¾[^.•¶·ge�>ÝÐ0dÈÔ©S§Îœii™›{êT~¾X,ççŸ>�•e`0aˆ‹õ0ÇEQL¦™Ùœ9»v¥§—”TW÷ÖZU{µ·k”jíár�œ,X¹ÒÝ=1qãÆèèŠ ¹œ B,ÎÊúõ×­[››gÌXµ*,ÌÐ�ža¼¶öðáE‹–.MJš2åôéŠŠššŠŠÂÂ+W¾úÊÇçÎÚf6ÛÌlÚ´;²²jkjk««kjÊËÿýwÚ´ôôO>ùì³ÄÄÞo~îO>nOö�/†¡CMLüý·o3æÏ?.twÿÏèÑúÜÝõõ=<ÞzËÓséÒ?þ ýóO“aÃÐc/'6ºž7jj..Ë—9rêú½ÏƒÁç¬\¹d “éç¹b…»ûçŸ_»6t¨¶vNNokp¹––#Føúš˜<™Y–;;³²öï¿vÍÊjÙ²mÛº§bÓÓ Xºtùr++oï²²:;£¢LLÔÕíìBB<=««¿úêõ×_½¢bìØaÃ||üüF�;6$dÐ >ŸÉ”É NŸþçŸôôŒŒÚÚ¨(OÏÝ»»ç�–J[[Š‘#ëêzŽ2ÉåZX lhHBcj:fÌ+¯ØÛ/_~à@a¡X¼b…rDi55[Û°°À@ z|V;;33‘è矛›é«oZZ‘‘ƒ56nܘ”TQ1fÌ�ùù¶¶ãÇ{xXZººFDhimÙWRRZÊb]¾L’£Fýö›� —Ks\ª´VSS•^½÷Xd²òrUÛÃb™™Mž¼jUXØêÕï¾»cÇ­[¯½¶bEeå¦Mÿü£§÷öÛ/¢Œñ:;³²þú+5ÕÒrñâíÛÝÝ•SÞq¹šš\îÝcâÊdµµçÏõÕ[omÛvútZZIISSW—HTWר8dH{{oƒñô§åñàyãûïI¿³àEbk«£ckKÿ;{¶³óœ9èè a܇coÿÚk_|1o^lìéÓS§ÎŸogÇáDïaßãEQ$yçÈŽÊT¢·i� z&lúÖNeë—.={¶  ,,1ñüù‹¯^MM½xñ»ï&O~÷Ýõë—-KL¼tiùr‚�Ë �`ÆŒC‡ 3¦ïQÓzŸ6�¢är©T¡`2Ùlå,×}�JÙŸ™Ê _ßiÓìíKJþùçøñãÇ[Zøüˆˆ>àó¹\CÃàà#$’�>:sæÌ’lj2dÍWWz4E©”$õ¸zk­j½ºr¥‹Ë½¯§êí!]ÝÑ£?ø`þ|…bòä�ÒÓ«¨HL4è£�¶l¡ÏÂîíÊd$Édr¹,Öý&¼‹SRV¯ž4iÕªªª�>:zôܹ={üüÌÍåò“'ƒƒml6oîûLìOËç9Ð?(ú~*d²††ŒŒS§ؽ{ïÞýûNH¨©ékXs…B(,(ÈÌ,( {~®(•–•�<¹k×É“eeÏÃÜ…Ï[{àIa2 ÆŽýå—S§~ú)4TO¯ÿ³‡S”X\[[X˜Ÿ_^ÞÖv¿iÌzF"QiiZZ]�¶¶ƒƒ¡!íhi 4{¶Ÿ_yù–-¿ý–œ,*§¦ ““ûmË–òr?¿Ù³ ¢'§èìÌÍ=|8&&?_[ÛßúôÅ‹?þøÛoÿý÷ßß{Ïʪ¤$9ùöí®..ׯfܸ øüÿýôÓŸ¾vM¹]‚�Éšš23�?v¬°ðÎÛ]¥ÒÎή.Š¢�ïÖ­¿þúã�òroï¨(/¯þM*t,–@0rääÉAbâW_ýþ{GG`àðáômºNNãÇ{znܸaCNŽ£cdä A<½æÃ—*TëÕ{×ë{ÔÕ==/~÷] ‹èè×^ûàƒââI“>þ8"B èY9¨¡áäjm]RrðàÅ‹55 A(--¹¹iiÕÕ=S•ËÛÚª«Åb“ÀÀ±c}|Ì͵´H²¾þúõ«W++ûþ�¶ÿ-‰rswíZ¿þË/÷ïÏÏ¿_?èœ<ï,xy ì{ d²ººë׳³¥ÒÁƒ§OŸ7oîÜéÓGŽ45íë‡w_aÀ‹J$JO_³& ÀÝ}Ö¬£GzŸ7œ¢�›>ÝÆF 0335µ³›:õÒ¥°°Ÿ~úüs:>c±LM£¢víÚ¿úôììÅ‹ÝÝÍÌLMMMÍÌÜÜ-ºqcúôýûw튊25¥G¤¨ÎÎÜÜ?ÿ\°`Ð �ÀÀÀØØÜÜÒÒÑqâÄãÇG�Þ²eӦѣµµ B]}È�Ï??uê÷߇���7ÏÙÙØØØØÄÄÈÈØØÍí•W¶mËÍíììYq%¥§úiP�““µµ©©•ÕØ±ÑÑ~~[·îØÑ½÷Džñ´3&$DG§¹™Éôó µµUŽŠÇã 4i’ƒCssc£¹ù¤IþþÝAXÿ�Kªöê½úß6ÛÊjÊ”… ½¼45=<ÞyçÝwýü”afwÿØÙýç?¿ýöî»2ÙÇ»»›˜XZº¸„‡ùejj{{Ï­ik�±nÝš5––?ýäãcbbnnk;hÐÌ™¿ÿ^Xxÿ�¯û×r‘èÆ�_~ùä“~¸z•ÃÑÕíëÖ[UÎùGgÀË…¡¼ ¢ÒÓÓÓÓÓ}|||||BÑÖVX˜˜xáBJJyyg'I2™ZZÖÖC‡††ŽáèÈç3Bafæ±c.ÈåAA‘‘¾¾ºº"ÑÍ›11G�VVz{GEEDØØ°XÕÕ/9’�mo?ujp0E�?¿oßÕ«\®µµ¶¶PX_ßÚ*K¥޹¹¿DDp°‹‹ŽýKIŠDii—.]»VTÔÒÒÕ%—3::..£G‡…ùùYX¨«3t�Ú¾}‰‰l¶……¶v[[S“X¬­íå5nÜØ±îî½�;òࣣ¨ÚÚ+W¢£�¿u«­�ËÕÔÔÔÔÔ´´ ‰Œ 43»;ð“Ëï]žËÕÖvq  34LN>xðêUuu[[¡°®N(‰ººØl3³‡;ê¾_9¹¼¹9'çâÅóç32jkéqS´´lmG�?~Ä++uu™Œî1ÕÚóà¾b±”Õ‚ûö%$(_…ÆÆÖV‘ˆ¢ôõ½¼BCÃý½��ïyàY+)))))±³³³³³CoÀ³¥ÊIõßå 7­­11ô-Ÿï½WV7eŠŽzåI‘Ë+*vì3& :zñâ´´Ó§çÌ15}¾§¥�JoÞ\·nÈ�‰««û-;{Û¶Q£î(``z¸Ÿ°øk×¹D{¢cö‘dGGaa|üµkíínn“'Ï�ëâÂãutܺuæÌñãññ……¦¦&¸¸ðx¦¦FF$™šÚÜ,•’¤ºzMMYYg'“ÙÖV\ÜÐ •ZZjk×Õµ¶jhXZp¹ÑØHïCKËÁ!8x̘1cx<Šjh¸v-:úìÙŒŒ†‡  K$*)IHÈÈ IoïÉ“gÏ65US“ËRR޽x13ÓÎnÆŒaà •­f±ôõ½¼ÂÃÇŽ51¹_¦êÑ™˜øûffV\œ“ãá5q"=\r/»ïå¥Ò²2z) ;»ÀÀààààG=ê¾>s–ÉjkÓÒ22:;==##çÏwsÓÒ’ÉZ[Åâ{µzP{˜LUÏ„î~Ð×÷ô;6<ÜÔTMM").>þÈ‘Äļ<{û©S½¼tt0ïÀÀ'߸±uëÆ�¥¥Û·Ošô¼Ç|A Emí•+/jiýç?¿ÿîï�˜ž®'öÉå--ee--ÚÚNNÖÖÊÊ8>߯ÆÉI[;5µ¸¸¥E.wq±´ÔÕµ²ÒщJK[Z:;™ÌòrÃÃcèPsó²²ÒÒÆF‘È¢®Ž¢LL|}•³‚“Éç›››šòxÊÈŠÏ´´äò›7%º„�¢š›KJªªêë««ëëÿù§¨HÙ:’”J)ÊÄD,îy«,“Éã úú÷¯zëÏÑ=îî�÷¨û û˜L--##-­ÖÖÌÌË—»ºì쬬¬­íì,-שׂS¥=$Ù¿¾¢_uuzIMMss;;>?1±¨¨­M.÷òÒÑÁ,dð¬èèL™×Ò2e úâIÓÐ2äÛo **^œ6³XVVo½í^?xž“Ùx¹\KKuõë×++KKår©ÔÜÜ××ÊÊÐP(ÌÍ-,,+#±XOÏÞž¾)´¯� z¶nômÊ\®¹yPÐŒãÇÛØ(G7¹Û‹;ÅÄ£u_X,==oïÉ“§M36.----//)IJЉÉή¯Ÿ2eÚ4__}}ÕÛó¨(Š$I’¢ &óqo`àx¢Eðl¶žžµµ®n{{aaEE{;II¶µ•—´·ëêZ[ëé±ÙA †††±±‰‰LvëÖ�íí¦¦––ÚÚêêFFvv<^UUffY‡ca¡\^u޾¾­­®nssffnnCƒ2Ò£(©T(¬¬|øY«T?ºþb0Øl.—ÉìêjkSÖÆ=í£–Ë…ÂŠŠúzC pt4hذ#<=uu%’æf‘¨¿mê_)r¹²…rykkqq^^G‡¡¡��¾>û9‰¨ž7O46a2y<'§ÀÀ¡CëêbccböíKI!I&SSÓÊjèÐÐP''O9æ‹Åç[X²XÅÅVVt‹%88èé‘dK‹‰‰‘уo®½ƒA�ê7ztkk||\Ü¡C{÷J¥EQ$Éåzx„†ššZ[?Ù£ëG ðòrqa±Î�;|xïÞìl}}w÷±cúG|ÒGMQRi}ýõë±±—.]¸ —3L¦ºº©©�Oh¨‰ =­ˆêµ�ýí+¹¼±1-íĉƒSRºº¤RŠâóFŒ µ³ÓÔDe@ïžøl¼ýAÏÆ{à@NŽ‡Çœ9÷ŸÌày‚ù³àù�ÙxžÌÆ Ïö\¢aFS€£ŸÁs…˵±™8qÁô@¡²`€@Ø0@ ì ö û„}Â>€aÀ�°`€@Ø0@ ì ö û„}Â>€aÀ�°`€@Ø0@ ì ö û„}Â>€aÀ�°`€@Ø0@ ì ö û„}Â>€aÀ�°`€@Ø0@ ì ö û„}Â>€aÀ�°`€`£ ^Nééééééè€�aÀKÇÇÇÇÇÇý0ðà6^€aÀ�°`€@Ø0@ ì ö ìGY¹¥¥¥¥¥� §§§§§‡«"<pEx±àg7<^¨ì ©²�ΞщÏTö û„}Â>€aÀ�°`€@Ø0@ ì ö û„}]}™7oݺ'ÐýµoßÚµ“&=ýý¢²`€@e¨dúô±c R~Ç`APT÷w EEuM£¿R.OÿÿÞgzê^÷EÚËáÃgÏfe=Û×a¨„$)Š¢(Š  eF§ Áèï襌žý(ýÝÝ¡I*·©Ü‚òÿ/Ö^žû@% IÒ±I’dÏgzÖ»ÑÏ(4åã †²JŽŽ ï~¼ç6 :\{Ñöò<@Ø*!IeÜ×puWúuW¹uß{çM¯t�œrkÊ­(·Ð½%e Þ‹·—ga¨D¡ ã0eLF?Ú} ì�‘Y÷xwÊg ¢»VîÎQòºÇ¿ë¹ä‹º—g a¨„$»oyíYÛvw8Ö³Ò­g8¦Ô34뮬ëí†Øk/Ï„} …‚®ƒ£Ý…ÑßÝýlÏïº)Ÿí¹üý·ó"íåYBØ*Q(è1ûh=+áz ËzNlѳRŽ~†~äΩ1zއ÷¢ïåYAØ*éž{¶ûÖ×;§­¸;*£g¼åpÔÔ\\OOSSMM6[¹E‘¨£C")--,¬ªJN®¯ï'ög/Ê¥»×í¾ÁV9âÞ�Sp<þ½<¯Â>P‰BÑ=fß�“](ó»o˜e0Ølwwookë‘#õôŒ¶¶êêúú¸¸úz™Œ ¸\55KKss]]33>ŸÍ&Éš¹\¡èYU§Ê^”Ïöœ3··I6îÞÚãß˳‡°T¢³¯»º¯çL´Ê0­g¨Ædª©Y[ëê*—êììì”H;;›šººH27·´´¡ACƒË¥(™Œ¢”Uv|¾………ŤInnÚÚººŽ2z“J;;;:rsoݪ®NJª«£(??GÇqã&³¦&+« àèÑÛ·e2‹Ï76ž;wÔ(¯££¾þÀ�+W„BCMÍÑÑÙÙÄdèPKKOYi(—wu•••”ÔÔ\¼XTÔÙ)‘�äÝ3ø*gæí"öŒŸ5„} ’TÆ}ÝÕoÝcÙuW½)ç¶e0(J")- ÂÊJO�Á05ut´²ŠŠrt$‚�ɺºÊËoßnl¼v-?¿¥¥³“®¤(Š’Ëe²®®ÒÒ¢¢ŽŽúz‘H¡àrutôôFŒppàñ4H ()¹p¡²²¢¢¤¤­­«K ÐÕ55µ´ÔÔTW¯¨hmÕÔ43ãóy<‚ ˆººÒÒ–––¹œÉ´´ôö¶µ ³° ˆÊÊââšš´´öv‚04´¶664ÈÉÉÊjÔ¨––üüÿ­«“Ë»of0è‘ù裦�RwöœºäYBØ*Q(”1^ϱû”Ï*°;G¾“H23““‹ŠHÒÅÅÄdÐ O_ŸÃ!‚àpÔÔ¬­mmÍÍ­­õô˜ÌÝ»¯\ihhi¡(‚èìliéèho70Ð×wu51áry<55.—Ë¥÷Æá°ÙEQ”XÜÐÐØ˜ŸßÕ¥«ë奯¯¯oe¥¡!‘èé™™q8A’ׯWUuuÉdL&�çæfjÊbÑ[±´´·75µ´¼ûH==‹ jjd2’¼³’� ‚~ŒÁ¸·òïYCØ*Q(Šî[w{wgØÇdr8&&\®D’™™™Y\œ‘‘™I,–šš††ƒƒ««�Í„ ¶¶\.AðùZZÚÚU_ߨH#GŽakëë«©)•æçß¼Y[›™ÙÑÁåøû›˜A�$IR”B!—‹DYYååR©——““¦æ A††!•VW74””ˆÅ$©P°ïȤÒôô””ÊÊââ®®;�A&‰ºº¤Ò;ë{Žó×}ã²²Âïùx�ö€J”³ñ*°Þªû”�Óÿ±X£F[[kiÕÕ55ݼYS#µ¶ÊåL¦��––²ÂN&«¯oi¡oš%I‹ÅÒÑQS£÷)“55µµI¥2™º:�§¡ÑÝŠ"ÿ‡Á¨¯/-mllltr2332²±ÑÕ%‚�˳³ ÛÛE":¶“É:;oܨ©Q(,,¸\OO;;�öö²²ŽŽövŠb³ ��uuÍÍÛÛ«ªÎË‰ŠžÇ¬—P9vßóu/Â>P]IGÝcôݽDÏg ‚ är±8/¯¢¢³sȃѣMMéª<‚ (©T")--,¼};..'§¹¹­�ÖŠŽŽ+WnÜ ‚Áƒuu‡°±©­½}»£C¡èÞÝå¿íí--ééMMffcÇAˆDÍÍׯ75Éåt H·¨°055?ÿøñÎNs󀃠 __CCz«]]��7oÞ¾-“Éåô–ï<ºžS’P ]çvïÍ»4eàE’Ê ,”�ß9mÇ�ëß]-§\æEÜËóa¨„®ìS[ÝÕpwF]=ï=Dë+SŽø×s›/ê^ž„} …¢gÔGQtåÛݱWÏùkïÛïιzï^âÎîÅÛËóa¨¤ûÖ;+Ýî­†»³îÁÕot¨FQE’ ]{÷¢îåÙBØ*éž £§ž·Å*DZc0èP­û+eœrÙžKô¬’c0zŽ ø¢íåÙCØ*¹srŠ;ovUVÍÑóÔÞYØÛº×êþ¾{~ßu/ÏÂ>PIYYYYc#úáyÆD ¨ì€>íÛ·ví¤Iè‡*û„}Â>€â‘Æìë9Á0<[¨ì PÙ0@ ì ö û„}Â>€â‘Â>‘H$‰Ð‰ÏƒG ûJJJJJJЉÏnŘè€�aÀ�°`€@Ø0@ ì ö û„}Äÿ~T€¨Ë"‡IEND®B`‚django-commons-django-constance-1032c50/docs/_static/screenshot3.png000066400000000000000000000620541527040271600254360ustar00rootroot00000000000000‰PNG  IHDRÅæ¨oÔsRGB®ÎégAMA±� üa pHYsÃÃÇo¨dcÁIDATx^íÝ |Õ¡öÿ‡J‚ñ�p �„kˆ.B>€´@E!§zˆ´ˆøWÒ‚ ÔK,EÑZ¼ã­Tª¾"ˆm¨G@+†ì‰(6PM0‰o@�ƒ¹%Á¡xù¯5{rO —Mæ÷å³Ù³ÖÌž™½³³3Ï^kÖ4:iú�{€çŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(xV£“†; \X…õÎÜEZ³£@ÇM±qP˜:ô©‰ñýÕÊ·œW>Ù¥Ôw•´n·Ù$¤Æ ißSCGŽÑÐ>­à,ä?òWÏÖó»‡ëÅÛ{¹587¶(1á÷Úó+ÝwK75³?øÒ#Ê/8¢fáþ÷>Ð0]ØîÓ¹«5û‘甬XÝ÷⫚7ož¹ÍÑñý•ûþczä�LqõÇ‹ u¨ÐIï8—r7ikqo�¼Õ ÄVÀjE p]ÀPœ«ä×éHìoõ¤ Á®(OBº¢CÅ?ù[ÅìJÔ»Ýj4l¹¹*p'àb¹pݧ7&*a�4qN¼êꈜ›¶ýÍ‹Qò‚fy÷<Õ°Þs×]Ü¡jûã‘í«”øörmÚ]ì[.´³ÆMÔíýݳY3_Óä¹_ú¦ËõÖ”ySÕÇ-•æ¯ÓÛsßպ݇j_‡#S¯M~_QO<©kLèŸûö:ívžHˆ:¯)·öªò<¬Â-ÉZ¸Ðì[�³V…v¨¸‰·«Êj…Ú’¼P —o’oÑPu§‰·×ãœÜÒ|­{{®Þ^Wöº¶WÏ‘·+~h”®p)“¿îm-HJÕwBÚ÷ט)v*¿¦õ}žv¹¹ªñÊN™§©æ…-ÿ9ÚB¹Rg*Öjöu¢&ÍÕ‚{25¨ÒÏÎÄ ÅÛß™®—‹'V ¼§sĨGæjwωº/¾¿ÂM+ÍߢU çjù‘‘úío‡*Ü]Ô S[;(&¿T]ïŠWÿW( Ô„ÆU 47)@c^ºOËÒ^n²žzj�ºÞ7E·t³ÝuKud×:-˜½PGâ^ÐŒÁ1µö�f8ëX£ðøûß?ÜYGá–UZ07I¥UÖá ‹ºæ  1�õ–n²Y²4w�_^ ½Ò¬[£ÜeíS~M�$+vÊD íÖÌYo¾Yï›tÍ£ÓT±k¹J~ê)­ �¯xm Ík³`®’ƒâ5Ëìoõp[¡–Ç٥̷ßлWÜ®o¯øå&?¥Ç’¯P\Ùþ”Ñ®LP·ªë”YŠïUŒÏìyú¾tP•/¬Ú^ïšûàû¹¾�Yªã;B4†P à,]°îÓ‡ )$¼z›è©”*31Q[»NÔ£wù‚›Ъ›b§Ý§AÅï*qu¡¯²Ì¦už6Um ¶å€fê;Q#ÛoÒšuËnINÖ¡A·ëV'[¶ ÷@Ý71F¹«ÒLd<=»Žâ¡÷é.'[jÖ-V÷ÝÞS;V$k»SW&O[â4ãV_P´Âûë®q1:’¶F[|Uæ)g*Ñ„Í>S¦)Ö ÄV€Z™õN¤¤¤ò%U¸:QÉ¡ñU_›föµ™¨>[ßÕò]¾ºÚä&¿¦¤€1z¨òc¯è þwMU̦$•¿¬&ø¿–Tªá3*íOÀêÐÿ.=z{¥-x»bßõ|žgd£’“ 3±ò>ØŸë4Míz„.ؾÿ½Nqé:­ù2D±·ô¯¥Å3J·Œ0ásMšòÝGï¡•ZRË4SÏž¡Ú±µ"%èP^nÍA½zÅkΓ±å­Ï§Ò-~ŽfÝRsÉ€]v¨@‡Ü²O¨ÅÖÒBÞ«§ºïÒŽ²'±)U_†Äšðç–+ë?U/Þ^ÖÒš«´U¹ê3´¶×¦—zv-ÐÖMU^™Jr•™š§Þ±ƒktÛ–yæ·ÌšVÞ¢¾}õ*åõ¾EµºëÅYº/FJ{ûyM¿g²~9ó5­ÚR­;ö)ùznf‚~Y9”>–¤¾½Ò^ž­ä-…î:ìuŠ“5ûÝ�¬4²s³¡w).è½<;Y[*vBÛS5sú\¥ž¢ouxìTž]õu=²KëÞxMi]Gh`ÙË«©qRÒsU÷'wÝz:q‡zÆ�QmeŸSý5&.HŸ¼ü²R+ÞÚ’<[³×”*ÄWg¥ÑIÃ�¾0LøJ}7QIëvë�3qc…´ï©Ø1·kh·ZBî‘íZ•ø¶–oÚ­b»|ãPõ9^ãM@¬¼´ízýØö[4ojͦËÚæÙA²,7á<¯Ø¹qP{³ÞÛ?4ªÚÊG´ñíç4wMžŽ7î©ñ/Þç»dQ¾ òsßÖºÝîãC{jиqºµ×n½6ù]…?1˽”Q¦)¿¯¨'žTͱ¯jŸWhBð…˵©ÀY³B{ŽÔøñ±ªùòäkÝÛ ””ºC¾EƒÖ¡¿FN£þe ®‹ ÐëÞž«·ÍÏÁ¾®öù÷¿}Šnï_³;¸óZ%¥j‡»?Aíûëö)·WÛÆ>ÏÌ×4y®4eÞTUþ‰Õþs<¢í«•øþ—Ê󽨾÷@ŸšýØvÝRmP_>çÊ–D%,Ò}/ÞÊ%›œÿ½$pÛ3·ª¸C”:¸e8S„bø¿]™Zµe—*NC?¢]©¯iîš Å�éÎFÿà=tŸ†ß+ݵZ‰o¯ÒÖ]yå硇v¨¸úœ; §@(xݧžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(þ^ µñý7ôÆïkc¡[u!ä&kæäÉš<3Y¹nàÌùa(.UþºD=•ðKM¶Áoò/õÈËÉÚ~Ä�íWе;s½Ö¯ÏÔîb·ªº#۵굧”ðKû\&ë— OéµUÛå—O<¦ÑIÃ�ö ¹ÉO鱤Ýn©’ÐõЬ[åýC®’g>¦¤¼0Å=ñ¤bÃÝê2¥õÆôW´¾–Àtͽzñ®^ pËu;Í6gÍ¿Bqé:½|ÏmR�zŽTS¶R@i¡RßxL ¿,Ö5÷ΓɑÆmO^ ¹Ë7éÐq©qØ M™v»z5³¡z¦ Õy ‹{H·äÎÕi‡¤�ž3í> v¥m‰~91Myö±fÞ-÷MÑÐ�4Fݶ¼¯5áwiÞԮζ˜m˜åÕ8Hí{Æ)þ®ÁêpêÀºë�Gôô'Rûõ«ûnU·f¥*4ë}ù÷Ÿh·Bu㣳tk‡L½6y®¾ToÅ�+ÖªE[u¨qˆzŽ™¦ûË]¿»B£÷”yšž¬™�%)/,NO<+»ÙÒ]«õÆïêKçI™}컦Ä÷W«Ê¡ú¡[”;÷ ù^’1šf6àÛå|­K|Y‰iy:®Æ j?Pïó½žÐ�ùW÷é-™&�ã4Þb;ÐL§ÎѼye�ØßÙz>Ɉ­ãykôÊìªç×J~YsMú³‹?´I‹WËžö[ºñ =½Àˆ-;ïÝÙo(³ÔW¶òÖ¼«5f�Ððp“Ó8Ûr±u¼X»¿\¤¹ïïr+êR¨í;L 6?&Îb[ fÝnÕ˜˜ 3] ­›òm¥ëK%Ù@l'�Ò¦E/ë�íÎŒÓ+\­ÙO/òbËîcÚ=ýÆFU<­CJ~y® ÄÎ+¢C›)qµïDèío?­N ¶Ž«x·y=ŸG§{†p©ó¯P|Ü�p!ÍTw#å%'ÛîÕ=5þ…y&,¿ q]MÑãÔJ©¸8ë–pT® là=•b;'‡(¼•SQ®Y¸Ù¦QêÆPŸ ú•ݧ4±·/4§­)Tì“O(Îy¶5zž¦ö±ÓUíJþÐ<7¿{OtöûÕG‡«½)¯_¡ÔòÀŠûŒ^5óŸî¬Ð¼$öQ¹ÚºÅöïî¬1öõ|i¢|›ß¤MŒâ �óïѧ3_sÛòÝf&›”V˜«\çÝMZø ­P‹¶Úr�r+‡¸�pµ²MÍáQna+W;œe ´æù{ÌcïÑï×8m³Ê-¨ôà ŠPt…;u¾)´™Ýáfê?´�)™kžP}µ. è]öwö; ÃP êlkÊž¯OˆIç¾—¤r?ïPµ�²!}‡Þú9½–T >Sž0á™ó—4|þŠ»ÃN*¬; –Wê|¦JíÃÏ@©6}i[S¥žã_pºpÏ›ÒÛ)Ÿ^�|yú�r«56æú‚x€;÷5ùºŽ¯çZ_êÿ¢^}âWº}hi×*½ýûÇtOÂÚxþ7•…ân}ÔÓÞïHÒÂÔ|•ö™êÑG‡‡:³­:«ƒmFµÝ}Ÿ±Ý�mêWõÒK¯ÖÚµ¸ªêê´ †*æ^7äÚǾð’~[k³h¡Íç† ¸!&á–j]ªÓ,]ÍÕÙîw±Ò’ÞÑ–B›0í@[ïèÝ4›ÌCÕµgå~ÕÅ*p—Ùžö¥ïÜâðöªXÂ̯ã:N¡¡¾×gkê:çË„Rl×8Y>\�OÛÚkúš¬{f¯WèÐÛ5uÆ}êìö.í>]q¸ÄùW(è¯1qÎÙ°Ú´ð7ºÇí6ýô‡6�µ×À>6áuSl¬]f‡Þý�ím—¹G<0WëNÛ²ÙLG\c"n�Ò^y°â±>­ÚÇÍj¥®QNGf­Ålëžõvy&>}3j‡[ÆéûðÝŸè÷úºk?茞Üeä{íÜ—ÓpëÃìg“û å}¬Yæ… s~6uý<+~&à{¨òw×ä“ì�µðÏÉÚ¼ÿ¸=ÈR»«¦ ã®VKwq«~™Ãþ]߯/½®Eéût\�Üéz�›'ß!Û©Žç*Ô�]¤W?.«±�æ H?Åv.Mž}º8ý]-Ïs 5dkÃEÎÔæô,•:S•ìI׆Cî´ö(5µÚÄjcÞ@sæhŽ{#ãRc? çÌyQ�ŒÖ†yÏjÑ6w†e?Ëßß•TïûÒ _h³�ØŸ¥ô=NÕY°á×®÷a�hcŠåÛj(�Ø Qô€NæuúZ_—}>Ú¦,ûwq@_ßÅì ò}­Zô™Êßg•9�AêÒ¥�MŪyܹGéeïPófÍÚ¤ ø…p¨åõ±\¬ôϾvëÎT©6|a"q».ê´_YgŸŠ=!$z€:™Ï§¬m¾Ï˜ÒÍYúVí4 ¯ï×ì _¨(ȼ–ÎÇS-_Ú?UªÒÊYCèn.©óÛèSdŽCéJÞP¬6CGèj{ˆÐR×ÇPpqºj²�‹ã¹ú¸PÛi8.z(Ž1Bí¾]¡¿|Q³‹µsЩ.ü³¾jS£5¦� hgÞ—œ@½Ç”îï­è`ßÜrûVèÙ„%Ø[¥.À¥'HÁöxÿþŠ/‘l—÷ý=«ò·›•ß÷³>–3§tƒl&nÓ÷gÜÅ6§›�xÔ)$Z¶±øÛ¬ÍöO§²œ/È—‰Ý^,]ëg}ÛÔñ¥ÝY¨ëç ΡŠÑN¥YÖÌ_ÏÒëI昨ü઺ÓdŽâbÙV)P›£6éøq_±ªZŽç*«í®†}Zñ¬»L­§´Y§Ùª¸ø-Åa7égƒƒ”õ—$m«’‹ÝƒÎèŠë¡-k¶Æ´é;@íœî {L`Þ¯N˜·m5•» Ðw M¥î¶U¾Ý¬ü¾wÏmõu�6ìÑaêÞ·»MÅߣ µ¸]¨7giséfmp2qÕ®ÓÑ¢Ö£‡Zž«.Ôuý<À9vÓt=;s‚FDiÏg‹ôâÌU×÷ÑõÊçJ-Çp5Uî>Ý�N_»xübôéȸq t­Hõ�?ì(;_Ïi9yQ«÷›éê­1-ûj`'óæüK²¾Øß]רÞL 4$Å*²_µlY>ÐCý¹]§Ýo§ÿÙNWîB}Üù–³Ì¹hôl|]¨³ÌgL–¶Uï:mîí€d /®6¯d}»PW^¢Ø¼êàb h­›Æ? §ž�¬¾A{´"¹Ž®Æ§ÊAAN«p^^¥D]\äW5nì+Võ}ŽçÎÄ…ÚNÃà¡Xꢸ¸îUF©Þ³!Ët– ècn­¥5&D®ï®=YfÙîÔ×785ЕjÿgÉJ/ 2ïùnÝ(Íòu�ñ°û­âÝív¡îÔÝüî׆U_;ÝkJ÷¦ômæ3>2²Žo'=ÄíB�•ž®âò®Ó{´!Ë6W´ê><ÔéÊrŠ.ÔaêÒÅüÉÌZ­ÕûíB‡´mu–yÕ;©»Y?¸€}¦Wí©JË÷ø¾®6Ä~QPW˜8Eæ Ø¾AÚ·j…¾ðHé³ät PÍC¶ïyUÕ�9Ôc¼ù�§E3ͺ¦ÏÒŠ¢¾šüð8U=d³Ûë8ž;cu—�ÛíxÇÅ»N1µðÔuЏXÅÏ"<‹P ð¬ 2ÐÖ�Ü)N­E‹îÔùwAB1þˆîÓÏ"<‹P ð,B1À³ÅÏ"<‹P ð,®S õPRR¢üü|8p@G�ukàÒqùå—«E‹jÕª•ÝZ¡NÃâíÛ·+$$Ä9  rçÀ¥£¸¸ØùbïСCŠŠŠ"€‹P §±wï^ç¾mÛ¶Î=\ÊøL€ªüçœâü %N­!×\£k®¨áñ³´<û˜;3K³mý½Ë•_6]ëí^-ÏÏ×ò{ëšç®®†²ÇŒÔ쬲mùËu¯}ìì,·Â•5[m}üRùþ¬¸L½o[#•¸Å­+³%Q#Ý}qVW¶îê7ç9~Ày�Öý{“¿ü^ϼ_mËŠm!€†À~žÙÏ5€�„âcšõ“Ézµ`¤^ùÇz­_ÿÍ™£'ÆýZÖ8âŽÖ´õvsûÓmNMÌcɾòúW4²•Seܦ?•-Wc^]r´xö e»¥ºd­\ª;±1Eµ&‚-M«šŠ·¤-5µµ¸íO•öÑÜ^©Óî&<Èý2¨ú1®½Kãuê/~ª«ëË#ßí^»¢Væ÷±^¿7 Ÿ=‡˜.Ó ûyÆØ€OÉ7ßè»ßýNÛ‡/¿Ù²­‡wøG(.Ú«›2£û):ØV4QäèW´>õ ®Ôr{!lœ­ù)En¡6YZ¹ÔìlD„"”¦•iµ§�œ¥iªˆÅÙÚ¸²ÖH œ™�‰ZY½‚y§­LÜèN×W+�|¥ì˘d=SùË¥õz¥–$œ5Ûæê½&.QE+Wj÷Ô©*úè#·ÆÇ–�z3Þà¡8¸­"ìXKçké–ª�´I“&îÔ…0XÆI+gÏWå^ÔUlÉPŠÉăïyX£#¤´” U�ÐýVDÎR•7gghéÖ@ÅÄôs+€³3xpWÍ_š¡ÊoÏcK5¿ë`óîuÙîýg)£|¡l-w�¦}xª/{jQ©û´ Ä?_lêÿ¼Î.ÓDz—kVüùN˜­Ôz·Z\XN ñìÙÎtã6môƒ+®p¦í½-[v>-ÆÞࡸI?ÅÏ¡N%izööiàð{•˜š]#lžm5zÒ4õÊY¬Ù+jïDíë£!ýªß“äW¯Tjµ 2Ìæ­Üè[G¶ -[#&iÄ0FyÄ÷Óvô8�X±H�Š”²h…FŒmÞ½®èšºBntSñÞ -ß:XÃ:Ý0ÎJô´õrÎV°Ýýkëâ_”¢'ÆÍW˜×¯OÕŸFfë¾g?¼¿Ã§w0)ɹ·¸ýܹjû»ß9ÓöÞ–Ë‚qÙrhØüf ­¶C×Òä?éá[z)° M¯Þ÷S�œöa­çOÖÏbý¼ú9’õ9BÓn‹ÐÆÙó•RàV–Û¢´¥9R¯!êgRA×!#¨ÕZ�QýÐ?Z1£#´ui†²Í3ÈX¾U£cLm-lË[ù~žÉ9¡ð¤à~=i«¯t3ö®Ôâ­“4º_åÀÛͼÿBµ"e«SÊÏHÑÆÁÃô=2ñie¬ÔÊÈøòýhk¶³zµjüjø�Ãn—éÿ÷¯éx^ž¯¼Rßz˹·e[o•-‡†ÍoB±£U´F?’¨”ÏÞÓc1�:¼ú %Vô=CUÚªíÉÚ5Qô¤i¦•šýêJUÉÅ[Òd3±6>«Ÿ˜{ýä÷�·V®¬Þ…:PÝbF+bëRe¤¤iùÆ�ŽéfjkQe -5Âéu/-Ms„ËN[*Å31¸ªnCâ¹t¥²”¯´•i6r Îc&Ö±"ó°õYý´ì žØ'”f~+ŠÎö×à°á7÷ñÇÝ’�-—…bxƒ_„⢔GœéY©ît“H�œ4ÚL”(gïEhj ¢IÓz)'-M¾¶6Ÿì�+�®ÓÏ$W„í?�7QwejÍs�»ÅhtÄV½òø+Ú1Z&çFÛaº-"Q)YYJIŒÐmÃj¹Îd¤yÿu]ª•)JK»E#bÎg$6ì7>1Ï(¹ü ¾ä9¯r—hT£QZ’ë–«É]2J�F-Q³€ËžC^-ÛrÙ9Æð¿ÅÁý†Éžn»bñJù.M\¤ 4í‘vDÛó|0_‡ÈÓLðp Žle,59¢Ÿ"+èGF1ÿ¿¯”-Ú¶ k„>ìt�&ãÜ Ö�q1J¼÷^%ÆŒÓ�ZEÚ*fdW¥Ì~U+o¢~ç`¼ºÀPó q̹Y ­ŽÖ°ŒZY6PÞ±½ÊÊ:û“.-kõ\£Fj›è´ÞW—�«F§°5åjÉ(³>»ÎZn£ìŠÂÇjÙÉeî>œ‘¦7ßìÜÿ 8X�ܵvÞy§so˶Þ*[ ›tŸ¢Y›§I�‹5ùG¶ûå�4-5R÷Ìû›9ë£ùªçŸñùºM¢5iÚ°Š.ÏÎÒæ>¦«"}5ŽàÈ~êjîßO©:"°åt¡6ÿl×é:U9§ØÜêÙ¨¬I¿ÑŠ7Öñ£û©®ß�¶ƒG+2§@· ¯{™3ÑmØ=¼r²®·¨fø3¿Ã�-¦ìgG:ïã�?yD˳KjüN4hÎѲLwº\¦–ÍùÐ�®¯p�]vR'OÚ[Žÿ—ô_‹sÜòI-«% ¯}ÎæçÖº%p:Íãâœ{{þ°½üÒÞéÓ�i{oËvÚ¶—-‡†­‘9È:éN7xå—•©"F�%ÓÍ Pчš6£¤Äßå²R¥„Q&We—é>'Ek•«”%Õä±Ct>{2=˜+­þ¹:— Öq›þj¶}Ðm¬ð:B1œ+‘£tw¤p˰©»GU–Ï9Dñ}æ(%y­Rþz¿Æžï“{/¿\º5I9î@]¾#W”!À9Ó\±ñ±š3j”æÄÆ+¶Ö¼©!cûhÉŒÇ5ïþQº®rïê³tùå½UWÓoø�xMNIÔ²Ín飹Z›YÛÅ£¼‰P|^SÞ¶mÊóÔõhX—«„æÍ•0¶bêê"M`Žü2W�º®ÎeÎDŸQ�kü’©Ñ�×µÙ­+×Ü„ô”±ÚœÐÇé>Þ'^K6½§|üâ’L_-Ÿ¯¬6·éÖkíE²³õá›+UrÃ$�tÆÚ¦¿ÏOWð-ãôÃÚF‰Þ²\ó3šë–q?T½‘Îÿ§­”†ÕwùS±ÛÞÚU“|;ZI¾þéÛHíû à’rÎ/]rp™â»¯U|ù¥™àÂâ’LPÁ/ZŠ;´i¦Â}{}…ü]*8!íËv»÷åå¨à²Pu8Wá²Õ5®<oÑòùËÍÿõeÃî"ý3ß-vYˆm@^^ÿ5ð®ÌÄç”9c,�À\ö¸áN_4M~�¯¬�‡Õ¦O¤”µVO6ÓåùGjË›Öjƒþ]C»µ6ùs-Kú»Ö|ž¡¬ì㊸ª½šlUÆ·ÿÒÁ-k´:5C_í TT·0ÙAw�l[¥¿$}¬´õ¦~×q…w7ËÛ–âe;}T«æ¯VŽëÛŒ-:Ò)Z�´M«þ’¤�ÓÖ+cËA5»2J-|ûè ÄïëëÃ%Êÿ:C{›öS·„Ó¤~JÓüÕ9ÒáoÍ㎨Stkåg}#]iÖy…ÝíeJúû}ž‘¥ì㺪}Sw�.999jÛ¶­[ú>Öê¹Fôkó/ñw£Ô¾±[ عû\€KŸœSܶ½Ú”äiWÑ1eçVDŸj+[6�² PÍÚt0 íUÊÊoÔ|è/4iÒ/4¤ù×JùÜ,`�¸LWþx’&ýb„:¥iU–=™7_é»f—·õG¾¬XÞÑM#' V„ù7x’íæ|LY+?Õ‘>cÌú'é¶«jõ?+FÓJ?w‹z4mª·”uívÙãÁRÄ`MªÞ-{oŠV~Ó\Ca÷oˆš�¢*»ÀC®ÓŒ“'•�œpNØÀ÷ç'muP‡6…Ú·ëeïk£ÈȶêY¬œì]Ê)TX‡`“q¿ÑÞâÃÚºâ�š?ÿ�&h–èðÁßÚ«�=¹q[õ»²™ öæ˜B°"š_¦¼ŒOõù7'Ôgô$÷œåºd›m—(çÓÅfýóµ8½@'æéûæ×üoöªøðV­øã|ÍÿãJ}SrXe» ¸¸ü$7QDhSeei_›HÙ+{¶�ŒÐÁìL,n£öå½{l«î$§%×Þ& ¯y Ðàà+tâÄq3ÕD]~|‡ÆüG…£U‹µê´W!ñµ—­ҭךh}Øäòýž ZvpøÍ%™ZuUqa¡šFD:ç+2R¡99ÊiÓA¶ó´Z]©¶Aû´õ+_Ûí±¼lír/»©âƒÚg«�ç+õë…¶ 3 diÙüeÚx"T‘}ú©Gè åçÕÖî[¢â#öÞ„ñ6‡µkã^ÙH}¼([Ùy¶¾º•»“Õ™Ϊ*iue[íÛ*ßnS^ö.•í6àâò›PlCp5SäUem³6¤JMC#|!Ym5dX/�Èðuo^¸2S{Ë®¬‚¿¿©ù|_[Õ[7ô1ëh­!1—)k±ín½X'ziH�îÓ‘êѾHéÿýŽ>?ØDÑÃnTØÞÿÑÍúÿønª¾)ªqÕãÊ }ó?óõ÷mnU™Èj_”®ÿ~ç󪡷í ëuB‹ç›ýX¨•™{M4ø¿¸N1ø3®ç  ¡ás *øOK1¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(ðˆÉ“'»S€2„b°�xÞ¼yn P†P ÐÀˆ n„b€Œ@ §F(h ªbÎ)€šÅp—_~¹Š‹‹Ý\j Ķl?ÏìçÀ‡P §Ñ¢E 8pÀ-€ÿ«+[öóÌ~®|�4Üi@-JJJ´}ûv…„„8’AAAî¸tØbˆ:¤¨¨(ºsÀÛÅP6ççç;”G�ukàÒa»LÛ/öZµjE €JÅÏâœb€gŠžE(x¡àY„b€gŠžE(x¡àY„b€g5:i¸ÓçÍ�Ü)N­E‹îÔùwABqII‰;À©ºSçݧžE(x¡àY„b€gŠžE(x¡àY\§ê¡´´TÐÁƒuìØ1·.Mš4QóæÍÕ¢E ¸µàŸ.äuŠ Åp6ïܹSÍš5s&ƒ‚‚Ü9pé(..v¾Ü+,,TÇŽ Æü¡üȾ}ûôƒü@mÛ¶ukàÒµwï^�8qBaaan øo†â‚/ôÖ /è­îÔaªeÏkÊ£÷ë?;5137jÎÀIz÷ÚßhùK�ô–�ö=ªškõ›å�JO�Ô3Ÿ»Uåì¼—ôŸ¡n±Š}ð@ÕÇ4íøCÝñàƒºãj÷çhà¤Z¶:f¾Rz�fÿ­}ñÖ zá­jçaßúx@#Ícæ§Ênnãœ�ªº;MÕñ‡wèÁïÐÕ¡îëåΩÊ÷:tzË>¾òkrLß~ð==÷ïÚ´¿ÄlÿZ�zôA%\á<Êì€ùŒ>£ù‹ÔË}ZÕ÷ hˆ¶nݪ+¯¼’b ‚m1þæ›oÔµkW·üÏ… Åþ1ÐÖ±/ôÂè{4wÿê÷¦*5õCýŸçè™ñ3ôQ�»L¹^JHµË˜Ûü1N͵¿Yî+§V½&¼•-Wc^]*³8!TÜ3Z|�ãÎó©Ø–{³Ið´ûL_û¥îy³Dw,t×?¡‰–=6^3ª<ÁŠí/´¯þwî=úå[[Ýyµ±¡ÖÝ�ŸÓußÎÕ=¿|M�Õ÷5ªðmÒ¯4þ™/ÔóIßr˟줵¿º­ÆóWî»úÃß¿u €7Øsˆ Ä ûyÆØ€Oéöí:0gŽv�Y~³e[ïð�P|8G9¶1¹g_õ ¶MÔ)î%¥¦¼¤AM/·vèuS4á‡%úüÅ·ôÅévátû_´Foý÷NŽš ÿt^Co¥Q2ëÿûª‘û�àëi�¹ßùÅÿÖ:¿†à«uë}ÍÞÕšÿuëêËìß›/nPɨM¹ÚyfuS”0ªöç¿éojM‘[.AÿúøcåÝ¿þµj•[ãcËN½™oð�PÜ4B¶u|Ù›JÚZ5mÙ‘/Ž`u½îߥ’µÚtº†ÑÓíÿ·›ôO3Ý÷ßÝDì¸ÚךûÒÍN÷èꊾX£5 Ôµ?¾®ÖùçÔ·_ÈþÊ_ÛóßMœ/ÓDÿÞóZóüÿ®µ•CöoÒMfé9onß1àRä´ÿáÎtã6môƒ+®p¦í½-[v>-ÆÞࡸÉÕºãÉ«cÉçz1~¸†Œx@o­ýV»1²i`Kó® NwJô9Ûÿw5ià@ 4·á÷¼«’?©Go®g$.úBï¼µAê8FƒL–?wJÌ¿J"FiÂý=•ûîD/j\ŠŠþö7çÞà6&ü¶~öYgÚÞÛrY0.[ ›„b#bУZ²|¾¦�ê©ÀýŸkî¯nÓè‡>Rµ3ZÏ@EÀ´·>¨W'äÓúü™‘åë8pŽ6ºõçfÿ+Î)þðÕ1 üû ÍH:Õ>×3#Ý}>Ck;MÑ«ÿgjù XçG :ýø~� ߤ?¼¹¦~]»?rÄí2ýÿþõ/�Ø·OQQ óMçÞ–m½U¶6¿ ÅŽÐ^Š{ð ­üÇbýæÚ@þç3zë´'ôÖ¥ê@[/�~”­—ì7ÿšª´• *.ŸÃý¾ÚwNñ¦5µŸsìSi ­Ô•Zò¼yÚ�uÎT}þŽ&½4!á&éã9šû±}�€K� ¿Ï<ã–|l¹,Ãü"­™é´v¾°Ö �M:é?'Œ2%ÊÉ9ì«»àŠ´ÕžLøcÙS‹Oå´ûß©§~hJþ·r«ïšc[xg®©£›õ…‚\�®–‰¸ú|ÓÿV:Oø˜þwÓçu>ÿàAtÏ\}þù™Žêø{qèo~ã–|l¹ìcxƒ_„âà¾7é&“ÿþÎÇúÖIeEúâãµæ>PMmÅW°v®Þüg ®�~‡®>MwäÓîð ÝñÿuTɲ7õ�o|´LËÌü›nê+ßxÏuØ_¢óþµ€Ù¿ Óû*pÙÍýÂÑ‹¾˜«9ËNõü;éÇ÷�Q¸[.W êÜÿ 8X—µiã ¨•;q¢so˶Þ*[ ›tŸ6¡ìÉ¥¯jBà»úåp{ŽìpÍXÛIS^]ªO—HëTõœâ�Ч=¶â1·Í)Кí¿Tv %WÕsŠÍmÎÆzìõšú†æß¬ewýÈ·þ7�iÔ õ›AuEâÙÁŸõ¿kUm@ëó¢SÜïµð7WkÓLßó9ó[]göï¹jÏ¿²&½&(Á~\B‚òçþx^žö%$è»Gq¦í½-ÛiÛZ\¶¶F' wú¼))9ÝðÍÆÆ95é]·PΞ—û’Îâ”c‘••¥~ýú¹%¸ôedd(::Ú-Þd¯C\vY¦êl nv×]ú·›ìI†¸/\ã›§B1œ B1€††P øØîÒö²K•G™¶]¦m ±‰¡ü¡@CC(àï.d(ö¯K2pŠžE(x¡àY~Š·$'*)ã°[Ú©OÞJTò6·¨ú(ñ=¥íw‹ÕmKVâ{iªkv ûÓôÞ™, ÛÌ~Wìç¹wæë߯´÷NñZªð‹PÜ®u3~—ë+ìߣý'¤ïvîô•MýþËZª]K_ñ{k£Ÿþ4F¾ÕmSrb²ùß?u‰�Wl·Pû¥@yrn©˜ŸþT1çêµ€î²Ç wú¼9q¤ÜSüA�¾úê°ZGw”¾Z¯¯N†èò‚cjiË›×ëÿêJ îÒÊä }°b¥R×gê«�'Þ­­þmÿ6eî<¢Âm©úôóLmÉ Td—Ö²xïX­¿®HQúS¿ç„ºÚåÓôÞ»~Õ1­NüT¹:¬�™ÛTÜñ*uЭþë ¥¤oPæ¶B5멾},ó]z’Þÿèsmø*WÅæ±?ë£.6„Ö¶of‰«ÿª)éÚ�¹E{N„©kÛ3 ûêÿjê3³¶©0¤³"[)í½wôù¶�ÊLÛ¡ >]´?9Qé²ë·á=M; ÿ¯VÛÇlÉU`dµÞiñ§¹Òaó˜mÅêxÕ¿)ó½´;Ü<— »KhÅÊT­ÏÌÒ¶ÂuŽl¡ç‹€�´mÿv¥¯NSÆW;u"¼›œÝP«}ûö©mÛ¶n .}999 sKà.»ì2wêüó�sŠÃÛ©uÉwÚs¸D;s+<ºŸÂeË&Øí7á´u;³P®>ýd‡š ¾Cññwè†f[ôiY—ë—©óÍñŠ¿#Ví§kõWöºÈû•™±G­íò¶¾8«byGÅÆß`¶®âmëj‰¾ú$MÅÑqfýñúi·B}šæ¶V—ÉýTŸl Ò€ŸšmÝÚGÍʳ~û¶?S{Zkðñº#¶�г>•­.ùê­>ÜM?µõÿÙQûWjâ¸O`ç5.>Öì]u…:Ñ.ÖYl»Ã&Ð~¥’.¦|C¸yýnP|yë·ËÙ×ËÔÇîë7«ÝwiZ½­ìzÑæ¾�ÝŽYWÇee–m¼ÅOÚj§v­ õÝžÚù]kuì®.‹•»s�r÷ªu»¦&`îPnñamK~K‰‰oé“%:\èž<ÔL­Í"º,\}:7Óþ\Û»©Â›]¦ï2Ó”±ã„¢¯¸~v¡ºì4Û.QnÚ{fý‰z/c¿N~§Ê1ºÄô’vW©›»­ð²ÕÕµoMÃÕì²ï”™–¡'¢õ“ø8Ù]عó;…w‰VÓËÌjZP\üÍêì®*0¨®} TӖΆÞ§³šíÏ5Q¼n…;ÍÜÎýÊ÷µßUÍô�­s©™ïS¸}§iÉ€†ÊOBq ÂMà;üÕWú®uGu45á&îÌRaqkµ ÷-ejuC|¼Ó’kowÜh—¬ªiÓ ·»v :ß|«ânî¦ÖÅ;´ú½EZ]­á·¦¦êfÂsÙúããú™š %Å%º,ÐvÌ®M-ûØY7ß§›»µvºr¿·hµ‰Þç@`�Ís$ÊÀ÷ã'¡XjÙ®¥Š Õ4¼£s>°:vTËÜ\å¶n'ÛyZ-;+<è;mÛâk»-ùn§ö:“2Ôw¶úÄ~¥oÉUËðÖ¶�²>Hü@_�h©ŽÑ}Ô­å íwª®DÅÅöÞ„ñÖ‡µç«\'lž8l[Žm}…¦íZë²�™rvá„Ù·²ÕÕ±o%_} Ä¾Ò‰–ݧ›Zšý³»Ð±ckånËrZ¡OìOWRâGåݧëV¢Ãû�G(7ã+}×2\æYú”Ëy •4ë.íÈ(ß׌¯¾3¯Kù· ÃoB± Á­ÕL�þ¾N… ©&ˆšðçk› × 7^¥™¾îÍ‹>ÉRnÙ)²-›jÿGo)ñ­¿i›¢mÖx•np™¾zÏvi~O™'L¹F÷éŽêÖî°2ÞIRFa ®ºq°Zç~¤·ÌúßJJ׎ÃÕ¢fx? °çó¾—¨Äw²t™ÓÙ™Që¾^uƒ\ö•Þ{Ë,ÿ^¦N˜²Ý…À«nÔà¦[L6Ûù`§Z¾¡¼ûô©þ*Ùéž�¼#PÑ1ѾVìŽÝÔîp†ÞIÊPÙwŽðtc·Ê´ûúÖGÚÓz°n¼ª®Vnð¦F' wú¼)))K¯8;vÄèL5û —[.†¬¬,õë×Ï-À¥/##CÑÑÑn üO`�§­ž{þÓR ÀF(¾$ØËGÑJ ç¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠ„b8�æÍ›ëÀ�n ü_]�زŸgÍš5s¦R£“†;}Þ”””¸Spé)--ÕÎ�;�ƒÈ-Z(((È�—ÛBlqaa¡:v쨀€wøŸÀÀ@wêü#@=Ø`¼ÿ~ç`òرcn-\:l—iûå^Ë–- Äü¡àY2sN1À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð,B1À³�4ÜéóæÀ�î§Ö¢E wêü» ¡D÷i€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡ØÏe¾6Y“'OÖk™nàœitÒp§/²\%Ï|LIyn±º°8=qËv=6÷Kßô“± wgù�üuz{A’Rwè¸)6í¬�qu{ÿV¾ùgÁ†bû”{O™§©}ÜJÀ9AKñ¹’›¬§~³@kÜ@l/Ø¡5 ~£§’sÝšÓÉÔk“mËðkfʧÏÔyš7�@ 烵WfÃá\}©Þš2oªÊó`ækšì´Òøž»ôö';t¼qgÝ8í>Ýu…o™#Û•¼`®–o:dÂic³èM»½—šUZçø{›)yîåo¬Îq¿Õ´k¶ë�çßÖ—‡¤�žc4í¾Án+ômO^ ¹Ë7é�Iº�Ív§L»]½š93+)Õº—ïÑ‚MRPÏñztÊ@µ (Unê\=·p“ŠÕS_½OVÍÔcIyjã8EmzWkòÌÚuÎ0ë¼¢lÿÊ„)î‰'þ~õ–âBm|g®®ÙáۧО9q¢b�ç_ñ'>¥UO%i‡yŽö5˜a^çªöú„ö¼ES¦ U‡;¼åÒl)Î[£…6Ûéã;ôÉ‚åÚåÌÈUòìç•ä>ë¸YôÍ®ÒRû¥¾b±�>®I/ë±ß/4�ØVסM‹”¸ºÐÎTnòl=Ÿä ÄÖq³ÝWf'›­T·]›¶Ûûö9Þb; ð�ã×ÙNoRæ{ï³û“EN ¶œu.H5ñ»~¶$>¦WÌs/ß§‚MJz~¶ª6FoÕÛÏÙ@l§ík°@ï:Û/Ôê¹U_ŸΟž»ÎÄzðžK3‡ Ò¯^�§y/�WO[.تMùæ~K²’w›ûžãõÂ<3ÿ…qêjŠykR+Ù úÕ«š7ï ³å5¾æQ½j–ÂW¡›v˜ÿ·(Ù·2�Áva~Aã|+Sj�T\¬#Åö>T¡UZ‘›©YˆoªÔ ±Ž®ãœý{õÑá&F›Öh]aM�7E½�l ù“Š­~Òté:}˜f7¦á�Úçð‚&ö2åÝf_+¥nÈ{Æ¿`æ¿äÎ/Ööv§whÓVsã{ýžnÖdlZgb;xÏ¥ŠƒBÕ̶Æ^¥®Nª+µ�ž*ÌÍ5ñÏØ´PÚss\$›U�[)™àj®(7t†Gu01ÒÞWJ¡…æ1¾•iáƒö<ߵȷ2™Í|?A&,›»€C5ÈiIΕ“YO§ @6û+l :ý�›©ÿÐ>æ™ØkvÊ×¾m©C¸ÝÂêå¦rG¸ºµolNÓËO½¬7V7Ó˜‡^Ы•»¨€‡4¨�¶J‹Ïa'àÒâ3èR¤+l25�¹ "™…*<ä› 0Y´¦+àœèk¶uAú/‡kèoçè…‡îÕ-=ƒT¼é}½òüƒºç©Úº„@Ã× Bq«ÎœVSu£gl÷`Û…úÕ—ôÒ«gÑÚª³:øV¦1ÏØ®Êv}¯ê¥—^­e$è(õŒ²÷»µ|aªr�€kÚZ¨$Û[=Õ§›½w›°lï ×i½Óo¹½:Té*]¬#µ�d*çâNy©JÝe7R¨u«2�Öñ ðp§õù”r“5sò=z0)O×Ür—îûm¼¯ûùîí„bžÔ B±ºÅ*Öž¤»ã]ýæÛåÙÜîy@œÕ@RÝë[™ÞýÍ=¾u™@ùÀsµ®ÆÊÔLœs~pñ¦…zÌÙö=zÌyÚDÞ¸1êï ¾åÚºÈíÞ½Ðw.oçAŠqÒnˆB�ÞÎ[µð�Ézc£�®$ ¿†Çؤž§Ÿ¶ûô |élÁìkåÔ]‡ðžêjî·¾«í>Þ³ÀÝ~OýÀ{V(V¸b§=¤8“ü|½•+¨}o�ÑÓ9gøL…ÇNÓCq=êv}nÔ^½ÇŒPÏÚV«ß>3Qƒ:—mÛ^.©³M|F¿­>bV× s÷0l�î½o°ÛÊ¥‘·Ç(ęոÖ}îÿ„îÔ^AeûÚSqM«9(W­:èÖ‡îÕ öAå¯OhÏ8=T¾}ð?½NqÔ›ì»N±zOѼš}°Xk) þÅÏ¢û4À³h)x¡àY„b€gŠžuAÚ:pà€;À©µhÑÂ�:ÿ.H(...v§8µ   wêü£û4À³ÅÏ"<‹P ð,B1À³.ÞèÓYo*áÍ, ¸GÏŽëâTå}©]œžœ>X!Nm¥úè š3¡�>žõ¬l±²è s4¡l帴¹ï¡2�ƒÚiÀ�“5¦‡}GdéÍ„7Íÿ•µÑˆ‡'Hožâ}Qš®×§/Òæ–ƒ5}fœÚÙ™ÎvòÌcÑMa¶ÂV%èͼzø‘›dÞhæ}§JókÛ¶]å½�ÚÒg/ÍÔ_Š*½þÊóý.”ý¾g+é·s´º¨»î|ñnù>Üeäû™¸?&í³Ÿ î+Ÿ÷±f™X˜ó3¯ë}Rñ³—�*çMÉþX ÿœ¬Íû�ÛƒEµ»úgš0îjµt·ê—-ìqÄ~}±èu-Jß§ãj¬àN×kÜ„8ù=Ou\Zùx¤îŒ2 ½úñe�…4g@ú)¶Ó°xjôéâôwµ<Ï-Ô�­ _9S›Ó³TêLU²']¹ÓÚ£ÔÔjo«�ycÍ™£9î�@ÜðØ‘9s^Ô##‚µaÞ³Z´Í�aÙ�òŸ¥ SÇû¢tÃÚl'ög)}�Suløµë}X#Ú˜bù¶ħ¢è�Ìëÿµ¾.û\8´MYöïÔ€¾¾?RÙäûXجô¬Ÿ g§®÷ ¸„mSÒ¼ú¶Ý8=iÿÆÏŒSpúŸõúÇu„�Sf‹R}½ðEý9+Lãž4ëzñÅ¥kÞ³‹ÌV*œò¸´²zeûE}Å2å_ðõÞêźOïÓªEŸ©üýW™sð¤.]ÚÙT¬šÇ¿{”^öÎ5oâ¬ýAº€_(À¯¨åõ±\¬ôϾvëÎT©6|a"q».ê´_YgŸŠñ=„DP'ó¹�µÍ÷»]º9Kߪ�ôõ}š½á ™Ÿ‘ó±PË—eŽR•Vî°2@w›pùH�ß~Ÿ"[JWò†bµ:BWÛC’€–º>v€‚‹ÓUóÐó\—ÖÇ…ÚNÃwÑCqôˆj÷í ý勚]¬�ƒ_uÑàŸõU›­Bm4`@;ó~Ýàê=æàx§hEûæ–Û·BÏ&$(ÁÞ*u5@C¤`ûÁµÅ—,¶+‹ûóŸUù[ÁÊï‹YË™SºA6·éû3 îb‹ÓÍG#.¸�hÙÆâo³6Û?eÊr¾¨ _&v{�t¬ŸõmSÇ—eg¡®÷ ¸„õPìˆv*Íú³fþz–^O2Çvå‰Õ�&[˦•°°J�:(Ø}JÇ�ûŠUÕr\ZYmÇ¢5ìÓŠgÝej=5Ï:ÍvP/¿¥8ì&ýlp�²þ’¤mUr±{ð=@Ña=Ô£eÍV¡6}¨�ÓÍa� ÌûÕiÀóv®¦r×úN{O¥n±U¾¬ü¾pÏAõu�6ˆÑaêÞ·»MÅߣ 5Ξۅzs–6—nÖ'Wí:= Za=z¨å¹êB]×û\ÒÂnš®ggNЈè íùl‘^œù¢êúþ»^Ùâ\©åX´¦Êݧ9 ï|ò‹Ñ§#ãÆi€Òµ"Õwþ°£ì¼A§çE­Þo¦«· µì«��Ì›ö/Éúbw]? z31¼¥XEö‹•–-ËH¨?·ë´û�Üô?ÛéÊ]¨�;ß–9�“¨›¯ u–ùÝÎÒ¶ê]§Í½è,áÅÕæ'Tß.Ô•—(6?Mà-£uÓøôÔ³“Õ7h�V$×ÑÕøTÙ"(ÈiÎË«”¨‹‹œãÃÆ�}Ū¾Ïq陸PÛiØü"K]×½Ê(Õ{6d™ƒß²‹Ìíᡵ´ …hÀõݵ'Ë,Û}€úú§†'•jÿgÉJ/ 2ï‰nÝ(Íòu�ñ°ûmÜÝív¡îÔݼC÷kê¯�n)¥û?Sú6óÙYÇ·zøÞÜ.ÔYéé*.ï:½G²l3qE«îÃC�.$§èB¦.]ÌŸ°¬ÕZ½ß.tHÛVg™Ÿf'u7ë Ø¡Ïôª=5jùß×ã&kØ/ƃê §È!Û7HûV­Ð¾B}–œ®¢ ªyèù=�KëíBm§áó“PlÞœÆég婾. ê­în�ÂzÈÛn¶})+ è{µ³L÷Ѫõí]¹¿¾¹qZqÃã´&L׬Eê;ùa¹Wøò©t®hB¬:»Ë”šð»YÁêÑ¥"æv±©ÉŽ‚\|½&$ŒP›moj¦YÏôY+T}§¦WÙPås>Ì�7Ú÷äv¡6Ê»N;^Ø�…òOó±ÐEAæ'Wþ±PËï{ä˜éºsÀq%ÏšnêfêõÍ-5"án]_ùëÔz¾OÀ%$Äw ×rÃKšnÿÆÏ\¤=�Fhr\åc¸ªêÎê1ÞSDçiÑL³®é³´¢¨¯&? ð,B1À³ÅÏ"<‹P ð,B1À³ÅÏâ:ÅüZII‰òóóuàÀ=zÔ­€KÇå—_®-Z¨U«V tkþ‚P ÀoÙ@¼}ûv…„„8”AAAî¸t;_ì:tHQQQcð3„b~kïÞ½Î}Û¶m�{¸”ñ™þÉÎ)Î×ò{¯Ñ5×Ô~›�%eͶÓ÷jy¾ûG–fÛeî]nÖ`‹³k<Ö¹ùVà–G*q‹óà [5Ò]Ö.Z÷þTß>ü‹ïýàûºï™²ÂEWußP¶eŶ@C`?ÏìçÀ¿øA(n¥‘¯¬×úõæ–ü˜blÕmò•ÍmZ´³P½Å<–\þXçVe9ZšV5oI[jjks›þTy=ë_ÑÈVî,ø½èiÕöu±�õûá‘¿ü^? á ‡=‡˜.Ó ûyÆØÀÅQòÍ7úîw¿ÓöáÃËo¶lëÏ�>�³4M±8[WÖ‰‡ÓË`¶‰Î¸­\©ÝS§ªè£�Ü[vêÍ|x›§Bq¿Áƒ‘³Tå�ÅÙZº5P11ýÜ 4U»OÓ–E÷jø@S7$^³?\ªÇ«Ý¢ŒEºwø@€‡(~VŠœ³¾l þùb3±X?¯OkrQ†Ý;\¯¹FCâg)ÅwêX5¾îù÷VZ™ÓÊ\v€Ù×ìå³?ÄìëÀኟ�êÖàL9-ijg;Ó�Û´Ñ®¸Â™¶÷¶lÙù´{Û%ŠÓôD¬=··ìösUÎLà�a‘£•³�rvÆRm�˜¤Ã²!;–1[?_©Ç>\¯õ)¯hDAŠV¸ó|2”V2P¿û0Uëÿ1[Ñi�(1ã˜íƒ­õºÍÌ·]éëÑ}>k¯"ûP©ë?SâÈýú‘¥¾p}ŠRžÐ¸ùMLh6ûšú'�̾OÏ~XäÎÀ™8˜”äÜÛÜ~î\µýÝïœi{oËeÁ¸l9xÓ%ŠcôXrÙù½öö'UjJ{"¶Rp®Þí5Z1£#´ui†²M\ÉX¾U£cLmmlë`E¯Ü²‡KËÆ”÷?Zƒm)XÝ¢#�ú ý4,&RMìd°™R¢œ½gDŽÔ@'87QäˆÛtËÆ•Ê8£T\¤Œ•+�}íçìk[ £Õ«3Ìœ©Ãn—éÿ÷¯éx^ž¯¼Rßz˹·e[o•-ojpݧ«´5­Zà T·˜ÑŠØºT)iZ¾1B£cº™ÚÚThëFÙºDåko¶ØÔ-OùËuo¥/K*º=WÓ$ØDã m-pËõrLE&ýn}ö§åë�}"Ídå"3gˆßÜÇwK>¶\Šámžhˤb�ŽØªWE#FËdb4hM,•˜çD«‘z¥Ò—%ë_©Ú¿.±Û‹0aÜWªìT�õí¼˜gª� ^ç6¼n­žkÔHÏ­uKÏ5R£²ÂEWußÀÅeÏ!¯Šm¹ìcx›÷B±º9]¨>ìt�&7tÁÎk‰‹•áôA.RVÆŒ%j¢í±úEê”åJq›�÷.OÔÒNµ}éÒJ‘ý"”‘µÕ×%úX¶ÒVf8sì¼�£‡)cÅJmqûKÛ›eOUF=\7ã¤NθÎ-�Š ¬£´$×-ž¥Ü%£ü(„€Ú4½ùfçþÁÁjæ ¨µóÎ;�{[¶õVÙr𦊫žSlnµ\;ÖéBmþÙ®Óu«zNñ5çàZ¶¸8‚‡?¬y#·jÚ�ÌÏqȽZ¹7Ô�S݆éžÁ+5ùšqZ䟭Nýn¨‚ßûFŸ·4BϾ­Ö/]ºÝö;ݓ󄆛å†L^¤àa£Ý9f_‡<¦EòõìHûž¨Ÿ<²\Ù%tž¾ Ö>§F�ž3Ñ\êšÇÅ9÷öüa{ù¥½Ó§;ÓöÞ–í´m-.[ÞÔè¤áNÞà\jIúS�sÎáo222Ô¯_]—L³-¾1RÚIÙbÛ}:FinkñQe¾¯±�¿£-—׳sÆ*;.Wñ'gè:çqÏ©yR¬Rf<®w¶\®á÷¿®×çÄ*Ò☇}«×iqÎ2� w‹Õؖ∔!š{ðu=þÎ]>ü~½þúÅ:ã¸UÞ·\-¡%cs´Ì]™óØ%c•³l¬Â;n^2C 3þ �vÓð„×µdÆ5w–ÐМús Àù`¯C\vY¦êl �2EÁƹ5ð"vŸ†÷SþÞ|w°ª"¥®LQàˆ~ª>5Ž£)�+öõH½¾ù¤Nf/ѨÜešçÎóY«”£±JÜœ«“Õ'%^¯§µ}°u2íY3ÿY“gëÄå2s9g³rO+el¶þãîD�¦CA “4ä¹Ë5#Óìkn²Æ®ý‘–tç€ïËÞö¯½V£‹´-ÛK3ˆA(†(mö}úÉ@Û%y¤fï½Mzxˆ|g� !ÊL~^ÝîÖ§¹µ¹º÷©~®ñu5$R—ÛÉæC4jlž6çžE5J±Np¾\‘ctÿ‡Ë´öŒRñA­]6¯Ò¾F*vìiaòZ3œ+öLm~ýkE}øaùÍ–m=@(†´ÕÈÙ‹ôaªÍ9EKg�S7ç¢Äh˜r•½ÙdÝË�Èûýå.ѨF�Ô¨ì6j‰ÙB-ÌöÂõWeŸÑ^GuÐ,¿úç�Ë×qÛ_Í6š9¸Å˜ËÕÜi½=G±2|¬–�<);ü‚ssήÝAõ¶Ù¸†Sås;ïÖ¤œŠõŸf8·Å˜æº.v¼þ:g‰Ö:}�*smŠ3§^LJímS¯H½d‰’Ý–áì%s”xm¼†ôñ•+„+òºÞZ»6Ó×%úh¶R–•�m®!ñ“•’¸L›ÝþÒGs×*óLOLÀY#hpš�𣴱k5¶E#5Šk‚ë´»ö¥ÇÇ/Ñ� Ñë›Ýº:ü×ݱÊ�Ñ]á�iHb¸æ$Þ­™Øèsw¢ÏMPw³\¤Ù·æ£âÝ9f_cç(eìf%ô±Ý§ÃÕ'~‰6¥ó4À…â—dújù|eµ¹M·^k‡>ÊÖ‡o®TÉ “4Ò¹Èë6ý}~º‚o§¶²åj¶,×üŒæºeÜUÛìòÿ©E+¥aõ]þòÿ¹Hïì§I¾pŽ�³K—8—Z²WH²—d€‹‡K2€ÿñ‹–âmš©pß^_!— NHû²Ýþƒy9*¸,T¾o‚-Óê‡Wˆ·hùüåæÿúÊ×?-Ò?óÝ"?uT¹Ùe] *eÙ…Mî£îN¨pÙã†;}Ñ4ùA¾²6V›>‘RÖZm<ÙL—çU¨-oZ« úw íÖÚäϵ,éïZóy†²²�+âªöjZ°UßþK·¬ÑêÔ }µ7PQÝÂd>²m•þ’ô±ÒÖ›ú]ÇÞÝ,o[Š—íTDôQ­š¿Z9:¬o3¶èH§huÒ6­úK’>N[¯Œ-ÕìÊ(µ ðí£/¿¯¯—(ÿë ímÚOþ•¥¯•è`æ'æ1Úr°™®Œj)û�¼Ï—)éïkôyF–²�GèªöM�–åÿ^Ÿ­¼ŒÕúÇç¦Þ„ýÀMè½�Ó”¹í˜Úõê ¦vSµ=O[xLNNŽÚ¶më–ÎÄn­H«±wÿ\÷N_¢Í]´äÕ±Šlì΀‹äì?×狜Sܶ½Ú”äiWÑ1eçVDŸj+[6ù° PÍÚt0 íUÊÊoÔ|è/4iÒ/4¤ù×JùÜ,`�¸LWþx’&ýb„:¥iUÖ1S™¯Œô] ³ËÛú#_V,ï覑“+Âü<ÉvÍ>¦¬•ŸêHŸ1fý“tÛUµúŸ•G»i¥Ž»E=š6U�[ʺvǃÕ{ŒÙöm7(8;]™v°œ½)ZùMs ý…ݧ!jþuŠÊ7}ÅóPO‘›˜¢Í¹v4çl­µçúž£+4 añ“�¶:¨C›BíÛõ�²÷µQdd[u�,VNö.å*¬C°É¸ßhoñam]ñGÍŸÿG:Ktø`�ïáAÍÕÆžŽÜ¸­ú]ÙL{sL!XÍ/S^Ƨúü›ê3z’{Îr]²Í¶K”óéb³þùZœ^ ótÚ8zE¨ZÙÖ§à6jdòùq»«{U|x«Vüq¾æÿq¥¾)9¬²]µË77w�;´7÷mÔõ*»OÁjߦ©ŠöåŸúyÎ)? ÅMjBaV–öµ‰T¤©i¡ƒÙ™:XÜFíË{ÙVÝI²-¹ö6a¸]²ªàà+tÂ&S³Î.?¾Ccþ£‡ÂŠ¿ÑªÅ‰ZuÚËœøZ�ËÖ?éÖkM\=Kƒ+Ö3i‚jÙÕS8ýó|~sI¦VBU\X¨¦‘ÎùÀŠŒThNŽrÚt�í<­VWªmÐ>mýÊ×v{,/[»Üëzªø öÙêãùJý:G¡mÃÌYZ6™6žUdŸ~êzBùyµµû–¨øˆ½7a¼ÍaíÚ¸W6R/ÊVvž­¯®D%ÅîdZ]ÙVAû¶Ê·«Ç”—½Ëw}Òú8ÕóœS~Šmn£fŠtº;&¤JMC#|!Ym5dX/�Èðuo^¸2S{í©ÃVh° þþ¦æÿñ}mUoÝÐǬ£I´†Ä\¦¬Å¶òbeœè¥!5ºOGªGû"¥ÿ÷;úü`E»Qa{ÿG4ëÿ㻩ú¦ÈIË•´R�+ƒôÍÿÌ×ß·¹Uµi;DÃz�PÆâùfÛ µ2s¯‰ÆõuŠç 8§üâ:ÅP®ç  ¡ás ü�ÿ´p�ŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY„b€ólòäÉîÀߊÎ#ˆçÍ›ç–þ†P pžˆÀÿŠÎ1\ÅçXõ@Ì9Åà¿ÅüÖå—_®ââb·—†Ú±-ÛÏ3û¹ð/„b~«E‹:pà€[ÿWW ¶ìç™ý\ø—F' wüJII‰¶oß®��ç@2((È�—ÛBlñ¡C‡¥ÀÀ@wÀŠø5Œóóó�Ê£G�ºµpé°]¦í{­Zµ"€"<‹sŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžÕè¤áNŸ7p§8µ-Z¸Sçß ÅÛ·ow§8µ¨¨(wêü» ¡Ä9ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð¬F' wp‘|ºé-ý4SÛö~§=æÖ^úzGµ×ø›®UŸ+Û»5þ…P ™ Ä3ÿ¼Â-5L¿Ÿ4Ú/ƒ1ݧà"[úÏ îTõð£µî”!ÀEöåŽ=îTõ-'ß�ò/„bÀyç¯çIŠžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(xV£“†; ¸n|èîÔÙ »6VÏÿÇ•êÔØ)Þ¿[ÉIKõÚV§è7>yþ~wÊÐR —²ƒôÔÈnj¹wƒxîºñ¹÷´0'D?ó_î.‚ºÑR Ù÷j)¾~´þö“ÖÚö·×õ«ÏÜ:G;õŽÚ£/·KýGŽÖ×¶Wx€©.ý—¾úü#ݳ|¿œö Åß ÿ°Æ÷�‘ãôÉ —)ù¹·ô‚¢õè/®×�mš˜ǵg–žÿ?k´Î·äY¡¥pn}ö�¶7ÑU?£¯o§0·ZòbEÅê�ÚK›þG·=ô¶^2uWÝp½¦ªH ·'µmo¦}¦Fµ–öîÖÂÁšzç ݼ_oÌùƒn[ô�J:öÕ]qÁî’ ¡.i™úÕ¼OôiQˆnøÉOµøù_ê½ûc52Â�½=YãúƒÆ-Þª<åkù–:¬+Ô±·”—’£mj¡«bí‚u•ÉÄÛv®7ËõUŸ¶�•»ý3-Î1Ë}™¬Ì}RŒB1\êr²ôôè'&üÞõîWÚ|¥˜¦ì6û#wNÃA(€KØ?÷Ð{Oÿ\Oé¤+mE‹v𨳵š–Ò¶¯¤–A—I¥Ç”»3ß„ßJècæ9�t¥l×6Eh@GiÛ¦õnåZ¥ï=®ŽQ#ÜnØ­4<îfM­š¿B1\Ê2þª‡–ç¨ã�zÃvužñSý´í¿ôÉòåzí€ Í)_)óhký4ÁÌûåuê¸ÿ;íwê³^Ë·ÿK*Î5˺U*Òk^£OŽ·×öqÏß®z‡«e‹†7Зd€‹ì{]’éÂ%™ð#„b€gŠžE(x¡àY„b€gŠžE(x¡àY„bÀy÷o—ºSþ…P YïÎíÜ©†«KDkwÊ¿Šà"ýþîTÃ5þæëÜ)ÿÒè¤áN.’O7}£¥ÿÜ m9ßé_GKÜÚKŸm·�¸Ï•íÝÿB(xݧžE(x¡àY„b€gŠžE(x¡àY„b€gŠžE(x¡àY�NîôysàÀw €SkÑ¢…;uþ]�P €?¢û4À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð,B1À³ÅÏ"<‹P ð(éÿ¾T§O枬/IEND®B`‚django-commons-django-constance-1032c50/docs/backends.rst000066400000000000000000000130141527040271600233360ustar00rootroot00000000000000.. _backends: .. highlight:: python Backends ======== Constance ships with a bunch of backends that are used to store the configuration values. By default it uses the Redis backend. To override the default please set the :setting:`CONSTANCE_BACKEND` setting to the appropriate dotted path. Configuration values are stored in JSON format and automatically serialized/deserialized on access. Redis ----- The configuration values are stored in a redis store and retrieved using the `redis-py`_ library. Please install it like this:: pip install django-constance[redis] Configuration is simple and defaults to the following value, you don't have to add it to your project settings:: CONSTANCE_BACKEND = 'constance.backends.redisd.RedisBackend' Default redis backend retrieves values every time. There is another redis backend with local cache. `CachingRedisBackend` stores the value from a redis to memory at first access and checks a value ttl at next. Configuration installation is simple:: CONSTANCE_BACKEND = 'constance.backends.redisd.CachingRedisBackend' # optionally set a value ttl CONSTANCE_REDIS_CACHE_TIMEOUT = 60 .. _`redis-py`: https://pypi.org/project/redis/ Settings ^^^^^^^^ There are a couple of options: ``CONSTANCE_REDIS_CONNECTION`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A dictionary of parameters to pass to the to Redis client, e.g.:: CONSTANCE_REDIS_CONNECTION = { 'host': 'localhost', 'port': 6379, 'db': 0, } Alternatively you can use a URL to do the same:: CONSTANCE_REDIS_CONNECTION = 'redis://username:password@localhost:6379/0' ``CONSTANCE_REDIS_CONNECTION_CLASS`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An (optional) dotted import path to a connection to use, e.g.:: CONSTANCE_REDIS_CONNECTION_CLASS = 'myproject.myapp.mockup.Connection' If you are using `django-redis `_, feel free to use the ``CONSTANCE_REDIS_CONNECTION_CLASS`` setting to define a callable that returns a redis connection, e.g.:: CONSTANCE_REDIS_CONNECTION_CLASS = 'django_redis.get_redis_connection' ``CONSTANCE_REDIS_PREFIX`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ The (optional) prefix to be used for the key when storing in the Redis database. Defaults to ``'constance:'``. E.g.:: CONSTANCE_REDIS_PREFIX = 'constance:myproject:' ``CONSTANCE_REDIS_CACHE_TIMEOUT`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The (optional) ttl of values in seconds used by `CachingRedisBackend` for storing in a local cache. Defaults to `60` seconds. Database -------- Database backend stores configuration values in a standard Django model. You must set the ``CONSTANCE_BACKEND`` Django setting to:: CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' Please make sure to apply the database migrations:: python manage.py migrate .. note:: If you're upgrading Constance to 1.0 and use Django 1.7 or higher please make sure to let the migration system know that you've already created the tables for the database backend. You can do that using the ``--fake`` option of the migrate command:: python manage.py migrate database --fake Just like the Redis backend you can set an optional prefix that is used during database interactions (it defaults to an empty string, ``''``). To use something else do this:: CONSTANCE_DATABASE_PREFIX = 'constance:myproject:' Caching ^^^^^^^ The database backend has the ability to automatically cache the config values and clear them when saving. Assuming you have a :setting:`CACHES` setting set you only need to set the the :setting:`CONSTANCE_DATABASE_CACHE_BACKEND` setting to the name of the configured cache backend to enable this feature, e.g. "default":: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CONSTANCE_DATABASE_CACHE_BACKEND = 'default' .. warning:: The cache feature won't work with a cache backend that is incompatible with cross-process caching like the local memory cache backend included in Django because correct cache invalidation can't be guaranteed. If you try this, Constance will throw an error and refuse to let your application start. You can work around this by subclassing ``constance.backends.database.DatabaseBackend`` and overriding `__init__` to remove the check. You'll want to consult the source code for that function to see exactly how. We're deliberately being vague about this, because it's dangerous; the behavior is undefined, and could even cause your app to crash. Nevertheless, there are some limited circumstances in which this could be useful, but please think carefully before going down this path. .. note:: By default Constance will autofill the cache on startup and after saving any of the config values. If you want to disable the cache simply set the :setting:`CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT` setting to ``None``. Memory ------ The configuration values are stored in a memory and do not persist between process restarts. In order to use this backend you must set the ``CONSTANCE_BACKEND`` Django setting to:: CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend' The main purpose of this one is to be used mostly for testing/developing means, so make sure you intentionally use it on production environments. django-commons-django-constance-1032c50/docs/changes.rst000066400000000000000000000260151527040271600232010ustar00rootroot00000000000000Changelog --------- Starting with version 4.0.0, the changelog is maintained at the GitHub releases `GitHub releases`_ .. _GitHub releases: https://github.com/django-commons/django-constance/releases v4.0.0 (2024/08/21) ~~~~~~~~~~~~~~~~~~~ * Replace `pickle` with JSON for the database backend * Fix migration on MySQL * Fix data loss using `DatabaseBackend` when the DB connection is unstable * Fix typos in the documentation * Fix small HTML errors * Drop support for legacy Django versions * Migrate JavaScript to ES2015 * Fix documentation build * Add linters and formatters (using `ruff`) * Add support for Django 5.1 and 5.2 * Migrate from `setup.py` to `pyproject.toml` * Bump `tox` * Declare support for Python 3.12 v3.1.0 (2023/08/21) ~~~~~~~~~~~~~~~~~~~ * Add support for using a subdirectory of `MEDIA_ROOT` for file fields * Remove pypy from tox tests v3.0.0 (2023/07/27) ~~~~~~~~~~~~~~~~~~~ * Refactor database backend Backward incompatible changes: remove ``'constance.backends.database'`` from ``INSTALLED_APPS`` * Dropped support for python < 3.7 and django < 3.2 * Example app now supports django 4.1 * Add support for django 4.2 * Forward the request when saving the admin changelist form v2.9.1 (2022/08/11) ~~~~~~~~~~~~~~~~~~~ * Add support for gettext in fieldset headers * Add support for Django 4.1 * Fix text format for MultiValueField usage v2.9.0 (2022/03/11) ~~~~~~~~~~~~~~~~~~~ * Added arabic translation * Add concrete_model class attribute to fake admin model * Added tests for django 3.2 * Fix do not detect datetime fields as date type * Added support for python 3.10 * Fixes for Ukrainian locale * Added documentation for constance_dbs config * Add caching redis backend * Serialize according to widget * Add default_auto_field to database backend v2.8.0 (2020/11/19) ~~~~~~~~~~~~~~~~~~~ * Prevent reset to default for file field * Fields_list can be a dictionary, when a fieldset is defined as collapsible * Create and add fa language translations files * Respect other classes added by admin templates * Removed deprecated url() * Use gettext_lazy instead of ugettext_lazy * Updated python and django version support v2.7.0 (2020/06/22) ~~~~~~~~~~~~~~~~~~~ * Deleted south migrations * Improve grammar of documentation index file * Simplify documentation installation section * Fix IntegrityError after 2.5.0 release (Allow concurrent calls to `DatabaseBackend.set()` method) * Make groups of fieldsets collapsable * Allow override_config for pytest * Put back wheel generation in travis * Fix wrong "is modified" in admin for multi line strings * Switch md5 to sha256 * Fix Attempts to change config values fail silently and appear to succeed when user does not have change permissions * Make constance app verbose name translatable * Update example project for Django>2 * Add anchors in admin for constance settings * Added a sticky footer in django constance admin * Add memory backend * Added Ukrainian locale * Added lazy checks for pytest v2.6.0 (2020/01/29) ~~~~~~~~~~~~~~~~~~~ * Drop support py<3.5 django<2.2 * Set pickle protocol version for the Redis backend * Add a command to delete stale records v2.5.0 (2019/12/23) ~~~~~~~~~~~~~~~~~~~ * Made results table responsive for Django 2 admin * Add a Django system check that CONFIG_FIELDSETS accounts for all of CONFIG * Rewrite set() method of database backend to reduce number of queries * Fixed "can't compare offset-naive and offset-aware datetimes" when USE_TZ = True * Fixed compatibility issue with Django 3.0 due to django.utils.six * Add Turkish language v2.4.0 (2019/03/16) ~~~~~~~~~~~~~~~~~~~ * Show not existing fields in field_list * Drop Django<1.11 and 2.0, fix tests vs Django 2.2b * Fixed "Reset to default" button with constants whose name contains a space * Use default_storage to save file * Allow null & blank for PickleField * Removed Python 3.4 since is not longer supported v2.3.1 (2018/09/20) ~~~~~~~~~~~~~~~~~~~ * Fixes javascript typo. v2.3.0 (2018/09/13) ~~~~~~~~~~~~~~~~~~~ * Added zh_Hans translation. * Fixed TestAdmin.test_linebreaks() due to linebreaksbr() behavior change on Django 2.1 * Improved chinese translation * Fix bug of can't change permission chang_config's name * Improve consistency of reset value handling for `date` * Drop support for Python 3.3 * Added official Django 2.0 support. * Added support for Django 2.1 v2.2.0 (2018/03/23) ~~~~~~~~~~~~~~~~~~~ * Fix ConstanceForm validation. * `CONSTANCE_DBS` setting for directing constance permissions/content_type settings to certain DBs only. * Added config labels. * Updated italian translations. * Fix `CONSTANCE_CONFIG_FIELDSETS` mismatch issue. v2.1.0 (2018/02/07) ~~~~~~~~~~~~~~~~~~~ * Move inline JavaScript to constance.js. * Remove translation from the app name. * Added file uploads. * Update information on template context processors. * Allow running set while database is not created. * Moved inline css/javascripts out to their own files. * Add French translations. * Add testing for all supported Python and Django versions. * Preserve sorting from fieldset config. * Added datetime.timedelta support. * Added Estonian translations. * Account for server timezone for Date object. v2.0.0 (2017/02/17) ~~~~~~~~~~~~~~~~~~~ * **BACKWARD INCOMPATIBLE** Added the old value to the config_updated signal. * Added a `get_changelist_form` hook in the ModelAdmin. * Fix create_perm in apps.py to use database alias given by the post_migrate signal. * Added tests for django 1.11. * Fix Reset to default to work with boolean/checkboxes. * Fix handling of MultiValueField's (eg SplitDateTimeField) on the command line. v1.3.4 (2016/12/23) ~~~~~~~~~~~~~~~~~~~ * Fix config ordering issue * Added localize to check modified flag * Allow to rename Constance in Admin * Preserve line breaks in default value * Added functionality from django-constance-cli * Added "Reset to default" feature v1.3.3 (2016/09/17) ~~~~~~~~~~~~~~~~~~~ * Revert broken release v1.3.2 (2016/09/17) ~~~~~~~~~~~~~~~~~~~ * Fixes a bug where the signal was sent for fields without changes v1.3.1 (2016/09/15) ~~~~~~~~~~~~~~~~~~~ * Improved the signal path to avoid import errors * Improved the admin layout when using fieldsets v1.3 (2016/09/14) ~~~~~~~~~~~~~~~~~ * **BACKWARD INCOMPATIBLE** Dropped support for Django < 1.8). * Added ordering constance fields using OrderedDict * Added a signal when updating constance fields v1.2.1 (2016/09/1) ~~~~~~~~~~~~~~~~~~ * Added some fixes to small bugs * Fix cache when key changes * Upgrade django_redis connection string * Autofill cache key if key is missing * Added support for fieldsets v1.2 (2016/05/14) ~~~~~~~~~~~~~~~~~ * Custom Fields were added as a new feature * Added documentation on how to use Custom settings form * Introduced ``CONSTANCE_IGNORE_ADMIN_VERSION_CHECK`` * Improved documentation for ``CONSTANCE_ADDITIONAL_FIELDS`` v1.1.2 (2016/02/08) ~~~~~~~~~~~~~~~~~~~ * Moved to Jazzband organization (https://github.com/jazzband/django-constance) * Added Custom Fields * Added Django 1.9 support to tests * Fixes icons for Django 1.9 admin v1.1.1 (2015/10/01) ~~~~~~~~~~~~~~~~~~~ * Fixed a regression in the 1.1 release that prevented the rendering of the admin view with constance values when using the context processor at the same time. v1.1 (2015/09/24) ~~~~~~~~~~~~~~~~~ * **BACKWARD INCOMPATIBLE** Dropped support for Python 2.6 The supported versions are 2.7, 3.3 (on Django < 1.9) and 3.4. * **BACKWARD INCOMPATIBLE** Dropped support for Django 1.4, 1.5 and 1.6 The supported versions are 1.7, 1.8 and the upcoming 1.9 release * Added compatibility to Django 1.8 and 1.9. * Added Spanish and Chinese (``zh_CN``) translations. * Added :class:`override_config` decorator/context manager for easy :doc:`testing `. * Added the ability to use linebreaks in config value help texts. * Various testing fixes. v1.0.1 (2015/01/07) ~~~~~~~~~~~~~~~~~~~ * Fixed issue with import time side effect on Django >= 1.7. v1.0 (2014/12/04) ~~~~~~~~~~~~~~~~~ * Added docs and set up Read The Docs project: https://django-constance.readthedocs.io/ * Set up Transifex project for easier translations: https://www.transifex.com/projects/p/django-constance * Added autofill feature for the database backend cache which is enabled by default. * Added Django>=1.7 migrations and moved South migrations to own folder. Please upgrade to South>=1.0 to use the new South migration location. For Django 1.7 users that means running the following to fake the migration:: django-admin.py migrate database --fake * Added consistency check when saving config values in the admin to prevent accidentally overwriting other users' changes. * Fixed issue with South migration that would break on MySQL. * Fix compatibility with Django 1.6 and 1.7 and current master (to be 1.8). * Fixed clearing database cache en masse by applying prefix correctly. * Fixed a few translation related issues. * Switched to tox as test script. * Fixed a few minor cosmetic frontend issues (e.g. padding in admin table header). * Deprecated a few old settings: ============================== =================================== deprecated replacement ============================== =================================== ``CONSTANCE_CONNECTION_CLASS`` ``CONSTANCE_REDIS_CONNECTION_CLASS`` ``CONSTANCE_CONNECTION`` ``CONSTANCE_REDIS_CONNECTION`` ``CONSTANCE_PREFIX`` ``CONSTANCE_REDIS_PREFIX`` ============================== =================================== * The undocumented feature to use an environment variable called ``CONSTANCE_SETTINGS_MODULE`` to define which module to load settings from has been removed. v0.6 (2013/04/12) ~~~~~~~~~~~~~~~~~ * Added Python 3 support. Supported versions: 2.6, 2.7, 3.2 and 3.3. For Python 3.x the use of Django > 1.5.x is required. * Fixed a serious issue with ordering in the admin when using the database backend. Thanks, Bouke Haarsma. * Switch to django-discover-runner as test runner to be able to run on Python 3. * Fixed an issue with refering to static files in the admin interface when using Django < 1.4. v0.5 (2013/03/02) ~~~~~~~~~~~~~~~~~ * Fixed compatibility with Django 1.5's swappable model backends. * Converted the ``key`` field of the database backend to use a ``CharField`` with uniqueness instead of just ``TextField``. For South users we provide a migration for that change. First you have to "fake" the initial migration we've also added to this release:: django-admin.py migrate database --fake 0001 After that you can run the rest of the migrations:: django-admin.py migrate database * Fixed compatibility with Django>1.4's way of refering to static files in the admin. * Added ability to add custom authorization checks via the new ``CONSTANCE_SUPERUSER_ONLY`` setting. * Added Polish translation. Thanks, Janusz Harkot. * Allow ``CONSTANCE_REDIS_CONNECTION`` being an URL instead of a dict. * Added ``CONSTANCE_DATABASE_PREFIX`` setting allow setting a key prefix. * Switched test runner to use django-nose. django-commons-django-constance-1032c50/docs/conf.py000066400000000000000000000067571527040271600223510ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html import os import sys from datetime import datetime def get_version(): # Try to get version from installed package metadata try: from importlib.metadata import PackageNotFoundError from importlib.metadata import version return version("django-constance") except (ImportError, PackageNotFoundError): pass # Fall back to setuptools_scm generated version file try: from constance._version import __version__ return __version__ except ImportError: pass return "0.0.0" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") # 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("extensions")) sys.path.insert(0, os.path.abspath("..")) # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "django-constance" project_copyright = datetime.now().year.__str__() + ", Django Commons" # The full version, including alpha/beta/rc tags release = get_version() # The short X.Y version version = ".".join(release.split(".")[:3]) # -- General configuration ------------------------------------------------ # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinx_search.extension", "settings", ] templates_path = ["_templates"] source_suffix = ".rst" root_doc = "index" exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] pygments_style = "sphinx" html_last_updated_fmt = "" # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "sphinx_rtd_theme" html_static_path = ["_static"] htmlhelp_basename = "django-constancedoc" # -- Options for LaTeX output --------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-latex-output latex_elements = {} latex_documents = [ ("index", "django-constance.tex", "django-constance Documentation", "Django Commons", "manual"), ] # -- Options for manual page output --------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-manual-page-output man_pages = [("index", "django-constance", "django-constance Documentation", ["Django Commons"], 1)] # -- Options for Texinfo output ------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-texinfo-output texinfo_documents = [ ( "index", "django-constance", "django-constance Documentation", "Django Commons", "django-constance", "One line description of project.", "Miscellaneous", ), ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "django": ("https://docs.djangoproject.com/en/dev/", "https://docs.djangoproject.com/en/dev/_objects/"), } django-commons-django-constance-1032c50/docs/extensions/000077500000000000000000000000001527040271600232325ustar00rootroot00000000000000django-commons-django-constance-1032c50/docs/extensions/__init__.py000066400000000000000000000000001527040271600253310ustar00rootroot00000000000000django-commons-django-constance-1032c50/docs/extensions/settings.py000066400000000000000000000002311527040271600254400ustar00rootroot00000000000000def setup(app): app.add_crossref_type( directivename="setting", rolename="setting", indextemplate="pair: %s; setting", ) django-commons-django-constance-1032c50/docs/index.rst000066400000000000000000000374021527040271600227020ustar00rootroot00000000000000Constance - Dynamic Django settings =================================== Features -------- * Easily migrate your static settings to dynamic settings. * Edit the dynamic settings in the Django admin interface. .. image:: _static/screenshot2.png Quick Installation ------------------ .. code-block:: pip install "django-constance[redis]" For complete installation instructions, including how to install the database backend, see :ref:`Backends `. Configuration ------------- Modify your ``settings.py``. Add ``'constance'`` to your :setting:`INSTALLED_APPS`, and move each key you want to turn dynamic into the :setting:`CONSTANCE_CONFIG` section, like this: .. code-block:: python INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.staticfiles', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', ... 'constance', ) CONSTANCE_CONFIG = { 'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, ' 'The Universe, and Everything'), } .. note:: Add constance *before* your project apps. .. note:: If you use admin extensions like `Grapelli `_, ``'constance'`` should be added in :setting:`INSTALLED_APPS` *before* those extensions. Here, ``42`` is the default value for the key ``THE_ANSWER`` if it is not found in the backend. The other member of the tuple is a help text the admin will show. See the :ref:`Backends ` section how to setup the backend and finish the configuration. ``django-constance``'s hashes generated in different instances of the same application may differ, preventing data from being saved. Use :setting:`CONSTANCE_IGNORE_ADMIN_VERSION_CHECK` in order to skip hash verification. .. code-block:: python CONSTANCE_IGNORE_ADMIN_VERSION_CHECK = True Signals ------- Each time a value is changed it will trigger a ``config_updated`` signal. .. code-block:: python from constance.signals import config_updated @receiver(config_updated) def constance_updated(sender, key, old_value, new_value, **kwargs): print(sender, key, old_value, new_value) The sender is the ``config`` object, and the ``key`` and ``new_value`` are the changed settings. Custom fields ------------- You can set the field type with the third value in the ``CONSTANCE_CONFIG`` tuple. The value can be one of the supported types or a string matching a key in your :setting:`CONSTANCE_ADDITIONAL_FIELDS` The supported types are: * ``bool`` * ``int`` * ``float`` * ``Decimal`` * ``str`` * ``datetime`` * ``timedelta`` * ``date`` * ``time`` * ``list`` * ``dict`` .. note:: To be able to use ``list`` and ``dict`` you need to set a widget and form field for these types as it is ambiguous what types shall be stored in the collection object. You can do so with :setting:`CONSTANCE_ADDITIONAL_FIELDS` as explained below. For example, to force a value to be handled as a string: .. code-block:: python 'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, ' 'The Universe, and Everything', str), Custom field types are supported using the dictionary :setting:`CONSTANCE_ADDITIONAL_FIELDS`. This is a mapping between a field label and a sequence (list or tuple). The first item in the sequence is the string path of a field class, and the (optional) second item is a dictionary used to configure the field. The ``widget`` and ``widget_kwargs`` keys in the field config dictionary can be used to configure the widget used in admin, the other values will be passed as kwargs to the field's ``__init__()`` .. note:: Use later evaluated strings instead of direct classes for the field and widget classes: .. code-block:: python CONSTANCE_ADDITIONAL_FIELDS = { 'yes_no_null_select': ['django.forms.fields.ChoiceField', { 'widget': 'django.forms.Select', 'choices': ((None, "-----"), ("yes", "Yes"), ("no", "No")) }], } CONSTANCE_CONFIG = { 'MY_SELECT_KEY': ('yes', 'select yes or no', 'yes_no_null_select'), } If you want to work with images or files you can use this configuration: .. code-block:: python CONSTANCE_ADDITIONAL_FIELDS = { 'image_field': ['django.forms.ImageField', {}] } CONSTANCE_CONFIG = { 'LOGO_IMAGE': ('default.png', 'Company logo', 'image_field'), } When used in a template you probably need to use: .. code-block:: html {% load static %} {% get_media_prefix as MEDIA_URL %} Images and files are uploaded to ``MEDIA_ROOT`` by default. You can specify a subdirectory of ``MEDIA_ROOT`` to use instead by adding the ``CONSTANCE_FILE_ROOT`` setting. E.g.: .. code-block:: python MEDIA_ROOT = os.path.join(BASE_DIR, 'media') CONSTANCE_FILE_ROOT = 'constance' This will result in files being placed in ``media/constance`` within your ``BASE_DIR``. You can use deeper nesting in this setting (e.g. ``constance/images``) but other relative path components (e.g. ``../``) will be rejected. In case you want to store a list of ``int`` values in the constance config, a working setup is .. code-block:: python CONSTANCE_ADDITIONAL_FIELDS = { list: ["django.forms.fields.JSONField", {"widget": "django.forms.Textarea"}], } CONSTANCE_CONFIG = { 'KEY': ([0, 10, 20], 'A list of integers', list), } Make sure to use the ``JSONField`` for this purpose as user input in the admin page may be understood and saved as ``str`` otherwise. Ordered Fields in Django Admin ------------------------------ To sort the fields, you can use an OrderedDict: .. code-block:: python from collections import OrderedDict CONSTANCE_CONFIG = OrderedDict([ ('SITE_NAME', ('My Title', 'Website title')), ('SITE_DESCRIPTION', ('', 'Website description')), ('THEME', ('light-blue', 'Website theme')), ]) Fieldsets --------- You can define fieldsets to group settings together: .. code-block:: python CONSTANCE_CONFIG = { 'SITE_NAME': ('My Title', 'Website title'), 'SITE_DESCRIPTION': ('', 'Website description'), 'THEME': ('light-blue', 'Website theme'), } CONSTANCE_CONFIG_FIELDSETS = { 'General Options': ('SITE_NAME', 'SITE_DESCRIPTION'), 'Theme Options': ('THEME',), } .. note:: CONSTANCE_CONFIG_FIELDSETS must contain all fields from CONSTANCE_CONFIG. .. image:: _static/screenshot3.png Fieldsets collapsing -------------------- To make some fieldsets collapsing you can use new format in CONSTANCE_CONFIG_FIELDSETS. Here's an example: .. code-block:: python CONSTANCE_CONFIG = { 'SITE_NAME': ('My Title', 'Website title'), 'SITE_DESCRIPTION': ('', 'Website description'), 'THEME': ('light-blue', 'Website theme'), } CONSTANCE_CONFIG_FIELDSETS = { 'General Options': { 'fields': ('SITE_NAME', 'SITE_DESCRIPTION'), 'collapse': True }, 'Theme Options': ('THEME',), } Field internationalization -------------------------- Field description and fieldset headers can be integrated into Django's internationalization using the ``gettext_lazy`` function. Note that the ``CONSTANCE_CONFIG_FIELDSETS`` must be converted to a tuple instead of dict as it is not possible to have lazy proxy objects as dictionary keys in the settings file. Example: .. code-block:: python from django.utils.translation import gettext_lazy as _ CONSTANCE_CONFIG = { 'SITE_NAME': ('My Title', _('Website title')), 'SITE_DESCRIPTION': ('', _('Website description')), 'THEME': ('light-blue', _('Website theme')), } CONSTANCE_CONFIG_FIELDSETS = ( ( _('General Options'), { 'fields': ('SITE_NAME', 'SITE_DESCRIPTION'), 'collapse': True, }, ), (_('Theme Options'), ('THEME',)), ) Usage ----- Constance can be used from your Python code and from your Django templates. Python ^^^^^^ Accessing the config variables is as easy as importing the config object and accessing the variables with attribute lookups:: from constance import config # ... if config.THE_ANSWER == 42: answer_the_question() Asynchronous usage ^^^^^^^^^^^^^^^^^^ If you are using Django's asynchronous features (like async views), you can ``await`` the settings directly on the standard ``config`` object:: from constance import config async def my_async_view(request): # Accessing settings is awaitable if await config.THE_ANSWER == 42: return await answer_the_question_async() async def update_settings(): # Updating settings asynchronously await config.aset('THE_ANSWER', 43) # Bulk retrieval is supported as well values = await config.amget(['THE_ANSWER', 'SITE_NAME']) Performance and Safety ~~~~~~~~~~~~~~~~~~~~~~ While synchronous access (e.g., ``config.THE_ANSWER``) still works inside async views for some backends, it is highly discouraged: * **Blocking:** Synchronous access blocks the event loop, reducing the performance of your entire application. * **Safety Guards:** For the Database backend, Django's safety guards will raise a ``SynchronousOnlyOperation`` error if you attempt to access a setting synchronously from an async thread. * **Automatic Detection:** Constance will emit a ``RuntimeWarning`` if it detects synchronous access inside an asynchronous event loop, helping you identify and fix these performance bottlenecks. For peak performance, especially with the Redis backend, always use the ``await`` syntax which leverages native asynchronous drivers. If you run test runners or libraries that keep a background asyncio event loop running during synchronous tests (such as ``pytest-playwright``), or if you want to disable automatic asyncio detection, set:: CONSTANCE_DETECT_ASYNCIO = False Django templates ^^^^^^^^^^^^^^^^ To access the config object from your template you can pass the object to the template context: .. code-block:: python from django.shortcuts import render from constance import config def myview(request): return render(request, 'my_template.html', {'config': config}) You can also use the included context processor. Insert ``'constance.context_processors.config'`` at the top of your ``TEMPLATES['OPTIONS']['context_processors']`` list. See the `Django documentation`_ for details. .. _`Django documentation`: https://docs.djangoproject.com/en/1.11/ref/templates/upgrading/#the-templates-settings This will add the config instance to the context of any template rendered with a ``RequestContext``. Then, in your template you can refer to the config values just as any other variable, e.g.: .. code-block:: django

Welcome on {{ config.SITE_NAME }}

{% if config.BETA_LAUNCHED %} Woohoo! Head over here to use the beta. {% else %} Sadly we haven't launched yet, click here to signup for our newsletter. {% endif %} Command Line ^^^^^^^^^^^^ Constance settings can be get/set on the command line with the manage command :command:`constance`. Available options are: .. program:: constance .. option:: list list all Constance keys and their values .. code-block:: console $ ./manage.py constance list THE_ANSWER 42 SITE_NAME My Title .. option:: get get the value of a Constance key .. code-block:: console $ ./manage.py constance get THE_ANSWER 42 .. option:: set set the value of a Constance key .. code-block:: console $ ./manage.py constance set SITE_NAME "Another Title" If the value contains spaces it should be wrapped in quotes. .. note:: Set values are validated as per in admin, an error will be raised if validation fails: E.g., given this config as per the example app: .. code-block:: python CONSTANCE_CONFIG = { ... 'DATE_ESTABLISHED': (date(1972, 11, 30), "the shop's first opening"), } Setting an invalid date will fail as follow: .. code-block:: console $ ./manage.py constance set DATE_ESTABLISHED '1999-12-00' CommandError: Enter a valid date. .. note:: If the admin field is a :class:`MultiValueField`, then the separate field values need to be provided as separate arguments. E.g., a datetime using :class:`SplitDateTimeField`: .. code-block:: python CONSTANCE_CONFIG = { 'DATETIME_VALUE': (datetime(2010, 8, 23, 11, 29, 24), 'time of the first commit'), } Then this works (and the quotes are optional): .. code-block:: console ./manage.py constance set DATETIME_VALUE '2011-09-24' '12:30:25' This doesn't work: .. code-block:: console ./manage.py constance set DATETIME_VALUE '2011-09-24 12:30:25' CommandError: Enter a list of values. .. option:: remove_stale_keys delete all Constance keys and their values if they are not in settings.CONSTANCE_CONFIG (stale keys) .. code-block:: console $ ./manage.py constance remove_stale_keys Record is considered stale if it exists in database but absent in config. Editing ------- Fire up your ``admin`` and you should see a new app called ``Constance`` with ``THE_ANSWER`` in the ``Config`` pseudo model. By default, changing the settings via the admin is only allowed for superusers. To change this, feel free to set the :setting:`CONSTANCE_SUPERUSER_ONLY` setting to ``False`` and give users or user groups access to the ``constance.change_config`` permission. .. figure:: _static/screenshot1.png The virtual application ``Constance`` among your regular applications. Custom settings form -------------------- If you aim at creating a custom settings form this is possible in the following way: You can inherit from ``ConstanceAdmin`` and set the ``form`` property on your custom admin to use your custom form. This allows you to define your own formsets and layouts, similar to defining a custom form on a standard Django ModelAdmin. This way you can fully style your settings form and group settings the way you like. .. code-block:: python from constance.admin import ConstanceAdmin, Config from constance.forms import ConstanceForm class CustomConfigForm(ConstanceForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) #... do stuff to make your settings form nice ... class ConfigAdmin(ConstanceAdmin): change_list_form = CustomConfigForm change_list_template = 'admin/config/settings.html' admin.site.unregister([Config]) admin.site.register([Config], ConfigAdmin) You can also override the ``get_changelist_form`` method which is called in ``changelist_view`` to get the actual form used to change the settings. This allows you to pick a different form according to the user that makes the request. For example: .. code-block:: python class SuperuserForm(ConstanceForm): # Do some stuff here class MyConstanceAdmin(ConstanceAdmin): def get_changelist_form(self, request): if request.user.is_superuser: return SuperuserForm: else: return super().get_changelist_form(request) Note that the default method returns ``self.change_list_form``. More documentation ------------------ .. toctree:: :maxdepth: 2 backends testing changes Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` django-commons-django-constance-1032c50/docs/make.bat000066400000000000000000000014401527040271600224370ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.https://www.sphinx-doc.org/ exit /b 1 ) if "%1" == "" goto help %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd django-commons-django-constance-1032c50/docs/requirements.txt000066400000000000000000000001071527040271600243150ustar00rootroot00000000000000readthedocs-sphinx-search==0.3.2 sphinx==7.3.7 sphinx-rtd-theme==2.0.0 django-commons-django-constance-1032c50/docs/testing.rst000066400000000000000000000071641527040271600232520ustar00rootroot00000000000000Testing ======= Testing how your app behaves with different config values is achieved with the :class:`override_config` class. This intentionally mirrors the use of Django's :class:`~django.test.override_setting`. .. py:class:: override_config(**kwargs) Replaces key-value pairs in the config. Use as decorator or context manager. Usage ~~~~~ It can be used as a decorator at the :class:`~django.test.TestCase` level, the method level and also as a `context manager `_. .. code-block:: python from constance import config from constance.test import override_config from django.test import TestCase @override_config(YOUR_NAME="Arthur of Camelot") class ExampleTestCase(TestCase): def test_what_is_your_name(self): self.assertEqual(config.YOUR_NAME, "Arthur of Camelot") @override_config(YOUR_QUEST="To find the Holy Grail") def test_what_is_your_quest(self): self.assertEqual(config.YOUR_QUEST, "To find the Holy Grail") def test_what_is_your_favourite_color(self): with override_config(YOUR_FAVOURITE_COLOR="Blue?"): self.assertEqual(config.YOUR_FAVOURITE_COLOR, "Blue?") The method decorator also supports ``async def`` tests. The overridden values remain active until the awaited test completes, including when it raises an exception. Pytest usage ~~~~~~~~~~~~ Django-constance provides pytest plugin that adds marker :class:`@pytest.mark.override_config()`. It handles config override for module/class/function, and automatically revert any changes made to the constance config values when test is completed. .. py:function:: pytest.mark.override_config(**kwargs) Specify different config values for the marked tests in kwargs. Module scope override .. code-block:: python pytestmark = pytest.mark.override_config(API_URL="/awesome/url/") def test_api_url_is_awesome(): ... Class/function scope .. code-block:: python from constance import config @pytest.mark.override_config(API_URL="/awesome/url/") class SomeClassTest: def test_is_awesome_url(self): assert config.API_URL == "/awesome/url/" @pytest.mark.override_config(API_URL="/another/awesome/url/") def test_another_awesome_url(self): assert config.API_URL == "/another/awesome/url/" If you want to use override as a context manager or decorator, consider using .. code-block:: python from constance.test.pytest import override_config def test_override_context_manager(): with override_config(BOOL_VALUE=False): ... # or @override_config(BOOL_VALUE=False) def test_override_context_manager(): ... Pytest fixture as function or method parameter. .. note:: No import needed as fixture is available globally. .. code-block:: python def test_api_url_is_awesome(override_config): with override_config(API_URL="/awesome/url/"): ... Any scope, auto-used fixture alternative can also be implemented like this .. code-block:: python @pytest.fixture(scope='module', autouse=True) # e.g. module scope def api_url(override_config): with override_config(API_URL="/awesome/url/"): yield Memory backend ~~~~~~~~~~~~~~ If you don't want to rely on any external services such as Redis or database when running your unittests you can select :class:`MemoryBackend` for a test Django settings file .. code-block:: python CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend' It will provide simple thread-safe backend which will reset to default values after each test run. django-commons-django-constance-1032c50/example/000077500000000000000000000000001527040271600215365ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/000077500000000000000000000000001527040271600236645ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/__init__.py000066400000000000000000000000001527040271600257630ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/000077500000000000000000000000001527040271600246275ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/__init__.py000066400000000000000000000000001527040271600267260ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/catalog/000077500000000000000000000000001527040271600262415ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/catalog/__init__.py000066400000000000000000000000001527040271600303400ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/catalog/admin.py000066400000000000000000000001571527040271600277060ustar00rootroot00000000000000from django.contrib import admin from cheeseshop.apps.catalog.models import Brand admin.site.register(Brand) django-commons-django-constance-1032c50/example/cheeseshop/apps/catalog/migrations/000077500000000000000000000000001527040271600304155ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/catalog/migrations/0001_initial.py000066400000000000000000000006561527040271600330670ustar00rootroot00000000000000from django.db import migrations from django.db import models class Migration(migrations.Migration): dependencies = [] operations = [ migrations.CreateModel( name="Brand", fields=[ ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), ("name", models.CharField(max_length=75)), ], ), ] django-commons-django-constance-1032c50/example/cheeseshop/apps/catalog/migrations/__init__.py000066400000000000000000000000001527040271600325140ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/catalog/models.py000066400000000000000000000001451527040271600300760ustar00rootroot00000000000000from django.db import models class Brand(models.Model): name = models.CharField(max_length=75) django-commons-django-constance-1032c50/example/cheeseshop/apps/storage/000077500000000000000000000000001527040271600262735ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/storage/__init__.py000066400000000000000000000000001527040271600303720ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/storage/admin.py000066400000000000000000000002751527040271600277410ustar00rootroot00000000000000from django.contrib import admin from cheeseshop.apps.storage.models import Shelf from cheeseshop.apps.storage.models import Supply admin.site.register(Shelf) admin.site.register(Supply) django-commons-django-constance-1032c50/example/cheeseshop/apps/storage/migrations/000077500000000000000000000000001527040271600304475ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/storage/migrations/0001_initial.py000066400000000000000000000015651527040271600331210ustar00rootroot00000000000000from django.db import migrations from django.db import models class Migration(migrations.Migration): dependencies = [] operations = [ migrations.CreateModel( name="Shelf", fields=[ ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), ("name", models.CharField(max_length=75)), ], options={ "verbose_name_plural": "shelves", }, ), migrations.CreateModel( name="Supply", fields=[ ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), ("name", models.CharField(max_length=75)), ], options={ "verbose_name_plural": "supplies", }, ), ] django-commons-django-constance-1032c50/example/cheeseshop/apps/storage/migrations/__init__.py000066400000000000000000000000001527040271600325460ustar00rootroot00000000000000django-commons-django-constance-1032c50/example/cheeseshop/apps/storage/models.py000066400000000000000000000004411527040271600301270ustar00rootroot00000000000000from django.db import models class Shelf(models.Model): name = models.CharField(max_length=75) class Meta: verbose_name_plural = "shelves" class Supply(models.Model): name = models.CharField(max_length=75) class Meta: verbose_name_plural = "supplies" django-commons-django-constance-1032c50/example/cheeseshop/fields.py000066400000000000000000000011101527040271600254750ustar00rootroot00000000000000import json from django.forms import fields from django.forms import widgets class JsonField(fields.CharField): widget = widgets.Textarea def __init__(self, rows: int = 5, **kwargs): self.rows = rows super().__init__(**kwargs) def widget_attrs(self, widget: widgets.Widget): attrs = super().widget_attrs(widget) attrs["rows"] = self.rows return attrs def to_python(self, value): if value: return json.loads(value) return {} def prepare_value(self, value): return json.dumps(value) django-commons-django-constance-1032c50/example/cheeseshop/settings.py000066400000000000000000000104651527040271600261040ustar00rootroot00000000000000""" Django settings for cheeseshop project. For more information on this file, see https://docs.djangoproject.com/en/4.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.1/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os from datetime import date BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ SITE_ID = 1 # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = "hdx64#m+lnc_0ffoyehbk&7gk1&*9uar$pcfcm-%$km#p0$k=6" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = ( "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", "cheeseshop.apps.catalog", "cheeseshop.apps.storage", "constance", ) MIDDLEWARE = ( "django.middleware.common.CommonMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", ) ROOT_URLCONF = "cheeseshop.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] WSGI_APPLICATION = "cheeseshop.wsgi.application" # Database # https://docs.djangoproject.com/en/4.1/ref/settings/#databases DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": "/tmp/cheeseshop.db", } } CONSTANCE_REDIS_CONNECTION = { "host": "localhost", "port": 6379, "db": 0, } CONSTANCE_ADDITIONAL_FIELDS = { "yes_no_null_select": [ "django.forms.fields.ChoiceField", {"widget": "django.forms.Select", "choices": ((None, "-----"), ("yes", "Yes"), ("no", "No"))}, ], "email": ("django.forms.fields.EmailField",), "json_field": ["cheeseshop.fields.JsonField"], "image_field": ["django.forms.ImageField", {}], } CONSTANCE_CONFIG = { "BANNER": ("The National Cheese Emporium", "name of the shop"), "OWNER": ("Mr. Henry Wensleydale", "owner of the shop"), "OWNER_EMAIL": ("henry@example.com", "contact email for owner", "email"), "MUSICIANS": (4, "number of musicians inside the shop"), "DATE_ESTABLISHED": (date(1972, 11, 30), "the shop's first opening"), "MY_SELECT_KEY": ("yes", "select yes or no", "yes_no_null_select"), "MULTILINE": ("Line one\nLine two", "multiline string"), "JSON_DATA": ( {"a": 1_000, "b": "test", "max": 30_000_000}, "Some test data for json", "json_field", ), "LOGO": ( "", "Logo image file", "image_field", ), } CONSTANCE_CONFIG_FIELDSETS = { "Cheese shop general info": [ "BANNER", "OWNER", "OWNER_EMAIL", "MUSICIANS", "DATE_ESTABLISHED", "LOGO", ], "Awkward test settings": ["MY_SELECT_KEY", "MULTILINE", "JSON_DATA"], } CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend" CACHES = { "default": { "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", "LOCATION": "127.0.0.1:11211", } } CONSTANCE_DATABASE_CACHE_BACKEND = "default" # Internationalization # https://docs.djangoproject.com/en/4.1/topics/i18n/ LANGUAGE_CODE = "en-us" TIME_ZONE = "America/Chicago" USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.1/howto/static-files/ STATIC_URL = "/static/" MEDIA_URL = "/media/" MEDIA_ROOT = os.path.join(BASE_DIR, "media") CONSTANCE_FILE_ROOT = "constance" DEFAULT_AUTO_FIELD = "django.db.models.AutoField" django-commons-django-constance-1032c50/example/cheeseshop/urls.py000066400000000000000000000004701527040271600252240ustar00rootroot00000000000000from django.conf import settings from django.contrib import admin from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.urls import re_path admin.autodiscover() urlpatterns = [ re_path("admin/", admin.site.urls), ] if settings.DEBUG: urlpatterns += staticfiles_urlpatterns() django-commons-django-constance-1032c50/example/cheeseshop/wsgi.py000066400000000000000000000002531527040271600252070ustar00rootroot00000000000000import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cheeseshop.settings") application = get_wsgi_application() django-commons-django-constance-1032c50/example/manage.py000077500000000000000000000003751527040271600233500ustar00rootroot00000000000000#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cheeseshop.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-commons-django-constance-1032c50/example/requirements.txt000066400000000000000000000000361527040271600250210ustar00rootroot00000000000000Django>=3.2 Pillow pymemcache django-commons-django-constance-1032c50/pyproject.toml000066400000000000000000000053121527040271600230200ustar00rootroot00000000000000[build-system] requires = ["setuptools>=42", "wheel", "setuptools_scm>=8"] build-backend = "setuptools.build_meta" [project] name = "django-constance" dynamic = ["version"] description = "Django live settings with pluggable backends, including Redis." readme = "README.rst" license = { text = "BSD" } requires-python = ">=3.10" authors = [ { name = "Jannis Leidel", email = "jannis@leidel.info" }, ] keywords = ["django", "libraries", "redis", "settings"] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Framework :: Django :: 5.2", "Framework :: Django :: 6.0", "Framework :: Django :: 6.1", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Utilities", ] [project.optional-dependencies] redis = [ "redis", ] [project.entry-points.pytest11] pytest-django-constance = "constance.test.pytest" [project.urls] homepage = "https://github.com/django-commons/django-constance/" documentation = "https://django-constance.readthedocs.io/en/latest/" repository = "https://github.com/django-commons/django-constance/" changelog = "https://github.com/django-commons/django-constance/releases/" [tool.setuptools] license-files = [] # see https://github.com/pypa/twine/issues/1216#issuecomment-2609745412 [tool.setuptools.packages.find] include = ["constance*"] [tool.setuptools_scm] version_file = "constance/_version.py" [tool.ruff] line-length = 120 indent-width = 4 [tool.ruff.format] indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" [tool.ruff.lint] select = [ "B", "D", "E", "ERA", "EXE", "F", "FBT", "FURB", "G", "FA", "I", "ICN", "INP", "LOG", "PGH", "RET", "RUF", "S", "SIM", "TID", "UP", "W", ] ignore = ["D1", "D203", "D205", "D415", "D212", "RUF012", "D400", "D401"] [tool.ruff.lint.per-file-ignores] "docs/*" = ["INP"] "example/*" = ["S"] "tests/*" = ["S"] [tool.ruff.lint.isort] force-single-line = true [tool.ruff.lint.flake8-boolean-trap] extend-allowed-calls = ["unittest.mock.patch", "django.db.models.Value"] django-commons-django-constance-1032c50/tests/000077500000000000000000000000001527040271600212455ustar00rootroot00000000000000django-commons-django-constance-1032c50/tests/__init__.py000066400000000000000000000000001527040271600233440ustar00rootroot00000000000000django-commons-django-constance-1032c50/tests/backends/000077500000000000000000000000001527040271600230175ustar00rootroot00000000000000django-commons-django-constance-1032c50/tests/backends/__init__.py000066400000000000000000000000001527040271600251160ustar00rootroot00000000000000django-commons-django-constance-1032c50/tests/backends/test_database.py000066400000000000000000000153571527040271600262070ustar00rootroot00000000000000from django.test import TestCase from django.test import TransactionTestCase from constance import LazyConfig from constance import settings from constance.backends.database import DatabaseBackend from constance.base import Config from tests.storage import StorageTestsMixin class TestDatabase(StorageTestsMixin, TestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = "constance.backends.database.DatabaseBackend" super().setUp() def test_database_queries(self): # Read and set to default value with self.assertNumQueries(5): self.assertEqual(self.config.INT_VALUE, 1) # Read again with self.assertNumQueries(1): self.assertEqual(self.config.INT_VALUE, 1) # Set value with self.assertNumQueries(2): self.config.INT_VALUE = 15 def tearDown(self): settings.BACKEND = self.old_backend class TestDatabaseWithCache(StorageTestsMixin, TestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = "constance.backends.database.DatabaseBackend" self.old_cache_backend = settings.DATABASE_CACHE_BACKEND settings.DATABASE_CACHE_BACKEND = "default" super().setUp() self.config._backend._cache.clear() def test_database_queries(self): # Read and set to default value with self.assertNumQueries(6): self.assertEqual(self.config.INT_VALUE, 1) # Read again with self.assertNumQueries(0): self.assertEqual(self.config.INT_VALUE, 1) # Set value with self.assertNumQueries(3): self.config.INT_VALUE = 15 def tearDown(self): settings.BACKEND = self.old_backend settings.DATABASE_CACHE_BACKEND = self.old_cache_backend class TestDatabaseBackendConstruction(TestCase): """Regression tests for #667: backend construction must not perform I/O.""" def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = "constance.backends.database.DatabaseBackend" def tearDown(self): settings.BACKEND = self.old_backend def test_backend_init_does_no_queries(self): with self.assertNumQueries(0): DatabaseBackend() def test_autofill_is_noop_without_cache(self): backend = DatabaseBackend() with self.assertNumQueries(0): backend.autofill() def test_backend_init_does_no_queries_with_cache(self): old_cache_backend = settings.DATABASE_CACHE_BACKEND settings.DATABASE_CACHE_BACKEND = "default" try: with self.assertNumQueries(0): DatabaseBackend() finally: settings.DATABASE_CACHE_BACKEND = old_cache_backend class TestDatabaseAsync(TransactionTestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = "constance.backends.database.DatabaseBackend" self.config = Config() async def test_lazy_config_first_access_in_async(self): # End-to-end: LazyConfig _setup() runs from inside the loop and the # awaited value resolves. Companion to TestDatabaseBackendConstruction # which is the strict #667 regression test. lazy = LazyConfig() value = await lazy.INT_VALUE self.assertEqual(value, 1) def tearDown(self): settings.BACKEND = self.old_backend async def test_aget_returns_none_for_missing_key(self): result = await self.config._backend.aget("INT_VALUE") self.assertIsNone(result) async def test_aget_returns_value(self): await self.config._backend.aset("INT_VALUE", 42) result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 42) async def test_aset_stores_value(self): await self.config._backend.aset("INT_VALUE", 99) result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 99) async def test_amget_returns_empty_for_no_keys(self): result = await self.config._backend.amget([]) self.assertEqual(result, {}) async def test_amget_returns_values(self): await self.config._backend.aset("INT_VALUE", 10) await self.config._backend.aset("BOOL_VALUE", value=True) result = await self.config._backend.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(result, {"INT_VALUE": 10, "BOOL_VALUE": True}) class TestDatabaseWithCacheAsync(TransactionTestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = "constance.backends.database.DatabaseBackend" self.old_cache_backend = settings.DATABASE_CACHE_BACKEND settings.DATABASE_CACHE_BACKEND = "default" self.config = Config() self.config._backend._cache.clear() def tearDown(self): settings.BACKEND = self.old_backend settings.DATABASE_CACHE_BACKEND = self.old_cache_backend async def test_aget_returns_none_for_missing_key(self): result = await self.config._backend.aget("INT_VALUE") self.assertIsNone(result) async def test_aget_returns_value_from_cache(self): # First set a value using async await self.config._backend.aset("INT_VALUE", 42) # Clear cache and re-fetch to test aget path self.config._backend._cache.clear() result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 42) async def test_aget_populates_cache(self): await self.config._backend.aset("INT_VALUE", 42) self.config._backend._cache.clear() # aget should populate the cache result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 42) async def test_aset_stores_value(self): await self.config._backend.aset("INT_VALUE", 99) result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 99) async def test_amget_returns_empty_for_no_keys(self): result = await self.config._backend.amget([]) self.assertEqual(result, {}) async def test_amget_returns_values(self): await self.config._backend.aset("INT_VALUE", 10) await self.config._backend.aset("BOOL_VALUE", value=True) result = await self.config._backend.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(result, {"INT_VALUE": 10, "BOOL_VALUE": True}) async def test_amget_uses_cache(self): # Set values using async and ensure they're cached await self.config._backend.aset("INT_VALUE", 10) await self.config._backend.aset("BOOL_VALUE", value=True) result = await self.config._backend.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(result["INT_VALUE"], 10) self.assertEqual(result["BOOL_VALUE"], True) django-commons-django-constance-1032c50/tests/backends/test_memory.py000066400000000000000000000037701527040271600257470ustar00rootroot00000000000000from django.test import TestCase from django.test import TransactionTestCase from constance import settings from constance.base import Config from tests.storage import StorageTestsMixin class TestMemory(StorageTestsMixin, TestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = "constance.backends.memory.MemoryBackend" super().setUp() self.config._backend._storage = {} def tearDown(self): self.config._backend._storage = {} settings.BACKEND = self.old_backend def test_mget_empty_keys(self): result = self.config._backend.mget([]) self.assertEqual(result, {}) class TestMemoryAsync(TransactionTestCase): def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = "constance.backends.memory.MemoryBackend" self.config = Config() self.config._backend._storage = {} def tearDown(self): self.config._backend._storage = {} settings.BACKEND = self.old_backend async def test_aget_returns_none_for_missing_key(self): result = await self.config._backend.aget("INT_VALUE") self.assertIsNone(result) async def test_aget_returns_value(self): self.config._backend.set("INT_VALUE", 42) result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 42) async def test_aset_stores_value(self): await self.config._backend.aset("INT_VALUE", 99) result = self.config._backend.get("INT_VALUE") self.assertEqual(result, 99) async def test_amget_returns_empty_for_no_keys(self): result = await self.config._backend.amget([]) self.assertEqual(result, {}) async def test_amget_returns_values(self): self.config._backend.set("INT_VALUE", 10) self.config._backend.set("BOOL_VALUE", value=True) result = await self.config._backend.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(result, {"INT_VALUE": 10, "BOOL_VALUE": True}) django-commons-django-constance-1032c50/tests/backends/test_redis.py000066400000000000000000000244461527040271600255500ustar00rootroot00000000000000from unittest import mock from django.core.exceptions import ImproperlyConfigured from django.test import TestCase from django.test import TransactionTestCase from constance import settings from constance.backends.redisd import RedisBackend from constance.base import Config from tests.storage import StorageTestsMixin class TestRedis(StorageTestsMixin, TestCase): _BACKEND = "constance.backends.redisd.RedisBackend" def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = self._BACKEND super().setUp() self.config._backend._rd.clear() def tearDown(self): self.config._backend._rd.clear() settings.BACKEND = self.old_backend def test_mget_empty_keys(self): result = self.config._backend.mget([]) self.assertEqual(result, {}) class TestCachingRedis(TestRedis): _BACKEND = "constance.backends.redisd.CachingRedisBackend" def test_mget_empty_keys(self): result = self.config._backend.mget([]) self.assertEqual(result, {}) class TestRedisAsync(TransactionTestCase): _BACKEND = "constance.backends.redisd.RedisBackend" def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = self._BACKEND self.config = Config() self.config._backend._rd.clear() def tearDown(self): self.config._backend._rd.clear() settings.BACKEND = self.old_backend async def test_aget_returns_none_for_missing_key(self): result = await self.config._backend.aget("INT_VALUE") self.assertIsNone(result) async def test_aget_returns_value(self): self.config._backend.set("INT_VALUE", 42) result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 42) async def test_aset_stores_value(self): await self.config._backend.aset("INT_VALUE", 99) result = self.config._backend.get("INT_VALUE") self.assertEqual(result, 99) async def test_amget_returns_empty_for_no_keys(self): result = await self.config._backend.amget([]) self.assertEqual(result, {}) async def test_amget_returns_values(self): self.config._backend.set("INT_VALUE", 10) self.config._backend.set("BOOL_VALUE", value=True) result = await self.config._backend.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(result, {"INT_VALUE": 10, "BOOL_VALUE": True}) async def test_amget_skips_missing_keys(self): self.config._backend.set("INT_VALUE", 10) result = await self.config._backend.amget(["INT_VALUE", "MISSING_KEY"]) self.assertEqual(result, {"INT_VALUE": 10}) class TestCachingRedisAsync(TransactionTestCase): _BACKEND = "constance.backends.redisd.CachingRedisBackend" def setUp(self): self.old_backend = settings.BACKEND settings.BACKEND = self._BACKEND self.config = Config() self.config._backend._rd.clear() self.config._backend._cache.clear() def tearDown(self): self.config._backend._rd.clear() self.config._backend._cache.clear() settings.BACKEND = self.old_backend async def test_aget_caches_value(self): # First set a value via sync self.config._backend.set("INT_VALUE", 42) # Clear the in-memory cache self.config._backend._cache.clear() # Async get should fetch and cache result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 42) # Verify it's cached self.assertIn("INT_VALUE", self.config._backend._cache) async def test_aget_returns_cached_value(self): # Manually set cache from time import monotonic timeout = self.config._backend._timeout self.config._backend._cache["INT_VALUE"] = (monotonic() + timeout, 100) result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 100) async def test_aget_refreshes_expired_cache(self): from time import monotonic # Set expired cache self.config._backend._cache["INT_VALUE"] = (monotonic() - 10, 100) # Set different value in redis using proper codec format self.config._backend._rd.set( self.config._backend.add_prefix("INT_VALUE"), b'{"__type__": "default", "__value__": 200}', ) result = await self.config._backend.aget("INT_VALUE") self.assertEqual(result, 200) async def test_aset_updates_cache(self): await self.config._backend.aset("INT_VALUE", 55) # Verify cache is updated self.assertIn("INT_VALUE", self.config._backend._cache) self.assertEqual(self.config._backend._cache["INT_VALUE"][1], 55) async def test_amget_returns_empty_for_no_keys(self): result = await self.config._backend.amget([]) self.assertEqual(result, {}) async def test_amget_returns_cached_values(self): from time import monotonic timeout = self.config._backend._timeout self.config._backend._cache["INT_VALUE"] = (monotonic() + timeout, 10) self.config._backend._cache["BOOL_VALUE"] = (monotonic() + timeout, True) result = await self.config._backend.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(result, {"INT_VALUE": 10, "BOOL_VALUE": True}) async def test_amget_fetches_missing_keys(self): from time import monotonic timeout = self.config._backend._timeout # One key cached, one in Redis only self.config._backend._cache["INT_VALUE"] = (monotonic() + timeout, 10) self.config._backend._rd.set( self.config._backend.add_prefix("BOOL_VALUE"), b'{"__type__": "default", "__value__": true}', ) result = await self.config._backend.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(result["INT_VALUE"], 10) self.assertEqual(result["BOOL_VALUE"], True) async def test_amget_refreshes_expired_keys(self): from time import monotonic # Set expired cache self.config._backend._cache["INT_VALUE"] = (monotonic() - 10, 100) # Set different value in redis using proper codec format self.config._backend._rd.set( self.config._backend.add_prefix("INT_VALUE"), b'{"__type__": "default", "__value__": 200}', ) result = await self.config._backend.amget(["INT_VALUE"]) self.assertEqual(result["INT_VALUE"], 200) class TestRedisBackendInit(TestCase): """Tests for RedisBackend.__init__ client initialization paths.""" def setUp(self): self.old_conn_cls = settings.REDIS_CONNECTION_CLASS self.old_async_conn_cls = settings.REDIS_ASYNC_CONNECTION_CLASS self.old_conn = settings.REDIS_CONNECTION def tearDown(self): settings.REDIS_CONNECTION_CLASS = self.old_conn_cls settings.REDIS_ASYNC_CONNECTION_CLASS = self.old_async_conn_cls settings.REDIS_CONNECTION = self.old_conn def test_no_redis_package_raises_improperly_configured(self): settings.REDIS_CONNECTION_CLASS = None settings.REDIS_ASYNC_CONNECTION_CLASS = "tests.redis_mockup.AsyncConnection" with mock.patch.dict("sys.modules", {"redis": None}), self.assertRaises(ImproperlyConfigured): RedisBackend() def test_sync_redis_from_url_with_string_connection(self): settings.REDIS_CONNECTION_CLASS = None settings.REDIS_ASYNC_CONNECTION_CLASS = "tests.redis_mockup.AsyncConnection" settings.REDIS_CONNECTION = "redis://localhost:6379/0" mock_redis = mock.MagicMock() with mock.patch.dict("sys.modules", {"redis": mock_redis, "redis.asyncio": mock_redis.asyncio}): backend = RedisBackend() mock_redis.from_url.assert_called_once_with("redis://localhost:6379/0") self.assertEqual(backend._rd, mock_redis.from_url.return_value) def test_sync_redis_with_dict_connection(self): settings.REDIS_CONNECTION_CLASS = None settings.REDIS_ASYNC_CONNECTION_CLASS = "tests.redis_mockup.AsyncConnection" settings.REDIS_CONNECTION = {"host": "localhost", "port": 6379} mock_redis = mock.MagicMock() with mock.patch.dict("sys.modules", {"redis": mock_redis, "redis.asyncio": mock_redis.asyncio}): backend = RedisBackend() mock_redis.Redis.assert_called_once_with(host="localhost", port=6379) self.assertEqual(backend._rd, mock_redis.Redis.return_value) def test_async_redis_not_available_sets_ard_none(self): settings.REDIS_CONNECTION_CLASS = "tests.redis_mockup.Connection" settings.REDIS_ASYNC_CONNECTION_CLASS = None mock_redis = mock.MagicMock() # Simulate redis.asyncio not being available with mock.patch.dict("sys.modules", {"redis": mock_redis, "redis.asyncio": None}): backend = RedisBackend() self.assertIsNone(backend._ard) def test_async_redis_from_url_with_string_connection(self): settings.REDIS_CONNECTION_CLASS = "tests.redis_mockup.Connection" settings.REDIS_ASYNC_CONNECTION_CLASS = None settings.REDIS_CONNECTION = "redis://localhost:6379/0" mock_aredis = mock.MagicMock() mock_redis = mock.MagicMock() mock_redis.asyncio = mock_aredis with mock.patch.dict("sys.modules", {"redis": mock_redis, "redis.asyncio": mock_aredis}): backend = RedisBackend() mock_aredis.from_url.assert_called_once_with("redis://localhost:6379/0") self.assertEqual(backend._ard, mock_aredis.from_url.return_value) def test_async_redis_with_dict_connection(self): settings.REDIS_CONNECTION_CLASS = "tests.redis_mockup.Connection" settings.REDIS_ASYNC_CONNECTION_CLASS = None settings.REDIS_CONNECTION = {"host": "localhost", "port": 6379} mock_aredis = mock.MagicMock() mock_redis = mock.MagicMock() mock_redis.asyncio = mock_aredis with mock.patch.dict("sys.modules", {"redis": mock_redis, "redis.asyncio": mock_aredis}): backend = RedisBackend() mock_aredis.Redis.assert_called_once_with(host="localhost", port=6379) self.assertEqual(backend._ard, mock_aredis.Redis.return_value) def test_check_async_support_raises_when_ard_is_none(self): backend = RedisBackend() backend._ard = None with self.assertRaises(ImproperlyConfigured): backend._check_async_support() django-commons-django-constance-1032c50/tests/cache_mockup.py000066400000000000000000000016411527040271600242420ustar00rootroot00000000000000from django.core.cache.backends.base import BaseCache from django.core.cache.backends.locmem import LocMemCache class Cache(BaseCache): def __init__(self, name, params): self._cache = LocMemCache(name, params) self.add = self._cache.add self.delete = self._cache.delete self.set = self._cache.set self.get = self._cache.get self.clear = self._cache.clear self.set_many = self._cache.set_many self.get_many = self._cache.get_many self.delete_many = self._cache.delete_many # Async methods for DatabaseBackend.aget() support async def aget(self, key, default=None, version=None): return self.get(key, default, version) async def aget_many(self, keys, version=None): return self.get_many(keys, version) async def aadd(self, key, value, timeout=None, version=None): return self.add(key, value, timeout, version) django-commons-django-constance-1032c50/tests/redis_mockup.py000066400000000000000000000013351527040271600243050ustar00rootroot00000000000000# Shared storage so sync (Connection) and async (AsyncConnection) instances # operate on the same underlying data, just like a real Redis server would. _shared_store = {} class Connection: def set(self, key, value): _shared_store[key] = value def get(self, key, default=None): return _shared_store.get(key, default) def mget(self, keys): return [_shared_store.get(key) for key in keys] def clear(self): _shared_store.clear() class AsyncConnection: async def set(self, key, value): _shared_store[key] = value async def get(self, key): return _shared_store.get(key) async def mget(self, keys): return [_shared_store.get(key) for key in keys] django-commons-django-constance-1032c50/tests/settings.py000066400000000000000000000071731527040271600234670ustar00rootroot00000000000000from datetime import date from datetime import datetime from datetime import time from datetime import timedelta from decimal import Decimal SECRET_KEY = "cheese" MIDDLEWARE = ( "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.SessionAuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ) DATABASE_ENGINE = "sqlite3" DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", }, "secondary": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", }, } CACHES = { "default": { "BACKEND": "tests.cache_mockup.Cache", "LOCATION": "locmem", } } INSTALLED_APPS = ( "django.contrib.admin", "django.contrib.staticfiles", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "constance", "constance.backends.database", ) ROOT_URLCONF = "tests.urls" CONSTANCE_REDIS_CONNECTION_CLASS = "tests.redis_mockup.Connection" CONSTANCE_REDIS_ASYNC_CONNECTION_CLASS = "tests.redis_mockup.AsyncConnection" CONSTANCE_ADDITIONAL_FIELDS = { "yes_no_null_select": [ "django.forms.fields.ChoiceField", {"widget": "django.forms.Select", "choices": ((None, "-----"), ("yes", "Yes"), ("no", "No"))}, ], # note this intentionally uses a tuple so that we can test immutable "email": ("django.forms.fields.EmailField",), "array": ["django.forms.fields.CharField", {"widget": "django.forms.Textarea"}], "json": ["django.forms.fields.CharField", {"widget": "django.forms.Textarea"}], } USE_TZ = True CONSTANCE_CONFIG = { "INT_VALUE": (1, "some int"), "BOOL_VALUE": (True, "true or false"), "STRING_VALUE": ("Hello world", "greetings"), "DECIMAL_VALUE": (Decimal("0.1"), "the first release version"), "DATETIME_VALUE": (datetime(2010, 8, 23, 11, 29, 24), "time of the first commit"), "FLOAT_VALUE": (3.1415926536, "PI"), "DATE_VALUE": (date(2010, 12, 24), "Merry Chrismas"), "TIME_VALUE": (time(23, 59, 59), "And happy New Year"), "TIMEDELTA_VALUE": (timedelta(days=1, hours=2, minutes=3), "Interval"), "CHOICE_VALUE": ("yes", "select yes or no", "yes_no_null_select"), "LINEBREAK_VALUE": ("Spam spam", "eggs\neggs"), "EMAIL_VALUE": ("test@example.com", "An email", "email"), "LIST_VALUE": ([1, "1", date(2019, 1, 1)], "A list", "array"), "JSON_VALUE": ( { "key": "value", "key2": 2, "key3": [1, 2, 3], "key4": {"key": "value"}, "key5": date(2019, 1, 1), "key6": None, }, "A JSON object", "json", ), } DEBUG = True STATIC_ROOT = "./static/" STATIC_URL = "/static/" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.i18n", "django.template.context_processors.request", "django.template.context_processors.static", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", "constance.context_processors.config", ], }, }, ] django-commons-django-constance-1032c50/tests/storage.py000066400000000000000000000114441527040271600232670ustar00rootroot00000000000000from datetime import date from datetime import datetime from datetime import time from datetime import timedelta from decimal import Decimal from constance import settings from constance.base import Config class StorageTestsMixin: def setUp(self): self.config = Config() super().setUp() def test_store(self): self.assertEqual(self.config.INT_VALUE, 1) self.assertEqual(self.config.BOOL_VALUE, True) self.assertEqual(self.config.STRING_VALUE, "Hello world") self.assertEqual(self.config.DECIMAL_VALUE, Decimal("0.1")) self.assertEqual(self.config.DATETIME_VALUE, datetime(2010, 8, 23, 11, 29, 24)) self.assertEqual(self.config.FLOAT_VALUE, 3.1415926536) self.assertEqual(self.config.DATE_VALUE, date(2010, 12, 24)) self.assertEqual(self.config.TIME_VALUE, time(23, 59, 59)) self.assertEqual(self.config.TIMEDELTA_VALUE, timedelta(days=1, hours=2, minutes=3)) self.assertEqual(self.config.CHOICE_VALUE, "yes") self.assertEqual(self.config.EMAIL_VALUE, "test@example.com") self.assertEqual(self.config.LIST_VALUE, [1, "1", date(2019, 1, 1)]) self.assertEqual( self.config.JSON_VALUE, { "key": "value", "key2": 2, "key3": [1, 2, 3], "key4": {"key": "value"}, "key5": date(2019, 1, 1), "key6": None, }, ) # set values self.config.INT_VALUE = 100 self.config.BOOL_VALUE = False self.config.STRING_VALUE = "Beware the weeping angel" self.config.DECIMAL_VALUE = Decimal("1.2") self.config.DATETIME_VALUE = datetime(1977, 10, 2) self.config.FLOAT_VALUE = 2.718281845905 self.config.DATE_VALUE = date(2001, 12, 20) self.config.TIME_VALUE = time(1, 59, 0) self.config.TIMEDELTA_VALUE = timedelta(days=2, hours=3, minutes=4) self.config.CHOICE_VALUE = "no" self.config.EMAIL_VALUE = "foo@bar.com" self.config.LIST_VALUE = [1, date(2020, 2, 2)] self.config.JSON_VALUE = {"key": "OK"} # read again self.assertEqual(self.config.INT_VALUE, 100) self.assertEqual(self.config.BOOL_VALUE, False) self.assertEqual(self.config.STRING_VALUE, "Beware the weeping angel") self.assertEqual(self.config.DECIMAL_VALUE, Decimal("1.2")) self.assertEqual(self.config.DATETIME_VALUE, datetime(1977, 10, 2)) self.assertEqual(self.config.FLOAT_VALUE, 2.718281845905) self.assertEqual(self.config.DATE_VALUE, date(2001, 12, 20)) self.assertEqual(self.config.TIME_VALUE, time(1, 59, 0)) self.assertEqual(self.config.TIMEDELTA_VALUE, timedelta(days=2, hours=3, minutes=4)) self.assertEqual(self.config.CHOICE_VALUE, "no") self.assertEqual(self.config.EMAIL_VALUE, "foo@bar.com") self.assertEqual(self.config.LIST_VALUE, [1, date(2020, 2, 2)]) self.assertEqual(self.config.JSON_VALUE, {"key": "OK"}) def test_nonexistent(self): self.assertRaises(AttributeError, getattr, self.config, "NON_EXISTENT") with self.assertRaises(AttributeError): self.config.NON_EXISTENT = 1 def test_missing_values(self): # set some values and leave out others self.config.BOOL_VALUE = False self.config.DECIMAL_VALUE = Decimal("1.2") self.config.DATETIME_VALUE = datetime(1977, 10, 2) self.config.DATE_VALUE = date(2001, 12, 20) self.config.TIME_VALUE = time(1, 59, 0) self.assertEqual(self.config.INT_VALUE, 1) # this should be the default value self.assertEqual(self.config.BOOL_VALUE, False) self.assertEqual(self.config.STRING_VALUE, "Hello world") # this should be the default value self.assertEqual(self.config.DECIMAL_VALUE, Decimal("1.2")) self.assertEqual(self.config.DATETIME_VALUE, datetime(1977, 10, 2)) self.assertEqual(self.config.FLOAT_VALUE, 3.1415926536) # this should be the default value self.assertEqual(self.config.DATE_VALUE, date(2001, 12, 20)) self.assertEqual(self.config.TIME_VALUE, time(1, 59, 0)) self.assertEqual(self.config.TIMEDELTA_VALUE, timedelta(days=1, hours=2, minutes=3)) def test_backend_retrieves_multiple_values(self): # Check corner cases such as falsy values self.config.INT_VALUE = 0 self.config.BOOL_VALUE = False self.config.STRING_VALUE = "" values = self.config._backend.mget(settings.CONFIG) self.assertEqual(values["INT_VALUE"], 0) self.assertEqual(values["BOOL_VALUE"], False) self.assertEqual(values["STRING_VALUE"], "") def test_backend_does_not_return_none_values(self): result = self.config._backend.mget(settings.CONFIG) self.assertEqual(result, {}) django-commons-django-constance-1032c50/tests/test_admin.py000066400000000000000000000515721527040271600237600ustar00rootroot00000000000000import json from datetime import datetime from unittest import mock from django.contrib import admin from django.contrib.admin.models import CHANGE from django.contrib.admin.models import LogEntry from django.contrib.auth.models import Permission from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.template.defaultfilters import linebreaksbr from django.test import RequestFactory from django.test import TestCase from django.urls import resolve from django.urls import reverse from django.utils.translation import gettext_lazy as _ from constance import settings from constance.admin import Config from constance.forms import ConstanceForm from constance.utils import get_values class TestAdmin(TestCase): model = Config def setUp(self): super().setUp() self.rf = RequestFactory() self.superuser = User.objects.create_superuser("admin", "nimda", "a@a.cz") self.normaluser = User.objects.create_user("normal", "nimda", "b@b.cz") self.normaluser.is_staff = True self.normaluser.save() self.options = admin.site._registry[self.model] # Clear ContentType cache to avoid stale content_type_id references # across tests wrapped in transactions. ContentType.objects.clear_cache() def test_changelist(self): self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertEqual(response.status_code, 200) def test_custom_auth(self): settings.SUPERUSER_ONLY = False self.client.login(username="normal", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.normaluser self.assertRaises(PermissionDenied, self.options.changelist_view, request, {}) self.assertFalse(request.user.has_perm("constance.change_config")) # reload user to reset permission cache request = self.rf.get("/admin/constance/config/") request.user = User.objects.get(pk=self.normaluser.pk) request.user.user_permissions.add(Permission.objects.get(codename="change_config")) self.assertTrue(request.user.has_perm("constance.change_config")) response = self.options.changelist_view(request, {}) self.assertEqual(response.status_code, 200) def test_linebreaks(self): self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, "LINEBREAK_VALUE") self.assertContains(response, linebreaksbr("eggs\neggs")) @mock.patch( "constance.settings.CONFIG_FIELDSETS", { "Numbers": ("INT_VALUE",), "Text": ("STRING_VALUE",), }, ) def test_fieldset_headers(self): self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, "Numbers") self.assertContains(response, "Text") @mock.patch( "constance.settings.CONFIG_FIELDSETS", ( ("Numbers", ("INT_VALUE",)), ("Text", ("STRING_VALUE",)), ), ) def test_fieldset_tuple(self): self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, "Numbers") self.assertContains(response, "Text") @mock.patch( "constance.settings.CONFIG_FIELDSETS", { "Numbers": { "fields": ( "INT_VALUE", "DECIMAL_VALUE", ), "collapse": True, }, "Text": { "fields": ( "STRING_VALUE", "LINEBREAK_VALUE", ), "collapse": True, }, }, ) def test_collapsed_fieldsets(self): self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) self.assertContains(response, "module collapse") @mock.patch("constance.settings.CONFIG_FIELDSETS", {"FieldSetOne": ("INT_VALUE",)}) @mock.patch( "constance.settings.CONFIG", { "INT_VALUE": (1, "some int"), }, ) @mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True) @mock.patch("constance.forms.ConstanceForm.save", lambda _: []) @mock.patch("constance.forms.ConstanceForm.is_valid", lambda _: True) def test_submit(self): """ Test that submitting the admin page results in an http redirect when everything is in order. """ initial_value = {"INT_VALUE": settings.CONFIG["INT_VALUE"][0]} self.client.login(username="admin", password="nimda") request = self.rf.post( "/admin/constance/config/", data={ **initial_value, "version": "123", }, ) request.user = self.superuser request._dont_enforce_csrf_checks = True with ( mock.patch("django.contrib.messages.add_message") as mock_message, mock.patch.object(ConstanceForm, "__init__", **initial_value, return_value=None) as mock_form, ): response = self.options.changelist_view(request, {}) mock_form.assert_called_with(data=request.POST, files=request.FILES, initial=initial_value, request=request) mock_message.assert_called_with(request, 25, _("Live settings updated successfully.")) self.assertIsInstance(response, HttpResponseRedirect) @mock.patch("constance.settings.CONFIG_FIELDSETS", {"FieldSetOne": ("MULTILINE",)}) @mock.patch( "constance.settings.CONFIG", { "MULTILINE": ("Hello\nWorld", "multiline value"), }, ) @mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True) def test_newlines_normalization(self): self.client.login(username="admin", password="nimda") request = self.rf.post( "/admin/constance/config/", data={ "MULTILINE": "Hello\r\nWorld", "version": "123", }, ) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertIsInstance(response, HttpResponseRedirect) self.assertEqual(get_values()["MULTILINE"], "Hello\nWorld") @mock.patch( "constance.settings.CONFIG", { "DATETIME_VALUE": (datetime(2019, 8, 7, 18, 40, 0), "some naive datetime"), }, ) @mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True) @mock.patch("tests.redis_mockup.Connection.set", mock.MagicMock()) def test_submit_aware_datetime(self): """ Test that submitting the admin page results in an http redirect when everything is in order. """ request = self.rf.post( "/admin/constance/config/", data={ "DATETIME_VALUE_0": "2019-08-07", "DATETIME_VALUE_1": "19:17:01", "version": "123", }, ) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertIsInstance(response, HttpResponseRedirect) @mock.patch( "constance.settings.CONFIG_FIELDSETS", { "Numbers": ("INT_VALUE",), "Text": ("STRING_VALUE",), }, ) def test_inconsistent_fieldset_submit(self): """ Test that the admin page warns users if the CONFIG_FIELDSETS setting doesn't account for every field in CONFIG. """ self.client.login(username="admin", password="nimda") request = self.rf.post("/admin/constance/config/", data=None) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertContains(response, "is missing field(s)") @mock.patch( "constance.settings.CONFIG_FIELDSETS", { "Fieldsets": ( "STRING_VALUE", "INT_VALUE", ), }, ) def test_fieldset_ordering_1(self): """Ordering of inner list should be preserved.""" self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) response.render() content_str = response.content.decode() self.assertGreater(content_str.find("INT_VALUE"), content_str.find("STRING_VALUE")) @mock.patch( "constance.settings.CONFIG_FIELDSETS", { "Fieldsets": ( "INT_VALUE", "STRING_VALUE", ), }, ) def test_fieldset_ordering_2(self): """Ordering of inner list should be preserved.""" self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) response.render() content_str = response.content.decode() self.assertGreater(content_str.find("STRING_VALUE"), content_str.find("INT_VALUE")) @mock.patch( "constance.settings.ADDITIONAL_FIELDS", { "language_select": [ "django.forms.fields.TypedMultipleChoiceField", { "widget": "django.forms.CheckboxSelectMultiple", "choices": (("en", "English"), ("de", "German"), ("fr", "French")), "coerce": str, }, ], }, ) @mock.patch( "constance.settings.CONFIG", { "LANGUAGES": (["en", "de"], "Supported languages", "language_select"), }, ) def test_reset_to_default_multi_select(self): """ Test that multi-select config values render with data-field-type='multi-select' and a JSON-encoded data-default attribute. """ # Re-parse additional fields so the mock is picked up by the form from constance.forms import FIELDS from constance.forms import parse_additional_fields FIELDS.update( parse_additional_fields( { "language_select": [ "django.forms.fields.TypedMultipleChoiceField", { "widget": "django.forms.CheckboxSelectMultiple", "choices": (("en", "English"), ("de", "German"), ("fr", "French")), "coerce": str, }, ] } ) ) try: self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) response.render() content = response.content.decode() self.assertIn('data-field-type="multi-select"', content) self.assertIn('data-default="["en", "de"]"', content) finally: # Clean up FIELDS to avoid leaking into other tests FIELDS.pop("language_select", None) @mock.patch( "constance.settings.ADDITIONAL_FIELDS", { "language_select": [ "django.forms.fields.TypedMultipleChoiceField", { "widget": "django.forms.CheckboxSelectMultiple", "choices": (("en", "English"), ("de", "German"), ("fr", "French")), "coerce": str, }, ], }, ) @mock.patch( "constance.settings.CONFIG", { "LANGUAGES": (("en", "de"), "Supported languages", "language_select"), }, ) def test_reset_to_default_multi_select_tuple(self): """ Test that multi-select config values with a tuple default serialize correctly to a flat JSON list. """ from constance.forms import FIELDS from constance.forms import parse_additional_fields FIELDS.update( parse_additional_fields( { "language_select": [ "django.forms.fields.TypedMultipleChoiceField", { "widget": "django.forms.CheckboxSelectMultiple", "choices": (("en", "English"), ("de", "German"), ("fr", "French")), "coerce": str, }, ] } ) ) try: self.client.login(username="admin", password="nimda") request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request, {}) response.render() content = response.content.decode() self.assertIn('data-field-type="multi-select"', content) self.assertIn('data-default="["en", "de"]"', content) finally: FIELDS.pop("language_select", None) @mock.patch("constance.settings.CONFIG_FIELDSETS", {"FieldSetOne": ("INT_VALUE", "STRING_VALUE")}) @mock.patch( "constance.settings.CONFIG", { "INT_VALUE": (1, "some int"), "STRING_VALUE": ("Hello world", "greetings"), }, ) @mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True) @mock.patch("constance.forms.ConstanceForm.save", lambda _: ["INT_VALUE"]) @mock.patch("constance.forms.ConstanceForm.is_valid", lambda _: True) def test_log_entry_created_on_change(self): """Test that a valid LogEntry is created when config values are changed.""" request = self.rf.post( "/admin/constance/config/", data={ "INT_VALUE": "42", "STRING_VALUE": "Hello world", "version": "123", }, ) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertIsInstance(response, HttpResponseRedirect) log_entry = LogEntry.objects.latest("pk") self.assertEqual(log_entry.user, self.superuser) self.assertEqual(log_entry.action_flag, CHANGE) self.assertEqual(log_entry.object_repr, "Config") # Verify change_message uses Django's standard JSON format # so that get_change_message() can render it correctly. self.assertEqual( log_entry.get_change_message(), "Changed INT_VALUE.", ) @mock.patch("constance.settings.CONFIG_FIELDSETS", {"FieldSetOne": ("INT_VALUE",)}) @mock.patch( "constance.settings.CONFIG", { "INT_VALUE": (1, "some int"), }, ) @mock.patch("constance.settings.IGNORE_ADMIN_VERSION_CHECK", True) @mock.patch("constance.forms.ConstanceForm.save", lambda _: []) @mock.patch("constance.forms.ConstanceForm.is_valid", lambda _: True) def test_no_log_entry_when_no_changes(self): """Test that no LogEntry is created when the form is saved without any changes.""" initial_count = LogEntry.objects.count() request = self.rf.post( "/admin/constance/config/", data={ "INT_VALUE": "1", "version": "123", }, ) request.user = self.superuser request._dont_enforce_csrf_checks = True with mock.patch("django.contrib.messages.add_message"): response = self.options.changelist_view(request, {}) self.assertIsInstance(response, HttpResponseRedirect) self.assertEqual(LogEntry.objects.count(), initial_count) def test_history_view(self): """Test that the history view renders and shows LogEntry records.""" ct = ContentType.objects.get_for_model(self.model) LogEntry.objects.create( user_id=self.superuser.pk, content_type_id=ct.pk, object_id="Config", object_repr="Config", action_flag=CHANGE, change_message=json.dumps([{"changed": {"fields": ["INT_VALUE"]}}]), ) request = self.rf.get("/admin/constance/config/history/") request.user = self.superuser response = self.options.history_view(request) self.assertEqual(response.status_code, 200) response.render() content = response.content.decode() self.assertIn("INT_VALUE", content) self.assertIn("History", content) def test_history_view_fetches_users_with_entries(self): """Test that the history view loads each entry's user in the same query.""" ct = ContentType.objects.get_for_model(self.model) editor = User.objects.create_user("editor", "editor@example.com", "editor") for user in (self.superuser, editor): LogEntry.objects.create( user_id=user.pk, content_type_id=ct.pk, object_id="Config", object_repr="Config", action_flag=CHANGE, change_message=json.dumps([{"changed": {"fields": ["INT_VALUE"]}}]), ) request = self.rf.get("/admin/constance/config/history/") request.user = self.superuser response = self.options.history_view(request) entries = list(response.context_data["action_list"]) with self.assertNumQueries(0): usernames = {entry.user.get_username() for entry in entries} self.assertEqual(usernames, {self.superuser.get_username(), "editor"}) def test_history_view_empty(self): """Test that the history view renders correctly with no entries.""" request = self.rf.get("/admin/constance/config/history/") request.user = self.superuser response = self.options.history_view(request) self.assertEqual(response.status_code, 200) response.render() content = response.content.decode() self.assertIn("doesn't have a change history", content) def test_history_view_permission_denied(self): """Test that the history view denies access to users without permission.""" unprivileged = User.objects.create_user("noperm", "noperm", "c@c.cz") request = self.rf.get("/admin/constance/config/history/") request.user = unprivileged with self.assertRaises(PermissionDenied): self.options.history_view(request) def test_changelist_has_history_link(self): """Test that the changelist page contains a link to the history view.""" request = self.rf.get("/admin/constance/config/") request.user = self.superuser response = self.options.changelist_view(request) response.render() content = response.content.decode() history_url = reverse("admin:constance_config_history") self.assertIn(f'href="{history_url}"', content) self.assertIn("History", content) def test_change_url_redirects_to_changelist(self): """Test that the change URL (used by 'Recent actions') redirects to the changelist.""" url = reverse("admin:constance_config_change", args=["Config"]) self.assertIn("Config/change/", url) request = self.rf.get(url) request.user = self.superuser # The change URL is a simple lambda redirect, so invoke it via URL resolution. match = resolve(url) response = match.func(request, object_id="Config") self.assertEqual(response.status_code, 302) self.assertEqual(response.url, "../../") def test_labels(self): self.assertEqual(type(self.model._meta.label), str) self.assertEqual(type(self.model._meta.label_lower), str) django-commons-django-constance-1032c50/tests/test_async.py000066400000000000000000000266771527040271600240150ustar00rootroot00000000000000import warnings from django.test import TransactionTestCase from django.test import override_settings from constance import config from constance import utils from constance.base import AsyncValueProxy class AsyncTestCase(TransactionTestCase): async def test_async_get(self): # Accessing an attribute on config should be awaitable when in async context val = await config.INT_VALUE self.assertEqual(val, 1) async def test_async_set(self): await config.aset("INT_VALUE", 42) val = await config.INT_VALUE self.assertEqual(val, 42) # Verify sync access also works (and emits warning) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") sync_val = int(config.INT_VALUE) self.assertEqual(sync_val, 42) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_amget(self): values = await config.amget(["INT_VALUE", "BOOL_VALUE"]) self.assertEqual(values["INT_VALUE"], 1) self.assertEqual(values["BOOL_VALUE"], True) async def test_sync_math_in_async_loop(self): # Accessing math should work but emit warning with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") res = config.INT_VALUE + 10 # Note: res will be 42 + 10 if test_async_set ran before, or 1 + 10 if not. # TransactionTestCase should reset state, but let's be careful. # config.INT_VALUE defaults to 1. self.assertEqual(res, 11 if res < 50 else 52) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_utils_aget_values(self): values = await utils.aget_values() self.assertIn("INT_VALUE", values) self.assertIn("BOOL_VALUE", values) self.assertEqual(values["INT_VALUE"], 1) async def test_utils_aget_values_for_keys(self): values = await utils.aget_values_for_keys(["INT_VALUE"]) self.assertEqual(len(values), 1) self.assertEqual(values["INT_VALUE"], 1) async def test_bool_proxy(self): # BOOL_VALUE is True by default self.assertTrue(config.BOOL_VALUE) async def test_int_proxy(self): await config.aset("INT_VALUE", 1) self.assertEqual(int(config.INT_VALUE), 1) async def test_container_proxy(self): # LIST_VALUE is [1, "1", date(2019, 1, 1)] by default self.assertEqual(config.LIST_VALUE[0], 1) self.assertEqual(len(config.LIST_VALUE), 3) self.assertIn(1, config.LIST_VALUE) self.assertEqual(next(iter(config.LIST_VALUE)), 1) class AsyncValueProxyTestCase(TransactionTestCase): """Tests for AsyncValueProxy dunder methods in async context.""" async def test_str_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = str(config.STRING_VALUE) self.assertEqual(result, "Hello world") self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_repr_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = repr(config.STRING_VALUE) self.assertEqual(result, "'Hello world'") self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_float_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = float(config.FLOAT_VALUE) self.assertAlmostEqual(result, 3.1415926536) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_eq_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE == 1 self.assertTrue(result) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_ne_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE != 2 self.assertTrue(result) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_lt_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE < 10 self.assertTrue(result) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_le_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE <= 1 self.assertTrue(result) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_gt_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE > 0 self.assertTrue(result) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_ge_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE >= 1 self.assertTrue(result) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_hash_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = hash(config.INT_VALUE) self.assertEqual(result, hash(1)) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_sub_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE - 1 self.assertEqual(result, 0) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_mul_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE * 5 self.assertEqual(result, 5) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_truediv_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE / 1 self.assertEqual(result, 1.0) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_floordiv_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE // 1 self.assertEqual(result, 1) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_mod_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = config.INT_VALUE % 1 self.assertEqual(result, 0) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_radd_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = 10 + config.INT_VALUE self.assertEqual(result, 11) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_rsub_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = 10 - config.INT_VALUE self.assertEqual(result, 9) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_rmul_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = 10 * config.INT_VALUE self.assertEqual(result, 10) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_rtruediv_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = 10 / config.INT_VALUE self.assertEqual(result, 10.0) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_rfloordiv_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = 10 // config.INT_VALUE self.assertEqual(result, 10) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_rmod_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") result = 10 % config.INT_VALUE self.assertEqual(result, 0) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_index_proxy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") items = [100, 200, 300] result = items[config.INT_VALUE] self.assertEqual(result, 200) self.assertTrue(any("Synchronous access" in str(warn.message) for warn in w)) async def test_aset_invalid_key(self): with self.assertRaises(AttributeError): await config.aset("INVALID_KEY", 42) class AsyncUtilsTestCase(TransactionTestCase): """Tests for async utility functions.""" async def test_aget_values_for_keys_invalid_type(self): with self.assertRaises(TypeError): await utils.aget_values_for_keys("key1") async def test_aget_values_for_keys_missing_key(self): with self.assertRaises(AttributeError) as ctx: await utils.aget_values_for_keys(["INVALID_KEY"]) self.assertIn("INVALID_KEY", str(ctx.exception)) async def test_aget_values_for_keys_empty(self): result = await utils.aget_values_for_keys([]) self.assertEqual(result, {}) class ConfigBaseTestCase(TransactionTestCase): """Tests for Config class edge cases.""" def test_config_dir(self): # Test __dir__ method keys = dir(config) self.assertIn("INT_VALUE", keys) self.assertIn("BOOL_VALUE", keys) def test_access_backend_attribute(self): # Test accessing _backend attribute in sync context backend = config._backend self.assertIsNotNone(backend) class DetectAsyncioTestCase(TransactionTestCase): """Tests for CONSTANCE_DETECT_ASYNCIO setting.""" @override_settings(CONSTANCE_DETECT_ASYNCIO=False) async def test_detect_asyncio_disabled_returns_sync_value_in_async_context(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") val = config.INT_VALUE self.assertIsInstance(val, int) self.assertEqual(val, 1) self.assertEqual(len(w), 0) @override_settings(CONSTANCE_DETECT_ASYNCIO=True) async def test_detect_asyncio_enabled_returns_proxy_in_async_context(self): val = config.INT_VALUE self.assertIsInstance(val, AsyncValueProxy) django-commons-django-constance-1032c50/tests/test_checks.py000066400000000000000000000040731527040271600241220ustar00rootroot00000000000000from unittest import mock from django.test import TestCase from constance import settings from constance.checks import check_fieldsets from constance.checks import get_inconsistent_fieldnames class ChecksTestCase(TestCase): @mock.patch("constance.settings.CONFIG_FIELDSETS", {"Set1": settings.CONFIG.keys()}) def test_get_inconsistent_fieldnames_none(self): """ Test that get_inconsistent_fieldnames returns an empty data and no checks fail if CONFIG_FIELDSETS accounts for every key in settings.CONFIG. """ missing_keys, extra_keys = get_inconsistent_fieldnames() self.assertFalse(missing_keys) self.assertFalse(extra_keys) @mock.patch( "constance.settings.CONFIG_FIELDSETS", {"Set1": list(settings.CONFIG.keys())[:-1]}, ) def test_get_inconsistent_fieldnames_for_missing_keys(self): """ Test that get_inconsistent_fieldnames returns data and the check fails if CONFIG_FIELDSETS does not account for every key in settings.CONFIG. """ missing_keys, extra_keys = get_inconsistent_fieldnames() self.assertTrue(missing_keys) self.assertFalse(extra_keys) self.assertEqual(1, len(check_fieldsets())) @mock.patch( "constance.settings.CONFIG_FIELDSETS", {"Set1": [*settings.CONFIG.keys(), "FORGOTTEN_KEY"]}, ) def test_get_inconsistent_fieldnames_for_extra_keys(self): """ Test that get_inconsistent_fieldnames returns data and the check fails if CONFIG_FIELDSETS contains extra key that is absent in settings.CONFIG. """ missing_keys, extra_keys = get_inconsistent_fieldnames() self.assertFalse(missing_keys) self.assertTrue(extra_keys) self.assertEqual(1, len(check_fieldsets())) @mock.patch("constance.settings.CONFIG_FIELDSETS", {}) def test_check_fieldsets(self): """check_fieldsets should not output warning if CONFIG_FIELDSETS is not defined.""" del settings.CONFIG_FIELDSETS self.assertEqual(0, len(check_fieldsets())) django-commons-django-constance-1032c50/tests/test_cli.py000066400000000000000000000110051527040271600234220ustar00rootroot00000000000000import contextlib from datetime import datetime from io import StringIO from textwrap import dedent from django.conf import settings from django.core.management import CommandError from django.core.management import call_command from django.test import TransactionTestCase from django.test import override_settings from django.utils import timezone from django.utils.encoding import smart_str from constance import config from constance.models import Constance class CliTestCase(TransactionTestCase): def setUp(self): self.out = StringIO() def test_help(self): with contextlib.suppress(SystemExit): call_command("constance", "--help") def test_list(self): call_command("constance", "list", stdout=self.out) self.assertEqual( set(self.out.getvalue().splitlines()), set( dedent( smart_str( """ BOOL_VALUE\tTrue EMAIL_VALUE\ttest@example.com INT_VALUE\t1 LINEBREAK_VALUE\tSpam spam DATE_VALUE\t2010-12-24 TIME_VALUE\t23:59:59 TIMEDELTA_VALUE\t1 day, 2:03:00 STRING_VALUE\tHello world CHOICE_VALUE\tyes DECIMAL_VALUE\t0.1 DATETIME_VALUE\t2010-08-23 11:29:24 FLOAT_VALUE\t3.1415926536 JSON_VALUE\t{'key': 'value', 'key2': 2, 'key3': [1, 2, 3], 'key4': {'key': 'value'}, 'key5': datetime.date(2019, 1, 1), 'key6': None} LIST_VALUE\t[1, '1', datetime.date(2019, 1, 1)] """ # noqa: E501 ) ).splitlines() ), ) def test_get(self): call_command("constance", *(["get", "EMAIL_VALUE"]), stdout=self.out) self.assertEqual(self.out.getvalue().strip(), "test@example.com") def test_set(self): call_command("constance", *(["set", "EMAIL_VALUE", "blah@example.com"]), stdout=self.out) self.assertEqual(config.EMAIL_VALUE, "blah@example.com") call_command("constance", *("set", "DATETIME_VALUE", "2011-09-24", "12:30:25"), stdout=self.out) expected = datetime(2011, 9, 24, 12, 30, 25) if settings.USE_TZ: expected = timezone.make_aware(expected) self.assertEqual(config.DATETIME_VALUE, expected) def test_get_invalid_name(self): self.assertRaisesMessage( CommandError, "NOT_A_REAL_CONFIG is not defined in settings.CONSTANCE_CONFIG", call_command, "constance", "get", "NOT_A_REAL_CONFIG", ) def test_set_invalid_name(self): self.assertRaisesMessage( CommandError, "NOT_A_REAL_CONFIG is not defined in settings.CONSTANCE_CONFIG", call_command, "constance", "set", "NOT_A_REAL_CONFIG", "foo", ) def test_set_invalid_value(self): self.assertRaisesMessage( CommandError, "Enter a valid email address.", call_command, "constance", "set", "EMAIL_VALUE", "not a valid email", ) def test_set_invalid_multi_value(self): self.assertRaisesMessage( CommandError, "Enter a list of values.", call_command, "constance", "set", "DATETIME_VALUE", "2011-09-24 12:30:25", ) def test_delete_stale_records(self): self._populate_database_with_default_values() initial_count = Constance.objects.count() Constance.objects.create(key="STALE_KEY", value=None) call_command("constance", "remove_stale_keys", stdout=self.out) self.assertEqual(Constance.objects.count(), initial_count, msg=self.out) @override_settings( CONSTANCE_DATABASE_PREFIX="constance:", ) def test_delete_stale_records_respects_prefix(self): self._populate_database_with_default_values() initial_count = Constance.objects.count() call_command("constance", "remove_stale_keys", stdout=self.out) self.assertEqual(Constance.objects.count(), initial_count, msg=self.out) def _populate_database_with_default_values(self): """ Helper function to populate the database with default values defined in settings since that's not done automatically at startup """ for key, (value, *_) in settings.CONSTANCE_CONFIG.items(): Constance.objects.create(key=f"{getattr(settings, 'CONSTANCE_DATABASE_PREFIX', '')}{key}", value=value) django-commons-django-constance-1032c50/tests/test_codecs.py000066400000000000000000000117351527040271600241250ustar00rootroot00000000000000import uuid from datetime import date from datetime import datetime from datetime import time from datetime import timedelta from decimal import Decimal from unittest import TestCase from constance.codecs import dumps from constance.codecs import loads from constance.codecs import register_type class TestJSONSerialization(TestCase): def setUp(self): self.datetime = datetime(2023, 10, 5, 15, 30, 0) self.date = date(2023, 10, 5) self.time = time(15, 30, 0) self.decimal = Decimal("10.5") self.uuid = uuid.UUID("12345678123456781234567812345678") self.string = "test" self.integer = 42 self.float = 3.14 self.boolean = True self.none = None self.timedelta = timedelta(days=1, hours=2, minutes=3) self.list = [1, 2, self.date] self.dict = {"key": self.date, "key2": 1} def test_serializes_and_deserializes_default_types(self): self.assertEqual(dumps(self.datetime), '{"__type__": "datetime", "__value__": "2023-10-05T15:30:00"}') self.assertEqual(dumps(self.date), '{"__type__": "date", "__value__": "2023-10-05"}') self.assertEqual(dumps(self.time), '{"__type__": "time", "__value__": "15:30:00"}') self.assertEqual(dumps(self.decimal), '{"__type__": "decimal", "__value__": "10.5"}') self.assertEqual(dumps(self.uuid), '{"__type__": "uuid", "__value__": "12345678123456781234567812345678"}') self.assertEqual(dumps(self.string), '{"__type__": "default", "__value__": "test"}') self.assertEqual(dumps(self.integer), '{"__type__": "default", "__value__": 42}') self.assertEqual(dumps(self.float), '{"__type__": "default", "__value__": 3.14}') self.assertEqual(dumps(self.boolean), '{"__type__": "default", "__value__": true}') self.assertEqual(dumps(self.none), '{"__type__": "default", "__value__": null}') self.assertEqual(dumps(self.timedelta), '{"__type__": "timedelta", "__value__": 93780.0}') self.assertEqual( dumps(self.list), '{"__type__": "default", "__value__": [1, 2, {"__type__": "date", "__value__": "2023-10-05"}]}', ) self.assertEqual( dumps(self.dict), '{"__type__": "default", "__value__": {"key": {"__type__": "date", "__value__": "2023-10-05"}, "key2": 1}}', ) for t in ( self.datetime, self.date, self.time, self.decimal, self.uuid, self.string, self.integer, self.float, self.boolean, self.none, self.timedelta, self.dict, self.list, ): self.assertEqual(t, loads(dumps(t))) def test_invalid_deserialization(self): with self.assertRaisesRegex(ValueError, "Expecting value"): loads("THIS_IS_NOT_RIGHT") with self.assertRaisesRegex(ValueError, "Invalid object"): loads('{"__type__": "THIS_IS_NOT_RIGHT", "__value__": "test", "THIS_IS_NOT_RIGHT": "THIS_IS_NOT_RIGHT"}') with self.assertRaisesRegex(ValueError, "Unsupported type"): loads('{"__type__": "THIS_IS_NOT_RIGHT", "__value__": "test"}') def test_handles_unknown_type(self): class UnknownType: pass with self.assertRaisesRegex(TypeError, "Object of type UnknownType is not JSON serializable"): dumps(UnknownType()) def test_custom_type_serialization(self): class CustomType: def __init__(self, value): self.value = value register_type(CustomType, "custom", lambda o: o.value, lambda o: CustomType(o)) custom_data = CustomType("test") json_data = dumps(custom_data) self.assertEqual(json_data, '{"__type__": "custom", "__value__": "test"}') deserialized_data = loads(json_data) self.assertTrue(isinstance(deserialized_data, CustomType)) self.assertEqual(deserialized_data.value, "test") def test_register_known_type(self): with self.assertRaisesRegex(ValueError, "Discriminator must be specified"): register_type(int, "", lambda o: o.value, lambda o: int(o)) with self.assertRaisesRegex(ValueError, "Type with discriminator default is already registered"): register_type(int, "default", lambda o: o.value, lambda o: int(o)) register_type(int, "new_custom_type", lambda o: o.value, lambda o: int(o)) with self.assertRaisesRegex(ValueError, "Type with discriminator new_custom_type is already registered"): register_type(int, "new_custom_type", lambda o: o.value, lambda o: int(o)) def test_nested_collections(self): data = {"key": [[[[{"key": self.date}]]]]} self.assertEqual( dumps(data), ( '{"__type__": "default", ' '"__value__": {"key": [[[[{"key": {"__type__": "date", "__value__": "2023-10-05"}}]]]]}}' ), ) self.assertEqual(data, loads(dumps(data))) django-commons-django-constance-1032c50/tests/test_form.py000066400000000000000000000032771527040271600236320ustar00rootroot00000000000000from unittest import mock from django.forms import fields from django.test import TestCase from constance.forms import ConstanceForm class TestForm(TestCase): def test_form_field_types(self): f = ConstanceForm({}) self.assertIsInstance(f.fields["INT_VALUE"], fields.IntegerField) self.assertIsInstance(f.fields["BOOL_VALUE"], fields.BooleanField) self.assertIsInstance(f.fields["STRING_VALUE"], fields.CharField) self.assertIsInstance(f.fields["DECIMAL_VALUE"], fields.DecimalField) self.assertIsInstance(f.fields["DATETIME_VALUE"], fields.SplitDateTimeField) self.assertIsInstance(f.fields["TIMEDELTA_VALUE"], fields.DurationField) self.assertIsInstance(f.fields["FLOAT_VALUE"], fields.FloatField) self.assertIsInstance(f.fields["DATE_VALUE"], fields.DateField) self.assertIsInstance(f.fields["TIME_VALUE"], fields.TimeField) # from CONSTANCE_ADDITIONAL_FIELDS self.assertIsInstance(f.fields["CHOICE_VALUE"], fields.ChoiceField) self.assertIsInstance(f.fields["EMAIL_VALUE"], fields.EmailField) @mock.patch("constance.forms.messages.warning") def test_read_only_form_does_not_disable_later_editable_form(self, _mock_warning): read_only_request = mock.Mock() read_only_request.user.has_perm.return_value = False editable_request = mock.Mock() editable_request.user.has_perm.return_value = True read_only_form = ConstanceForm({}, request=read_only_request) editable_form = ConstanceForm({}, request=editable_request) self.assertTrue(read_only_form.fields["INT_VALUE"].disabled) self.assertFalse(editable_form.fields["INT_VALUE"].disabled) django-commons-django-constance-1032c50/tests/test_pytest_overrides.py000066400000000000000000000051351527040271600262740ustar00rootroot00000000000000import unittest try: import pytest from constance import config from constance.test.pytest import override_config class TestPytestOverrideConfigFunctionDecorator: """ Test that the override_config decorator works correctly for Pytest classes. Test usage of override_config on test method and as context manager. """ def test_default_value_is_true(self): """Assert that the default value of config.BOOL_VALUE is True.""" assert config.BOOL_VALUE @pytest.mark.override_config(BOOL_VALUE=False) def test_override_config_on_method_changes_config_value(self): """Assert that the pytest mark decorator changes config.BOOL_VALUE.""" assert not config.BOOL_VALUE def test_override_config_as_context_manager_changes_config_value(self): """Assert that the context manager changes config.BOOL_VALUE.""" with override_config(BOOL_VALUE=False): assert not config.BOOL_VALUE assert config.BOOL_VALUE @override_config(BOOL_VALUE=False) def test_method_decorator(self): """Ensure `override_config` can be used as test method decorator.""" assert not config.BOOL_VALUE @pytest.mark.override_config(BOOL_VALUE=False) class TestPytestOverrideConfigDecorator: """Test that the override_config decorator works on classes.""" def test_override_config_on_class_changes_config_value(self): """Assert that the class decorator changes config.BOOL_VALUE.""" assert not config.BOOL_VALUE @pytest.mark.override_config(BOOL_VALUE="True") def test_override_config_on_overridden_value(self): """Ensure that method mark decorator changes already overridden value for class.""" assert config.BOOL_VALUE == "True" def test_fixture_override_config(override_config): """ Ensure `override_config` fixture is available globally and can be used in test functions. """ with override_config(BOOL_VALUE=False): assert not config.BOOL_VALUE @override_config(BOOL_VALUE=False) def test_func_decorator(): """Ensure `override_config` can be used as test function decorator.""" assert not config.BOOL_VALUE except ImportError: pass class PytestTests(unittest.TestCase): def setUp(self): self.skipTest("Skip all pytest tests when using unittest") def test_do_not_skip_silently(self): """If no at least one test present, unittest silently skips module.""" pass django-commons-django-constance-1032c50/tests/test_test_overrides.py000066400000000000000000000065031527040271600257230ustar00rootroot00000000000000import asyncio from unittest.mock import patch from asgiref.sync import async_to_sync from asgiref.sync import iscoroutinefunction from django.test import TestCase from django.test import TransactionTestCase from constance import config from constance.test import override_config class OverrideConfigFunctionDecoratorTestCase(TestCase): """ Test that the override_config decorator works correctly. Test usage of override_config on test method and as context manager. """ def test_default_value_is_true(self): """Assert that the default value of config.BOOL_VALUE is True.""" self.assertTrue(config.BOOL_VALUE) @override_config(BOOL_VALUE=False) def test_override_config_on_method_changes_config_value(self): """Assert that the method decorator changes config.BOOL_VALUE.""" self.assertFalse(config.BOOL_VALUE) def test_override_config_as_context_manager_changes_config_value(self): """Assert that the context manager changes config.BOOL_VALUE.""" with override_config(BOOL_VALUE=False): self.assertFalse(config.BOOL_VALUE) self.assertTrue(config.BOOL_VALUE) @override_config(BOOL_VALUE=False) class OverrideConfigClassDecoratorTestCase(TestCase): """Test that the override_config decorator works on classes.""" def test_override_config_on_class_changes_config_value(self): """Assert that the class decorator changes config.BOOL_VALUE.""" self.assertFalse(config.BOOL_VALUE) class OverrideConfigAsyncDecoratorTestCase(TransactionTestCase): @override_config(BOOL_VALUE=False) async def test_override_config_on_async_method(self): self.assertFalse(await config.BOOL_VALUE) await asyncio.sleep(0) self.assertFalse(await config.BOOL_VALUE) def test_async_override_lasts_until_await_completes(self): @override_config(BOOL_VALUE=False) async def decorated(): self.assertFalse(await config.BOOL_VALUE) await asyncio.sleep(0) self.assertFalse(await config.BOOL_VALUE) return "completed" self.assertTrue(iscoroutinefunction(decorated)) self.assertEqual(async_to_sync(decorated)(), "completed") self.assertTrue(config.BOOL_VALUE) def test_async_override_restores_after_exception(self): @override_config(BOOL_VALUE=False) async def decorated(): self.assertFalse(await config.BOOL_VALUE) await asyncio.sleep(0) raise ValueError("test failure") with self.assertRaisesMessage(ValueError, "test failure"): async_to_sync(decorated)() self.assertTrue(config.BOOL_VALUE) def test_async_override_with_database_backend(self): from constance import settings from constance.base import Config with patch.object(settings, "BACKEND", "constance.backends.database.DatabaseBackend"): database_config = Config() with patch("constance.test.unittest.config", database_config): @override_config(BOOL_VALUE=False) async def decorated(): self.assertFalse(await database_config.BOOL_VALUE) await asyncio.sleep(0) self.assertFalse(await database_config.BOOL_VALUE) async_to_sync(decorated)() self.assertTrue(database_config.BOOL_VALUE) django-commons-django-constance-1032c50/tests/test_utils.py000066400000000000000000000061751527040271600240270ustar00rootroot00000000000000import datetime from decimal import Decimal from django.core.exceptions import ValidationError from django.test import TestCase from constance.management.commands.constance import _set_constance_value from constance.utils import get_values from constance.utils import get_values_for_keys class UtilsTestCase(TestCase): def test_set_value_validation(self): self.assertRaisesMessage(ValidationError, "Enter a whole number.", _set_constance_value, "INT_VALUE", "foo") self.assertRaisesMessage( ValidationError, "Enter a valid email address.", _set_constance_value, "EMAIL_VALUE", "not a valid email", ) self.assertRaisesMessage( ValidationError, "Enter a valid date.", _set_constance_value, "DATETIME_VALUE", ( "2000-00-00", "99:99:99", ), ) self.assertRaisesMessage( ValidationError, "Enter a valid time.", _set_constance_value, "DATETIME_VALUE", ( "2016-01-01", "99:99:99", ), ) def test_get_values(self): self.assertEqual( get_values(), { "FLOAT_VALUE": 3.1415926536, "BOOL_VALUE": True, "EMAIL_VALUE": "test@example.com", "INT_VALUE": 1, "CHOICE_VALUE": "yes", "TIME_VALUE": datetime.time(23, 59, 59), "DATE_VALUE": datetime.date(2010, 12, 24), "TIMEDELTA_VALUE": datetime.timedelta(days=1, hours=2, minutes=3), "LINEBREAK_VALUE": "Spam spam", "DECIMAL_VALUE": Decimal("0.1"), "STRING_VALUE": "Hello world", "DATETIME_VALUE": datetime.datetime(2010, 8, 23, 11, 29, 24), "LIST_VALUE": [1, "1", datetime.date(2019, 1, 1)], "JSON_VALUE": { "key": "value", "key2": 2, "key3": [1, 2, 3], "key4": {"key": "value"}, "key5": datetime.date(2019, 1, 1), "key6": None, }, }, ) def test_get_values_for_keys(self): self.assertEqual( get_values_for_keys(["BOOL_VALUE", "CHOICE_VALUE", "LINEBREAK_VALUE"]), { "BOOL_VALUE": True, "CHOICE_VALUE": "yes", "LINEBREAK_VALUE": "Spam spam", }, ) def test_get_values_for_keys_empty_keys(self): result = get_values_for_keys([]) self.assertEqual(result, {}) def test_get_values_for_keys_throw_error_if_no_key(self): self.assertRaisesMessage( AttributeError, '"OLD_VALUE, BOLD_VALUE" keys not found in configuration.', get_values_for_keys, ["BOOL_VALUE", "OLD_VALUE", "BOLD_VALUE"], ) def test_get_values_for_keys_invalid_input_type(self): with self.assertRaises(TypeError): get_values_for_keys("key1") django-commons-django-constance-1032c50/tests/urls.py000066400000000000000000000001661527040271600226070ustar00rootroot00000000000000from django.contrib import admin from django.urls import path urlpatterns = [ path("admin/", admin.site.urls), ] django-commons-django-constance-1032c50/tox.ini000066400000000000000000000026161527040271600214230ustar00rootroot00000000000000[tox] isolated_build = true envlist = py{310,311,312}-dj{42}-{unittest,pytest,checkmigrations} py{310,311,312}-dj{50}-{unittest,pytest,checkmigrations} py{310,311,312,313}-dj{51}-{unittest,pytest,checkmigrations} py{310,311,312,313,314}-dj{52}-{unittest,pytest,checkmigrations} py{312,313,314}-dj{60}-{unittest,pytest,checkmigrations} py{312,313,314}-dj{61}-{unittest,pytest,checkmigrations} py{312,313,314}-dj{main}-{unittest,pytest,checkmigrations} skip_missing_interpreters = True [testenv] deps = redis coverage dj42: django>=4.2,<4.3 dj50: django>=5.0,<5.1 dj51: django>=5.1,<5.2 dj52: django>=5.2,<5.3 dj60: django>=6.0,<6.1 dj61: django>=6.1,<6.2 djmain: https://github.com/django/django/archive/main.tar.gz pytest: pytest pytest: pytest-cov pytest: pytest-django usedevelop = True ignore_outcome = djmain: True commands = unittest: coverage run {envbindir}/django-admin test -v2 unittest: coverage report unittest: coverage xml pytest: pytest --cov=. --ignore=.tox --disable-pytest-warnings --cov-report=xml --cov-append {toxinidir} checkmigrations: django-admin makemigrations --check --dry-run setenv = PYTHONPATH = {toxinidir} PYTHONDONTWRITEBYTECODE = 1 DJANGO_SETTINGS_MODULE = tests.settings [gh-actions] python = 3.10: py310 3.11: py311 3.12: py312 3.13: py313 3.14: py314