pax_global_header00006660000000000000000000000064136200170160014506gustar00rootroot0000000000000052 comment=494fb72e42652eeac5b67665ca1767171542e9cf duecredit-0.8.0/000077500000000000000000000000001362001701600134635ustar00rootroot00000000000000duecredit-0.8.0/.coveragerc000066400000000000000000000001161362001701600156020ustar00rootroot00000000000000[run] branch = True source = duecredit include = duecredit/* examples/* duecredit-0.8.0/.github/000077500000000000000000000000001362001701600150235ustar00rootroot00000000000000duecredit-0.8.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000005261362001701600206270ustar00rootroot00000000000000 This pull request fixes # ### Changes - [ ] I ran tests locally and they passed - [ ] If you would like to list yourself as a DueCredit contributor and your name is not mentioned please modify .zenodo.json file. duecredit-0.8.0/.github_changelog_generator000066400000000000000000000001101362001701600210130ustar00rootroot00000000000000future-release=0.5.0 exclude-tags-regex="(debian/.*|\d+\.\d+\.\d+\..*)" duecredit-0.8.0/.gitignore000066400000000000000000000001671362001701600154570ustar00rootroot00000000000000__pycache__ *.py[co] /build /dist /MANIFEST /duecredit/version.py /venv* /.tox .duecredit.p .coverage *.egg-info .idea duecredit-0.8.0/.mailmap000066400000000000000000000001741362001701600151060ustar00rootroot00000000000000Matteo Visconti dOC Matteo Visconti di Oleggio Castello duecredit-0.8.0/.travis.yml000066400000000000000000000030321362001701600155720ustar00rootroot00000000000000# vim ft=yaml # travis-ci.org definition for DueCredit build language: python sudo: false python: - "2.7" - "3.5" - "3.6" - "3.7-dev" # - "pypy" # - "pypy3" cache: - pip before_install: # - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then travis_retry sudo apt-get update -qq; fi git: depth: 99999 install: - if [[ $TRAVIS_PYTHON_VERSION == pypy ]] ; then dpkg --compare-versions $(pypy --version 2>&1 | awk '/PyPy/{print $2;}') ge 2.5.1 || { d=$PWD; cd /tmp; wget --no-check-certificate https://bitbucket.org/pypy/pypy/downloads/pypy-2.5.1-linux64.tar.bz2; tar -xjvf pypy*bz2; cd pypy-*/bin/; export PATH=$PWD:$PATH; cd $d; } ; fi - travis_retry pip install -q coveralls codecov - python setup.py --help # just to trigger generation of .version - pip install -e '.[tests]' - pip install --upgrade flake8 pytest script: #- nosetests --with-doctest --with-cov --cover-package duecredit --logging-level=INFO -v - coverage run --source duecredit -m py.test - python setup.py install # test installation # for now flaking only the stub.py - flake8 duecredit/stub.py after_success: - coveralls - codecov deploy: provider: pypi distributions: sdist user: yarikoptic password: secure: mTxbioGS+sdfxnJRbAGZCxjWlaGJx+KqXPfYGESKcg6IVaSUM9D4CUhxgHHW88FYSnkmCvwuu57w7AAot9FyG6Q/1q656gluCbEJzfDJerSH1S06HqAEmjSPJvIEG/zwvPIUm3RPc+8j9XtedztM3aVWkqBHAzvUzEnsX1jJpic= on: tags: true branch: master repo: duecredit/duecredit condition: "$TRAVIS_PYTHON_VERSION == 2.7 && $TRAVIS_TAG =~ ^[0-9][.][0-9][.0-9]*" duecredit-0.8.0/.zenodo.json000066400000000000000000000026101362001701600157310ustar00rootroot00000000000000{ "creators": [ { "affiliation": "Dartmouth College: Hanover, NH, United States", "name": "Halchenko, Yaroslav O.", "orcid": "0000-0003-3456-2493" }, { "affiliation": "UC Berkeley", "name": "Visconti di Oleggio Castello, Matteo", "orcid": "0000-0001-7931-5272" }, { "affiliation": "Otto-von-Guericke-University Magdeburg, Germany", "name": "Hanke, Michael", "orcid": "0000-0001-6398-6370" }, { "name": "Gors, Jason" }, { "name": "Szczepanik, Michał" }, { "name": "Barnes, Chris" }, { "name": "Irvine, Emily" }, { "affiliation": "Rotman Research Institute, Baycrest Health Sciences, Toronto, ON, Canada", "name": "Raamana, Pradeep Reddy", "orcid": "0000-0003-4662-0558" }, { "affiliation": "Stanford University", "name": "Markiewicz, Christopher J.", "orcid": "0000-0002-6533-164X" }, { "name": "Wilk, Jakub" }, { "name": "Volgyes, David" }, { "name": "Leinweber, Katrin" }, { "name": "Estève, Loïc" }, { "name": "Beckstein, Oliver" }, { "name": "Gulban, Omer Faruk" } ], "grants": [ ], "keywords": [ "citation tracking", "software citation" ], "access_right": "open", "license": "BSD-2-Clause", "upload_type": "software" } duecredit-0.8.0/CHANGELOG.md000066400000000000000000000357511362001701600153070ustar00rootroot00000000000000# Change Log ## [0.8.0](https://github.com/duecredit/duecredit/tree/0.8.0) (2020-02-09) - Variety of small fixes - Added .zenodo.json for more proper citation of duecredit - drop testing for 3.4 -- rare beast, lxml does not provide pkg for it - Support for citing matplotlib via injection - Address a few deprecation warnings (#146) - Provide more informative message whenever using older citeproc without encoding arg ## [0.7.0](https://github.com/duecredit/duecredit/tree/0.7.0) (2019-03-01) - Prevent warnings from the injector's `__del__`. - InactiveDueCollector in `stub.py` now provides also `active=False` attribute (so external tools could directly query if duecredit is active) and no-op `activate` and `dump` for consistent API with a `due` object whenever `duecredit` is available. - Provide `Text` citation entry for free form text. It does not have any meaningful rendering in BibTex but is present in text rendering. `Url` entry also acquired text rendering with prefix `URL: `. ## [0.6.5](https://github.com/duecredit/duecredit/tree/0.6.5) (2019-02-04) - Delay import of imports (thanks [Chris Markiewicz (@effigies)](https://github.com/effigies) - serves also as a workaround due to inconsistent installation of 3rd party modules/libraries such as openssl [\#142](https://github.com/duecredit/duecredit/issues/142) - Use https://doi.org as preferred DOI resolver. Thanks [Katrin Leinweber (@katrinleinweber)](https://github.com/katrinleinweber) for the contribution ## [0.6.4](https://github.com/duecredit/duecredit/tree/0.6.4) (2018-06-25) - Added doi to numpy injection - Minor tune-ups to the docs ## [0.6.3](https://github.com/duecredit/duecredit/tree/0.6.3) (2017-08-01) Fixed a bug disallowing installation of duecredit in environments with crippled/too-basic locale setting. ## [0.6.2](https://github.com/duecredit/duecredit/tree/0.6.2) (2017-06-23) - Testing was converted to pytest - Various enhancements in supporting python3 and BiBTeX with utf-8 - New tag 'dataset' to describe datasets ## [0.6.1](https://github.com/duecredit/duecredit/tree/0.6.1) (2016-07-09) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.6.0...0.6.1) **Merged pull requests:** - ENH: workaround for pages handling fixed in citeproc post 0.3.0 [\#98](https://github.com/duecredit/duecredit/pull/98) ([yarikoptic](https://github.com/yarikoptic)) ## [0.6.0](https://github.com/duecredit/duecredit/tree/0.6.0) (2016-06-17) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.5.0...0.6.0) **Implemented enhancements:** - Support system-specific references [\#81](https://github.com/duecredit/duecredit/issues/81) - export to bibtex doesn't support tags yet [\#19](https://github.com/duecredit/duecredit/issues/19) - ENH: support DUECREDIT\_REPORT\_ALL=1 to report all citations, not only with functionality used [\#92](https://github.com/duecredit/duecredit/pull/92) ([yarikoptic](https://github.com/yarikoptic)) **Fixed bugs:** - Outputting to bibtex doesn't filter by used citations [\#68](https://github.com/duecredit/duecredit/issues/68) - references package even if no cited functions/methods used [\#48](https://github.com/duecredit/duecredit/issues/48) - When injecting multiple citations at the same point, only one referenced [\#47](https://github.com/duecredit/duecredit/issues/47) **Merged pull requests:** - BF: allow multiple injections at the same path, avoid resetting \_orig\_import if already deactivated [\#91](https://github.com/duecredit/duecredit/pull/91) ([yarikoptic](https://github.com/yarikoptic)) - DOC: Update readme to reflect current output of duecredit summary [\#89](https://github.com/duecredit/duecredit/pull/89) ([mvdoc](https://github.com/mvdoc)) - enable codecov coverage reports [\#87](https://github.com/duecredit/duecredit/pull/87) ([yarikoptic](https://github.com/yarikoptic)) - REF,ENH: refactor {BibTeX,Text}Output into Output class with subclasses [\#86](https://github.com/duecredit/duecredit/pull/86) ([mvdoc](https://github.com/mvdoc)) ## [0.5.0](https://github.com/duecredit/duecredit/tree/0.5.0) (2016-05-11) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.8...0.5.0) **Fixed bugs:** - test\_noincorrect\_import\_if\_no\_lxml fails on my laptop \(and on travis\) [\#84](https://github.com/duecredit/duecredit/issues/84) - zenodo and "unofficial" bibtex entry types [\#77](https://github.com/duecredit/duecredit/issues/77) **Closed issues:** - duecredit on nipype [\#72](https://github.com/duecredit/duecredit/issues/72) **Merged pull requests:** - BF: workaround for zenodo bibtex entries imported with import\_doi [\#85](https://github.com/duecredit/duecredit/pull/85) ([mvdoc](https://github.com/mvdoc)) - enable testing under python 3.5 on travis [\#79](https://github.com/duecredit/duecredit/pull/79) ([yarikoptic](https://github.com/yarikoptic)) - ENH: appveyor configuration \(based on shablona's\) based on mix of conda and pip [\#70](https://github.com/duecredit/duecredit/pull/70) ([yarikoptic](https://github.com/yarikoptic)) ## [0.4.8](https://github.com/duecredit/duecredit/tree/0.4.8) (2016-05-04) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.7...0.4.8) **Closed issues:** - Referencing articles with no DOI [\#74](https://github.com/duecredit/duecredit/issues/74) - doi importer doesn't work with zenodo dois [\#73](https://github.com/duecredit/duecredit/issues/73) **Merged pull requests:** - BF: change request command to make it work with zenodo too [\#76](https://github.com/duecredit/duecredit/pull/76) ([mvdoc](https://github.com/mvdoc)) - DOC: Show that user can also enter BibTeX entries [\#75](https://github.com/duecredit/duecredit/pull/75) ([mvdoc](https://github.com/mvdoc)) ## [0.4.7](https://github.com/duecredit/duecredit/tree/0.4.7) (2016-04-21) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.6...0.4.7) ## [0.4.6](https://github.com/duecredit/duecredit/tree/0.4.6) (2016-04-19) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.5...0.4.6) **Fixed bugs:** - In PyMVPA, fail to handle failures if lxml, types not available [\#64](https://github.com/duecredit/duecredit/issues/64) **Merged pull requests:** - Primarily PEP8 for stub.py \(the rest needs more work\) [\#69](https://github.com/duecredit/duecredit/pull/69) ([yarikoptic](https://github.com/yarikoptic)) - Use HTTPS for GitHub URL [\#67](https://github.com/duecredit/duecredit/pull/67) ([jwilk](https://github.com/jwilk)) - Fix typos [\#66](https://github.com/duecredit/duecredit/pull/66) ([jwilk](https://github.com/jwilk)) ## [0.4.5](https://github.com/duecredit/duecredit/tree/0.4.5) (2015-12-03) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.4...0.4.5) **Merged pull requests:** - Make duecredit import and stub more robust to failures with e.g. import of lxml [\#65](https://github.com/duecredit/duecredit/pull/65) ([yarikoptic](https://github.com/yarikoptic)) ## [0.4.4](https://github.com/duecredit/duecredit/tree/0.4.4) (2015-11-08) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.3...0.4.4) ## [0.4.3](https://github.com/duecredit/duecredit/tree/0.4.3) (2015-09-28) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.2...0.4.3) **Implemented enhancements:** - Make "conditions" even more powerful [\#36](https://github.com/duecredit/duecredit/issues/36) **Merged pull requests:** - add mod\_ files for nibabel, nipy, nipype [\#62](https://github.com/duecredit/duecredit/pull/62) ([jgors](https://github.com/jgors)) - fixed headers for injections/mod\_ files and added item to .gitignore [\#60](https://github.com/duecredit/duecredit/pull/60) ([jgors](https://github.com/jgors)) - ENH+BF: recently introduced entries got fixed up [\#59](https://github.com/duecredit/duecredit/pull/59) ([yarikoptic](https://github.com/yarikoptic)) - add mod\_\* files [\#58](https://github.com/duecredit/duecredit/pull/58) ([jgors](https://github.com/jgors)) - ENH: versions -- provide dumps, keys, \_\_contains\_\_ [\#57](https://github.com/duecredit/duecredit/pull/57) ([yarikoptic](https://github.com/yarikoptic)) - ENH: Two more module level injections [\#56](https://github.com/duecredit/duecredit/pull/56) ([yarikoptic](https://github.com/yarikoptic)) ## [0.4.2](https://github.com/duecredit/duecredit/tree/0.4.2) (2015-09-03) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.1...0.4.2) **Closed issues:** - we should output description \(not just path\) in the listing [\#49](https://github.com/duecredit/duecredit/issues/49) **Merged pull requests:** - BF: print description, not just path. Closes \#49 [\#52](https://github.com/duecredit/duecredit/pull/52) ([yarikoptic](https://github.com/yarikoptic)) - Overhaul conditions -- "and" logic \(all must be met\) + allow to access attributes of the arguments [\#50](https://github.com/duecredit/duecredit/pull/50) ([yarikoptic](https://github.com/yarikoptic)) - BF: Fix get\_text\_rendering when Citation is passed with Doi [\#46](https://github.com/duecredit/duecredit/pull/46) ([mvdoc](https://github.com/mvdoc)) ## [0.4.1](https://github.com/duecredit/duecredit/tree/0.4.1) (2015-08-27) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.4.0...0.4.1) ## [0.4.0](https://github.com/duecredit/duecredit/tree/0.4.0) (2015-08-21) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.3.0...0.4.0) **Fixed bugs:** - Cross-referencing does not work [\#30](https://github.com/duecredit/duecredit/issues/30) **Closed issues:** - DUECREDIT\_ENABLE doesn't work anymore [\#45](https://github.com/duecredit/duecredit/issues/45) - test\_no\_double\_activation on injector fails on travis and locally with Python 2.7.{6,9} [\#43](https://github.com/duecredit/duecredit/issues/43) - possible bug \(race condition\) in injector's \_\_import\_\_ handling [\#40](https://github.com/duecredit/duecredit/issues/40) **Merged pull requests:** - ENH+DOC: always check if \_orig\_import is not None \(Closes \#40\) [\#44](https://github.com/duecredit/duecredit/pull/44) ([yarikoptic](https://github.com/yarikoptic)) - \[Injections\] Add all references for sklearn.cluster [\#42](https://github.com/duecredit/duecredit/pull/42) ([mvdoc](https://github.com/mvdoc)) - REF: text output, divide "model" from "view" [\#41](https://github.com/duecredit/duecredit/pull/41) ([mvdoc](https://github.com/mvdoc)) - RF to provide \_\_main\_\_ so we could do python -m duecredit existing script [\#39](https://github.com/duecredit/duecredit/pull/39) ([yarikoptic](https://github.com/yarikoptic)) ## [0.3.0](https://github.com/duecredit/duecredit/tree/0.3.0) (2015-08-05) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.2.2...0.3.0) **Implemented enhancements:** - automagically upload releases to pypi from travis [\#6](https://github.com/duecredit/duecredit/issues/6) **Fixed bugs:** - while "dump"ing -- references shouldn't be duplicated even if used in multiple modules [\#23](https://github.com/duecredit/duecredit/issues/23) **Closed issues:** - Syntax error in test\_utils.py with python 2.7.6 [\#26](https://github.com/duecredit/duecredit/issues/26) - Travis skips tests [\#25](https://github.com/duecredit/duecredit/issues/25) **Merged pull requests:** - RF: cite-on-import -\> cite-module since we might be dealing with other languages etc [\#37](https://github.com/duecredit/duecredit/pull/37) ([yarikoptic](https://github.com/yarikoptic)) - Few tune ups to injection and more to its testing [\#35](https://github.com/duecredit/duecredit/pull/35) ([yarikoptic](https://github.com/yarikoptic)) - RF: Donate -\> Url [\#34](https://github.com/duecredit/duecredit/pull/34) ([yarikoptic](https://github.com/yarikoptic)) - TST: check reference numbers are consistent [\#29](https://github.com/duecredit/duecredit/pull/29) ([mvdoc](https://github.com/mvdoc)) - PY3+make vcr optional: more concise use of six, it might take a while for vcr to come to debian [\#28](https://github.com/duecredit/duecredit/pull/28) ([yarikoptic](https://github.com/yarikoptic)) - BF: give correct ref numbers for citations [\#24](https://github.com/duecredit/duecredit/pull/24) ([mvdoc](https://github.com/mvdoc)) - Fix typo in README.md [\#21](https://github.com/duecredit/duecredit/pull/21) ([lesteve](https://github.com/lesteve)) ## [0.2.2](https://github.com/duecredit/duecredit/tree/0.2.2) (2015-07-27) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.2.1...0.2.2) ## [0.2.1](https://github.com/duecredit/duecredit/tree/0.2.1) (2015-07-27) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.2.0...0.2.1) ## [0.2.0](https://github.com/duecredit/duecredit/tree/0.2.0) (2015-07-27) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.1.1...0.2.0) **Closed issues:** - RFC: either rename "kind" and "level" into some thing more descriptive [\#8](https://github.com/duecredit/duecredit/issues/8) - add "classes" \(or tags?\) to citations on what citation is about [\#5](https://github.com/duecredit/duecredit/issues/5) - version for modules still doesn't work [\#2](https://github.com/duecredit/duecredit/issues/2) **Merged pull requests:** - BF: circular import of injector and duecredit [\#17](https://github.com/duecredit/duecredit/pull/17) ([mvdoc](https://github.com/mvdoc)) - Add six to the requirements [\#15](https://github.com/duecredit/duecredit/pull/15) ([mvdoc](https://github.com/mvdoc)) - ENH: conditions kwarg for dcite to condition when to trigger the citation given arguments to the function call [\#14](https://github.com/duecredit/duecredit/pull/14) ([yarikoptic](https://github.com/yarikoptic)) - \[WIP\] Start adding more injections [\#13](https://github.com/duecredit/duecredit/pull/13) ([mvdoc](https://github.com/mvdoc)) - RF arguments for cite: kind -\> tags, level -\> path, use -\> desc [\#12](https://github.com/duecredit/duecredit/pull/12) ([yarikoptic](https://github.com/yarikoptic)) - ENH: try to use new container based Travis infrastructure [\#11](https://github.com/duecredit/duecredit/pull/11) ([yarikoptic](https://github.com/yarikoptic)) - WiP NF: core to implement "injection" of duecredit entries into other modules [\#10](https://github.com/duecredit/duecredit/pull/10) ([yarikoptic](https://github.com/yarikoptic)) - coveralls call should be without any args, also test installation now [\#9](https://github.com/duecredit/duecredit/pull/9) ([yarikoptic](https://github.com/yarikoptic)) ## [0.1.1](https://github.com/duecredit/duecredit/tree/0.1.1) (2015-06-26) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.1.0...0.1.1) ## [0.1.0](https://github.com/duecredit/duecredit/tree/0.1.0) (2015-06-21) [Full Changelog](https://github.com/duecredit/duecredit/compare/0.0.0...0.1.0) **Closed issues:** - fix badges in README.md \(it is not ,rst ;\)\) [\#4](https://github.com/duecredit/duecredit/issues/4) **Merged pull requests:** - Stub tests pass [\#1](https://github.com/duecredit/duecredit/pull/1) ([mvdoc](https://github.com/mvdoc)) ## [0.0.0](https://github.com/duecredit/duecredit/tree/0.0.0) (2013-12-06) \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* duecredit-0.8.0/CONTRIBUTING.md000066400000000000000000000157431362001701600157260ustar00rootroot00000000000000Contributing to DueCredit ========================= [gh-duecredit]: https://github.com/duecredit/duecredit Files organization ------------------ - `duecredit/` is the main Python module where major development is happening, with major submodules being: - `cmdline/` contains commands for the command line interface. See any of the `cmd_*.py` files here for an example - `tests/` all unit- and regression- tests - `utils.py` provides convenience helpers used by unit-tests such as `@with_tree`, `@serve_path_via_http` and other decorators - `tools/` might contain helper utilities used during development, testing, and benchmarking of DueCredit. Implemented in any most appropriate language (Python, bash, etc.) How to contribute ----------------- The preferred way to contribute to the DueCredit code base is to fork the [main repository][gh-duecredit] on GitHub. Here we outline the workflow used by the developers: 0. Have a clone of our main [project repository][gh-duecredit] as `origin` remote in your git: git clone git://github.com/duecredit/duecredit 1. Fork the [project repository][gh-duecredit]: click on the 'Fork' button near the top of the page. This creates a copy of the code base under your account on the GitHub server. 2. Add your forked clone as a remote to the local clone you already have on your local disk: git remote add gh-YourLogin git@github.com:YourLogin/duecredit.git git fetch gh-YourLogin To ease addition of other github repositories as remotes, here is a little bash function/script to add to your `~/.bashrc`: ghremote () { url="$1" proj=${url##*/} url_=${url%/*} login=${url_##*/} git remote add gh-$login $url git fetch gh-$login } thus you could simply run: ghremote git@github.com:YourLogin/duecredit.git to add the above `gh-YourLogin` remote. 3. Create a branch (generally off the `origin/master`) to hold your changes: git checkout -b nf-my-feature and start making changes. Ideally, use a prefix signaling the purpose of the branch - `nf-` for new features - `bf-` for bug fixes - `rf-` for refactoring - `doc-` for documentation contributions (including in the code docstrings). We recommend to not work in the ``master`` branch! 4. Work on this copy on your computer using Git to do the version control. When you're done editing, do: git add modified_files git commit to record your changes in Git. Ideally, prefix your commit messages with the `NF`, `BF`, `RF`, `DOC` similar to the branch name prefixes, but you could also use `TST` for commits concerned solely with tests, and `BK` to signal that the commit causes a breakage (e.g. of tests) at that point. Multiple entries could be listed joined with a `+` (e.g. `rf+doc-`). See `git log` for examples. If a commit closes an existing DueCredit issue, then add to the end of the message `(Closes #ISSUE_NUMER)` 5. Push to GitHub with: git push -u gh-YourLogin nf-my-feature Finally, go to the web page of your fork of the DueCredit repo, and click 'Pull request' (PR) to send your changes to the maintainers for review. This will send an email to the committers. You can commit new changes to this branch and keep pushing to your remote -- github automagically adds them to your previously opened PR. (If any of the above seems like magic to you, then look up the [Git documentation](http://git-scm.com/documentation) on the web.) Quality Assurance ----------------- It is recommended to check that your contribution complies with the following rules before submitting a pull request: - All public methods should have informative docstrings with sample usage presented as doctests when appropriate. - All other tests pass when everything is rebuilt from scratch. - New code should be accompanied by tests. ### Tests All tests are available under `duecredit/tests`. To execute tests, the codebase needs to be "installed" in order to generate scripts for the entry points. For that, the recommended course of action is to use `virtualenv`, e.g. ```sh virtualenv --system-site-packages venv-tests source venv-tests/bin/activate pip install -e '.[tests]' ``` On Debian-based systems you might need to install some C-libraries to guarantee installation (building) of some Python modules we use. So for `lxml` please first ```sh sudo apt-get install libxml2-dev libxslt1-dev ``` On Mac OS X Yosemite additional steps are required to make `lxml` work properly (see [this stackoverflow answer](https://stackoverflow.com/questions/19548011/cannot-install-lxml-on-mac-os-x-10-9/26544099#26544099?newreg=d3394d8210cc4779accfac05fe5c9b21)). We recommend using homebrew to install the same dependencies (see the [Homebrew website](http://brew.sh/) to install it), then run ```sh brew install libxml2 libxslt brew link libxml2 --force brew link libxslt --force ``` note that this will override the default libraries installed with Mac OS X. Then use that virtual environment to run the tests, via ```sh python -m py.test -s -v duecredit ``` or similarly, ```sh py.test -s -v duecredit ``` then to later deactivate the virtualenv just simply enter ```sh deactivate ``` ### Coverage You can also check for common programming errors with the following tools: - Code with good unittest coverage (at least 80%), check with: pip install pytest coverage coverage run --source duecredit -m py.test coverage report ### Linting We are not (yet) fully PEP8 compliant, so please use these tools as guidelines for your contributions, but not to PEP8 entire code base. [beyond-pep8]: https://www.youtube.com/watch?v=wf-BqAjZb8M *Sidenote*: watch [Raymond Hettinger - Beyond PEP 8][beyond-pep8] - No pyflakes warnings, check with: pip install pyflakes pyflakes path/to/module.py - No PEP8 warnings, check with: pip install pep8 pep8 path/to/module.py - AutoPEP8 can help you fix some of the easy redundant errors: pip install autopep8 autopep8 path/to/pep8.py Also, some team developers use [PyCharm community edition](https://www.jetbrains.com/pycharm) which provides built-in PEP8 checker and handy tools such as smart splits/joins making it easier to maintain code following the PEP8 recommendations. NeuroDebian provides `pycharm-community-sloppy` package to ease pycharm installation even further. Easy Issues ----------- A great way to start contributing to DueCredit is to pick an item from the list of [Easy issues](https://github.com/duecredit/duecredit/labels/easy) in the issue tracker. Resolving these issues allows you to start contributing to the project without much prior knowledge. Your assistance in this area will be greatly appreciated by the more experienced developers as it helps free up their time to concentrate on other issues. duecredit-0.8.0/LICENSE000066400000000000000000000030421362001701600144670ustar00rootroot00000000000000Copyright 2015 Yaroslav Halchenko, Matteo Visconti di Oleggio Castello. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 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 HOLDER 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. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the copyright holder. duecredit-0.8.0/MANIFEST.in000066400000000000000000000002751362001701600152250ustar00rootroot00000000000000include LICENSE README.md CHANGELOG.md requirements.txt tox.ini CONTRIBUTING.md setup.cfg include .coveragerc .travis.yml include examples/example_scipy.py include duecredit/tests/envs/*/* duecredit-0.8.0/README.md000066400000000000000000000341731362001701600147520ustar00rootroot00000000000000# duecredit [![Build Status](https://travis-ci.org/duecredit/duecredit.svg?branch=master)](https://travis-ci.org/duecredit/duecredit) [![Coverage Status](https://coveralls.io/repos/duecredit/duecredit/badge.svg)](https://coveralls.io/r/duecredit/duecredit) duecredit is being conceived to address the problem of inadequate citation of scientific software and methods, and limited visibility of donation requests for open-source software. It provides a simple framework (at the moment for Python only) to embed publication or other references in the original code so they are automatically collected and reported to the user at the necessary level of reference detail, i.e. only references for actually used functionality will be presented back if software provides multiple citeable implementations. ## Installation Duecredit is easy to install via pip, simply type: `pip install duecredit` ## Examples ### To cite the modules and methods you are using You can already start "registering" citations using duecredit in your Python modules and even registering citations (we call this approach "injections") for modules which do not (yet) use duecredit. duecredit will remain an optional dependency, i.e. your software will work correctly even without duecredit installed. For example, list citations of the modules and methods `yourproject` uses with few simple commands: ```bash cd /path/to/yourmodule # for ~/yourproject cd yourproject # change directory into where the main code base is python -m duecredit yourproject.py ``` Or you can also display them in BibTex format, using: ```bash duecredit summary --format=bibtex ``` See this gif animation for better illustration: ![Example](examples/duecredit_example.gif) ### To let others cite your software For using duecredit in your software 1. Copy `duecredit/stub.py` to your codebase, e.g. wget -q -O /path/tomodule/yourmodule/due.py \ https://raw.githubusercontent.com/duecredit/duecredit/master/duecredit/stub.py **Note** that it might be better to avoid naming it duecredit.py to avoid shadowing installed duecredit. 2. Then use `duecredit` import due and necessary entries in your code as from .due import due, Doi, BibTeX To provide reference for the entire module just use e.g. due.cite(Doi("1.2.3/x.y.z"), description="Solves all your problems", path="magicpy") To provide a reference for a function or a method, use `dcite` decorator @due.dcite(Doi("1.2.3/x.y.z"), description="Resolves constipation issue") def pushit(): ... You can easily obtain DOI for your software using Zenodo.org and few other DOI providers. References can also be entered as BibTeX entries due.cite(BibTeX(""" @article{mynicearticle, title={A very cool paper}, author={Happy, Author and Lucky, Author}, journal={The Journal of Serendipitous Discoveries} } """), description="Solves all your problems", path="magicpy") ## Now what ### Do the due Once you obtained the references in the duecredit output, include them in in the references section of your paper or software, which used the cited software. ### Add injections for other existing modules We hope that eventually this somewhat cruel approach will not be necessary. But until other packages support duecredit "natively" we have provided a way to "inject" citations for modules and/or functions and methods via injections: citations will be added to the corresponding functionality upon those modules import. All injections are collected under [duecredit/injections](https://github.com/duecredit/duecredit/tree/master/duecredit/injections). See any file there with `mod_` prefix for a complete example. But overall it is just a regular Python module defining a function `inject(injector)` which will then add new entries to the injector, which will in turn add those entries to the duecredit whenever the corresponding module gets imported. ## User-view By default `duecredit` does exactly nothing -- all decorators do not decorate, all `cite` functions just return, so there should be no fear that it would break anything. Then whenever anyone runs their analysis which uses your code and sets `DUECREDIT_ENABLE=yes` environment variable or uses `python -m duecredit`, and invokes any of the cited function/methods, at the end of the run all collected bibliography will be presented to the screen and pickled into `.duecredit.p` file in current directory: $> python -m duecredit examples/example_scipy.py I: Simulating 4 blobs I: Done clustering 4 blobs DueCredit Report: - Scientific tools library / numpy (v 1.10.4) [1] - Scientific tools library / scipy (v 0.14) [2] - Single linkage hierarchical clustering / scipy.cluster.hierarchy:linkage (v 0.14) [3] 2 packages cited 0 modules cited 1 function cited References ---------- [1] Van Der Walt, S., Colbert, S.C. & Varoquaux, G., 2011. The NumPy array: a structure for efficient numerical computation. Computing in Science & Engineering, 13(2), pp.22–30. [2] Jones, E. et al., 2001. SciPy: Open source scientific tools for Python. [3] Sibson, R., 1973. SLINK: an optimally efficient algorithm for the single-link cluster method. The Computer Journal, 16(1), pp.30–34. Incremental runs of various software would keep enriching that file. Then you can use `duecredit summary` command to show that information again (stored in `.duecredit.p` file) or export it as a BibTeX file ready for reuse, e.g.: $> duecredit summary --format=bibtex @article{van2011numpy, title={The NumPy array: a structure for efficient numerical computation}, author={Van Der Walt, Stefan and Colbert, S Chris and Varoquaux, Gael}, journal={Computing in Science \& Engineering}, volume={13}, number={2}, pages={22--30}, year={2011}, publisher={AIP Publishing} } @Misc{JOP+01, author = {Eric Jones and Travis Oliphant and Pearu Peterson and others}, title = {{SciPy}: Open source scientific tools for {Python}}, year = {2001--}, url = "http://www.scipy.org/", note = {[Online; accessed 2015-07-13]} } @article{sibson1973slink, title={SLINK: an optimally efficient algorithm for the single-link cluster method}, author={Sibson, Robin}, journal={The Computer Journal}, volume={16}, number={1}, pages={30--34}, year={1973}, publisher={Br Computer Soc} } and if by default only references for "implementation" are listed, we can enable listing of references for other tags as well (e.g. "edu" depicting instructional materials -- textbooks etc. on the topic): $> DUECREDIT_REPORT_TAGS=* duecredit summary DueCredit Report: - Scientific tools library / numpy (v 1.10.4) [1] - Scientific tools library / scipy (v 0.14) [2] - Hierarchical clustering / scipy.cluster.hierarchy (v 0.14) [3, 4, 5, 6, 7, 8, 9] - Single linkage hierarchical clustering / scipy.cluster.hierarchy:linkage (v 0.14) [10, 11] 2 packages cited 1 module cited 1 function cited References ---------- [1] Van Der Walt, S., Colbert, S.C. & Varoquaux, G., 2011. The NumPy array: a structure for efficient numerical computation. Computing in Science & Engineering, 13(2), pp.22–30. [2] Jones, E. et al., 2001. SciPy: Open source scientific tools for Python. [3] Sneath, P.H. & Sokal, R.R., 1962. Numerical taxonomy. Nature, 193(4818), pp.855–860. [4] Batagelj, V. & Bren, M., 1995. Comparing resemblance measures. Journal of classification, 12(1), pp.73–90. [5] Sokal, R.R., Michener, C.D. & University of Kansas, 1958. A Statistical Method for Evaluating Systematic Relationships, University of Kansas. [6] Jain, A.K. & Dubes, R.C., 1988. Algorithms for clustering data, Prentice-Hall, Inc.. [7] Johnson, S.C., 1967. Hierarchical clustering schemes. Psychometrika, 32(3), pp.241–254. [8] Edelbrock, C., 1979. Mixture model tests of hierarchical clustering algorithms: the problem of classifying everybody. Multivariate Behavioral Research, 14(3), pp.367–384. [9] Fisher, R.A., 1936. The use of multiple measurements in taxonomic problems. Annals of eugenics, 7(2), pp.179–188. [10] Gower, J.C. & Ross, G., 1969. Minimum spanning trees and single linkage cluster analysis. Applied statistics, pp.54–64. [11] Sibson, R., 1973. SLINK: an optimally efficient algorithm for the single-link cluster method. The Computer Journal, 16(1), pp.30–34. The `DUECREDIT_REPORT_ALL` flag allows one to output all the references for the modules that lack objects or functions with citations. Compared to the previous example, the following output additionally shows a reference for scikit-learn since `example_scipy.py` uses an uncited function from that package. $> DUECREDIT_REPORT_TAGS=* DUECREDIT_REPORT_ALL=1 duecredit summary DueCredit Report: - Scientific tools library / numpy (v 1.10.4) [1] - Scientific tools library / scipy (v 0.14) [2] - Hierarchical clustering / scipy.cluster.hierarchy (v 0.14) [3, 4, 5, 6, 7, 8, 9] - Single linkage hierarchical clustering / scipy.cluster.hierarchy:linkage (v 0.14) [10, 11] - Machine Learning library / sklearn (v 0.15.2) [12] 3 packages cited 1 module cited 1 function cited References ---------- [1] Van Der Walt, S., Colbert, S.C. & Varoquaux, G., 2011. The NumPy array: a structure for efficient numerical computation. Computing in Science & Engineering, 13(2), pp.22–30. [2] Jones, E. et al., 2001. SciPy: Open source scientific tools for Python. [3] Sneath, P.H. & Sokal, R.R., 1962. Numerical taxonomy. Nature, 193(4818), pp.855–860. ... ## Tags You are welcome to introduce new tags specific for your citations but we hope that for consistency across projects, you would use following tags - `implementation` (default) — an implementation of the cited method - `reference-implementation` — the original implementation (ideally by the authors of the paper) of the cited method - `another-implementation` — some other implementation of the method, e.g. if you would like to provide citation for another implementation of the method you have implemented in your code and for which you have already provided `implementation` or `reference-implementation` tag - `use` — publications demonstrating a worthwhile noting use of the method - `edu` — tutorials, textbooks and other materials useful to learn more about cited functionality - `donate` — should be commonly used with Url entries to point to the websites describing how to contribute some funds to the referenced project - `funding` — to point to the sources of funding which provided support for a given functionality implementation and/or method development - `dataset` - for datasets ## Ultimate goals ### Reduce demand for prima ballerina projects **Problem**: Scientific software is often developed to gain citations for original publication through the use of the software implementing it. Unfortunately such established procedure discourages contributions to existing projects and fosters new projects to be developed from scratch. **Solution**: With easy ways to provide all-and-only relevant references for used functionality within a large(r) framework, scientific developers will prefer to contribute to already existing projects. **Benefits**: As a result, scientific developers will immediately benefit from adhering to proper development procedures (codebase structuring, testing, etc) and already established delivery and deployment channels existing projects already have. This will increase efficiency and standardization of scientific software development, thus addressing many (if not all) core problems with scientific software development everyone likes to bash about (reproducibility, longevity, etc.). ### Adequately reference core libraries **Problem**: Scientific software often, if not always, uses 3rd party libraries (e.g., NumPy, SciPy, atlas) which might not even be visible at the user level. Therefore they are rarely referenced in the publications despite providing the fundamental core for solving a scientific problem at hands. **Solution**: With automated bibliography compilation for all used libraries, such projects and their authors would get a chance to receive adequate citability. **Benefits**: Adequate appreciation of the scientific software developments. Coupled with a solution for "prima ballerina" problem, more contributions will flow into the core/foundational projects making new methodological developments readily available to even wider audiences without proliferation of the low quality scientific software. ## Similar/related projects [sempervirens](https://github.com/njsmith/sempervirens) -- *an experimental prototype for gathering anonymous, opt-in usage data for open scientific software*. Eventually in duecredit we aim either to provide similar functionality (since we are collecting such information as well) or just interface/report to sempervirens. [citepy](https://github.com/clbarnes/citepy) -- Easily cite software libraries using information from automatically gathered from their package repository. ## Currently used by This is a running list of projects that use DueCredit natively. If you are using DueCredit, or plan to use it, please consider sending a pull request and add your project to this list. Thanks to [@fedorov](https://github.com/fedorov) for the idea. - [PyMVPA](http://www.pymvpa.org) - [fatiando](https://github.com/fatiando/fatiando) - [Nipype](https://github.com/nipy/nipype) - [QInfer](https://github.com/QInfer/python-qinfer) - [shablona](https://github.com/uwescience/shablona) - [gfusion](https://github.com/mvdoc/gfusion) - [pybids](https://github.com/INCF/pybids) - [Quickshear](https://github.com/nipy/quickshear) - [meqc](https://github.com/emdupre/meqc) - [MDAnalysis](https://www.mdanalysis.org) - [bctpy](https://github.com/aestrivex/bctpy) Last updated 2019-07-24. duecredit-0.8.0/appveyor.yml000066400000000000000000000021401362001701600160500ustar00rootroot00000000000000environment: matrix: - PYTHON: C:\Python27 - PYTHON: C:\Python35 - PYTHON: C:\Python36 - PYTHON: C:\Python37 init: - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH% %MINICONDA%" # Not a .NET project, we build in the install step instead build: false install: # Prepend newly installed Python to the PATH of this build (this cannot be # done from inside the powershell script as it would require to restart # the parent CMD process). - SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH% - python -m pip install --upgrade pip virtualenv - virtualenv --python=python venv - venv\Scripts\activate - python --version # Install the dependencies of the project. - pip install --upgrade pip setuptools>=27.0 wheel - pip install -e .[tests] - pip install coverage test_script: - coverage run --source duecredit -m py.test -v - python setup.py install # for interactive debugging upon completion (have 30 min to react) #on_finish: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) duecredit-0.8.0/duecredit/000077500000000000000000000000001362001701600154335ustar00rootroot00000000000000duecredit-0.8.0/duecredit/__init__.py000066400000000000000000000013431362001701600175450ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Module/app to automate collection of relevant to analysis publications. Please see README.md shipped along with duecredit to get a better idea about its functionality """ from .entries import Doi, BibTeX, Url, Text from .version import __version__, __release_date__ from .dueswitch import due __all__ = ['Doi', 'BibTeX', 'Url', 'Text', 'due']duecredit-0.8.0/duecredit/__main__.py000066400000000000000000000055471362001701600175400ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Helper to use duecredit as a "runnable" module with -m duecredit""" import sys from . import due, __version__ from .log import lgr def usage(outfile, executable=sys.argv[0]): if '__main__.py' in executable: # That was -m duecredit way to launch executable = "%s -m duecredit" % sys.executable outfile.write("""Usage: %s [OPTIONS] [ARGS] Meta-options: --help Display this help then exit. --version Output version information then exit. """ % executable) def runctx(cmd, globals=None, locals=None): if globals is None: globals = {} if locals is None: locals = {} try: exec(cmd, globals, locals) finally: # good opportunity to avoid atexit I guess. pass for now pass def main(argv=None): import os import getopt if argv is None: argv = sys.argv try: opts, prog_argv = getopt.getopt(argv[1:], "", ["help", "version"]) # TODO: support options for whatever we would support ;) # probably needs to hook in somehow into commands/options available # under cmdline/ except getopt.error as msg: sys.stderr.write("%s: %s\n" % (sys.argv[0], msg)) sys.stderr.write("Try `%s --help' for more information\n" % sys.argv[0]) sys.exit(1) # and now we need to execute target script "manually" # Borrowing up on from trace.py for opt, val in opts: if opt == "--help": usage(sys.stdout, executable=argv[0]) sys.exit(0) if opt == "--version": sys.stdout.write("duecredit %s\n" % __version__) sys.exit(0) sys.argv = prog_argv progname = prog_argv[0] sys.path[0] = os.path.split(progname)[0] try: with open(progname) as fp: code = compile(fp.read(), progname, 'exec') # try to emulate __main__ namespace as much as possible globs = { '__file__': progname, '__name__': '__main__', '__package__': None, '__cached__': None, } # Since used explicitly -- activate the beast due.activate(True) runctx(code, globs, globs) # TODO: see if we could hide our presence from the final tracebacks if execution fails except IOError as err: lgr.error("Cannot run file %r because: %s" % (sys.argv[0], err)) sys.exit(1) except SystemExit: pass if __name__ == '__main__': main()duecredit-0.8.0/duecredit/cmdline/000077500000000000000000000000001362001701600170465ustar00rootroot00000000000000duecredit-0.8.0/duecredit/cmdline/__init__.py000066400000000000000000000007451362001701600211650ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ """ __docformat__ = 'restructuredtext' from . import cmd_summary from . import cmd_test duecredit-0.8.0/duecredit/cmdline/cmd_summary.py000066400000000000000000000033171362001701600217440ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Spit out the summary of the citations which were collected. """ import sys import os from ..log import lgr from ..config import DUECREDIT_FILE from ..collector import CollectorSummary from ..io import TextOutput, BibTeXOutput __docformat__ = 'restructuredtext' # magic line for manpage summary # man: -*- % summary of collected citations def setup_parser(parser): parser.add_argument( "-f", "--filename", default=DUECREDIT_FILE, help="Filename containing collected citations. Default: %(default)s") parser.add_argument( "--style", choices=("apa", "harvard1"), default="harvard1", help="Style to be used for listing citations") parser.add_argument( "--format", choices=("text", "bibtex"), default="text", help="Way to present the summary") def run(args): from ..io import PickleOutput if not os.path.exists(args.filename): lgr.debug("File {0} doesn't exist. No summary available".format( args.filename)) return 1 due = PickleOutput.load(args.filename) #CollectorSummary(due).dump() if args.format == "text": out = TextOutput(sys.stdout, due, args.style) elif args.format == "bibtex": out = BibTeXOutput(sys.stdout, due) else: raise ValueError("unknown to treat %s" % args.format) out.dump() duecredit-0.8.0/duecredit/cmdline/cmd_test.py000066400000000000000000000014771362001701600212330ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Run internal DueCredit (unit)tests to verify correct operation on the system""" __docformat__ = 'restructuredtext' # magic line for manpage summary # man: -*- % run unit-tests from .helpers import parser_add_common_args def setup_parser(parser): # TODO -- pass options such as verbosity etc pass def run(args): import duecredit raise NotImplementedError("Just use pytest duecredit for now") #duecredit.test() duecredit-0.8.0/duecredit/cmdline/common_args.py000066400000000000000000000024401362001701600217240ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ """ __docformat__ = 'restructuredtext' # argument spec template # = ( # , # {} #) from ..cmdline.helpers import HelpAction, LogLevelAction help = ( 'help', ('-h', '--help', '--help-np'), dict(nargs=0, action=HelpAction, help="""show this help message and exit. --help-np forcefully disables the use of a pager for displaying the help.""") ) version = ( 'version', ('--version',), dict(action='version', help="show program's version and license information and exit") ) log_level = ( 'log-level', ('-l', '--log-level'), dict(action=LogLevelAction, choices=['critical', 'error', 'warning', 'info', 'debug'] + [str(x) for x in range(1, 10)], default='warning', help="""level of verbosity. Integers provide even more debugging information""") ) duecredit-0.8.0/duecredit/cmdline/helpers.py000066400000000000000000000072731362001701600210730ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ """ __docformat__ = 'restructuredtext' import argparse import re import sys from ..utils import is_interactive class HelpAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): # import pydb; pydb.debugger() if is_interactive() and option_string == '--help': # lets use the manpage on mature systems ... try: import subprocess subprocess.check_call( 'man %s 2> /dev/null' % parser.prog.replace(' ', '-'), shell=True) sys.exit(0) except (subprocess.CalledProcessError, OSError): # ...but silently fall back if it doesn't work pass if option_string == '-h': helpstr = "%s\n%s" \ % (parser.format_usage(), "Use '--help' to get more comprehensive information.") else: helpstr = parser.format_help() # better for help2man helpstr = re.sub(r'optional arguments:', 'options:', helpstr) # yoh: TODO for duecredit + help2man #helpstr = re.sub(r'positional arguments:\n.*\n', '', helpstr) # convert all heading to have the first character uppercase headpat = re.compile(r'^([a-z])(.*):$', re.MULTILINE) helpstr = re.subn(headpat, lambda match: r'{0}{1}:'.format(match.group(1).upper(), match.group(2)), helpstr)[0] # usage is on the same line helpstr = re.sub(r'^usage:', 'Usage:', helpstr) if option_string == '--help-np': usagestr = re.split(r'\n\n[A-Z]+', helpstr, maxsplit=1)[0] usage_length = len(usagestr) usagestr = re.subn(r'\s+', ' ', usagestr.replace('\n', ' '))[0] helpstr = '%s\n%s' % (usagestr, helpstr[usage_length:]) print(helpstr) sys.exit(0) class LogLevelAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): from ..log import LoggerHelper LoggerHelper().set_level(level=values) def parser_add_common_args(parser, pos=None, opt=None, **kwargs): from . import common_args for i, args in enumerate((pos, opt)): if args is None: continue for arg in args: arg_tmpl = getattr(common_args, arg) arg_kwargs = arg_tmpl[2].copy() arg_kwargs.update(kwargs) if i: parser.add_argument(*arg_tmpl[i], **arg_kwargs) else: parser.add_argument(arg_tmpl[i], **arg_kwargs) def parser_add_common_opt(parser, opt, names=None, **kwargs): from . import common_args opt_tmpl = getattr(common_args, opt) opt_kwargs = opt_tmpl[2].copy() opt_kwargs.update(kwargs) if names is None: parser.add_argument(*opt_tmpl[1], **opt_kwargs) else: parser.add_argument(*names, **opt_kwargs) class RegexpType(object): """Factory for creating regular expression types for argparse DEPRECATED AFAIK -- now things are in the config file... but we might provide a mode where we operate solely from cmdline """ def __call__(self, string): if string: return re.compile(string) else: return None duecredit-0.8.0/duecredit/cmdline/main.py000066400000000000000000000170261362001701600203520ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. Originates from datalad package distributed # under MIT license # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """""" __docformat__ = 'restructuredtext' import argparse import logging import os import sys import textwrap from .. import __version__ from ..log import lgr import duecredit.cmdline as duecmd from . import helpers from ..utils import setup_exceptionhook def _license_info(): return """\ Copyright 2015-2016 Yaroslav Halchenko, Matteo Visconti di Oleggio Castello. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 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 HOLDER 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. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the copyright holder. """ def get_commands(): return sorted([c for c in dir(duecmd) if c.startswith('cmd_')]) def setup_parser(): # setup cmdline args parser # main parser parser = argparse.ArgumentParser( fromfile_prefix_chars='@', # usage="%(prog)s ...", description="""\ DueCredit simplifies citation of papers describing methods, software and data used by any given analysis script/pipeline. """, epilog='"Your Credit is Due"', formatter_class=argparse.RawDescriptionHelpFormatter, add_help=False ) # common options helpers.parser_add_common_opt(parser, 'help') helpers.parser_add_common_opt(parser, 'log_level') helpers.parser_add_common_opt(parser, 'version', version='duecredit %s\n\n%s' % (__version__, _license_info())) if __debug__: parser.add_argument( '--dbg', action='store_true', dest='common_debug', help="do not catch exceptions and show exception traceback") # yoh: atm we only dump to console. Might adopt the same separation later on # and for consistency will call it --verbose-level as well for now # log-level is set via common_opts ATM # parser.add_argument('--log-level', # choices=('critical', 'error', 'warning', 'info', 'debug'), # dest='common_log_level', # help="""level of verbosity in log files. By default # everything, including debug messages is logged.""") #parser.add_argument('-l', '--verbose-level', # choices=('critical', 'error', 'warning', 'info', 'debug'), # dest='common_verbose_level', # help="""level of verbosity of console output. By default # only warnings and errors are printed.""") # subparsers subparsers = parser.add_subparsers() # for all subcommand modules it can find cmd_short_description = [] for cmd in get_commands(): cmd_name = cmd[4:] subcmdmod = getattr(__import__('duecredit.cmdline', globals(), locals(), [cmd], 0), cmd) # deal with optional parser args if 'parser_args' in subcmdmod.__dict__: parser_args = subcmdmod.parser_args else: parser_args = dict() # use module description, if no explicit description is available if not 'description' in parser_args: parser_args['description'] = subcmdmod.__doc__ # create subparser, use module suffix as cmd name subparser = subparsers.add_parser(cmd_name, add_help=False, **parser_args) # all subparser can report the version helpers.parser_add_common_opt( subparser, 'version', version='duecredit %s %s\n\n%s' % (cmd_name, __version__, _license_info())) # our own custom help for all commands helpers.parser_add_common_opt(subparser, 'help') helpers.parser_add_common_opt(subparser, 'log_level') # let module configure the parser subcmdmod.setup_parser(subparser) # logger for command # configure 'run' function for this command subparser.set_defaults(func=subcmdmod.run, logger=logging.getLogger('duecredit.%s' % cmd)) # store short description for later sdescr = getattr(subcmdmod, 'short_description', parser_args['description'].split('\n')[0]) cmd_short_description.append((cmd_name, sdescr)) # create command summary cmd_summary = [] for cd in cmd_short_description: cmd_summary.append('%s\n%s\n\n' \ % (cd[0], textwrap.fill(cd[1], 75, initial_indent=' ' * 4, subsequent_indent=' ' * 4))) parser.description = '%s\n%s\n\n%s' \ % (parser.description, '\n'.join(cmd_summary), textwrap.fill("""\ Detailed usage information for individual commands is available via command-specific help options, i.e.: %s --help""" % sys.argv[0], 75, initial_indent='', subsequent_indent='')) return parser def main(args=None): parser = setup_parser() # parse cmd args args = parser.parse_args(args) # run the function associated with the selected command if args.common_debug or os.environ.get('DUECREDIT_DEBUG', None): # So we could see/stop clearly at the point of failure setup_exceptionhook() args.func(args) else: # Otherwise - guard and only log the summary. Postmortem is not # as convenient if being caught in this ultimate except try: args.func(args) except Exception as exc: lgr.error('%s (%s)' % (str(exc), exc.__class__.__name__)) sys.exit(1) duecredit-0.8.0/duecredit/collector.py000066400000000000000000000423031362001701600177750ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Citation and citations Collector classes""" import os import sys from functools import wraps from six import iteritems, itervalues from .config import DUECREDIT_FILE from .entries import DueCreditEntry from .stub import InactiveDueCreditCollector from .io import TextOutput, PickleOutput from .utils import never_fail, borrowdoc from .versions import external_versions from collections import namedtuple import logging lgr = logging.getLogger('duecredit.collector') CitationKey = namedtuple('CitationKey', ['path', 'entry_key']) class Citation(object): """Encapsulates citations and information on their use""" def __init__(self, entry, description=None, path=None, version=None, cite_module=False, tags=['implementation']): """Cite a reference Parameters ---------- entry: str or DueCreditEntry The entry to use, either identified by its id or a new one (to be added) description: str, optional Description of what this functionality provides path: str Path to the object which this citation associated with. Format is "module[.submodules][:[class.]method]", i.e. ":" is used to separate module path from the path within the module. version: str or tuple, version Version of the beast (e.g. of the module) where applicable cite_module: bool, optional If it is a module citation, setting it to True would make that module citeable even without internal duecredited functionality invoked. Should be used only for core packages whenever it is reasonable to assume that its import constitute its use (e.g. numpy) tags: list of str, optional Tags to associate with the given code/reference combination. Some tags have associated semantics in duecredit, e.g. (see full list in README.md or https://github.com/duecredit/duecredit/#tags) - "implementation" [default] tag describes as an implementation of the cited method - "reference-implementation" tag describes as the original implementation (ideally by the authors of the paper) of the cited method - "another-implementation" tag describes some other implementation of the method - "use" tag points to publications demonstrating a worthwhile noting use the method - "edu" references to tutorials, textbooks and other materials useful to learn more - "donate" should be commonly used with Url entries to point to the websites describing how to contribute some funds to the referenced project """ if path is None: raise ValueError('Must specify path') self._entry = entry self._description = description # We might want extract all the relevant functionality into a separate class self._path = path self._cite_module = cite_module self.tags = tags or [] self.version = version self.count = 0 def __repr__(self): args = [repr(self._entry)] if self._description: args.append("description={0}".format(repr(self._description))) if self._path: args.append("path={0}".format(repr(self._path))) if self._cite_module: args.append("cite_module={0}".format(repr(self._cite_module))) if args: args = ", ".join(args) else: args = "" return self.__class__.__name__ + '({0})'.format(args) @property def path(self): return self._path @property def cite_module(self): return self._cite_module @path.setter def path(self, path): # TODO: verify value, if we are not up for it -- just make _path public self._path = path @property def entry(self): return self._entry @property def description(self): return self._description @property def cites_module(self): if not self.path: return None return not (':' in self.path) @property def module(self): if not self.path: return None return self.path.split(':', 1)[0] @property def package(self): module = self.module if not module: return None return module.split('.', 1)[0] @property def objname(self): if not self.path: return None spl = self.path.split(':', 1) if len(spl) > 1: return spl[1] else: return None def __contains__(self, entry): """Checks if provided entry 'contained' in this one given its path If current entry is associated with a module, contained will be an entry of - the same module - submodule of the current module or function within If current entry is associated with a specific function/class, it can contain another entry if it really contains it as an attribute """ if self.cites_module: return ((self.path == entry.path) or (entry.path.startswith(self.path + '.')) or (entry.path.startswith(self.path + ':'))) else: return entry.path.startswith(self.path + '.') @property def key(self): return CitationKey(self.path, self.entry.key) @staticmethod def get_key(path, entry_key): return CitationKey(path, entry_key) def set_entry(self, newentry): self._entry = newentry class DueCreditCollector(object): """Collect the references The mighty beast which will might become later a proxy on the way to talk to a real collector Parameters ---------- entries : list of DueCreditEntry, optional List of reference items (BibTeX, Doi, etc) known to the collector citations : list of Citation, optional List of citations -- associations between references and particular code, with a description for its use, tags etc """ # TODO? rename "entries" to "references"? or "references" is closer to "citations" def __init__(self, entries=None, citations=None): self._entries = entries or {} self.citations = citations or {} @never_fail def add(self, entry): """entry should be a DueCreditEntry object""" if isinstance(entry, list): for e in entry: self.add(e) else: key = entry.get_key() self._entries[key] = entry lgr.log(1, "Collector added entry %s", key) @never_fail def load(self, src): """Loads references from a file or other recognizable source ATM supported only - .bib files """ # raise NotImplementedError if isinstance(src, str): if src.endswith('.bib'): self._load_bib(src) else: raise NotImplementedError('Format not yet supported') else: raise ValueError('Must be a string') def _load_bib(self, src): lgr.debug("Loading %s" % src) # # TODO: figure out what would be the optimal use for the __call__ # def __call__(self, *args, **kwargs): # # TODO: how to determine and cite originating module??? # # - we could use inspect but many people complain # # that it might not work with other Python # # implementations # pass # raise NotImplementedError @never_fail @borrowdoc(Citation, "__init__") def cite(self, entry, **kwargs): # TODO: if cite is invoked but no path is provided -- we must figure it out # I guess from traceback, otherwise how would we know later to associate it # with modules??? path = kwargs.get('path', None) if path is None: raise ValueError('path must be provided') if isinstance(entry, DueCreditEntry): # new one -- add it self.add(entry) entry_ = self._entries[entry.get_key()] else: entry_ = self._entries[entry] entry_key = entry_.get_key() citation_key = Citation.get_key(path=path, entry_key=entry_key) try: citation = self.citations[citation_key] except KeyError: self.citations[citation_key] = citation = Citation(entry_, **kwargs) assert(citation.key == citation_key) # update citation count citation.count += 1 # TODO: theoretically version shouldn't differ if we don't preload previous results if not citation.version: version = kwargs.get('version', None) if not version and citation.path: modname = citation.path.split('.', 1)[0] if '.' in modname: package = modname.split('.', 1)[0] else: package = modname # package_loaded = sys.modules.get(package) # if package_loaded: # # find the citation for that module # for citation in itervalues(self.citations): # if citation.package == package \ # and not citation.version: version = external_versions[package] citation.version = version return citation def _citations_fromentrykey(self): """Return a dictionary with the current citations indexed by the entry key""" citations_key = dict() for (path, entry_key), citation in iteritems(self.citations): if entry_key not in citations_key: citations_key[entry_key] = citation return citations_key @staticmethod def _args_match_conditions(conditions, *fargs, **fkwargs): """Helper to identify when to trigger citation given parameters to the function call """ for (argpos, kwarg), values in iteritems(conditions): # main logic -- assess default and if get to the next one if # given argument is not present if not ((len(fargs) > argpos) or (kwarg in fkwargs)): if not ('DC_DEFAULT' in values): # if value was specified but not provided and not default # conditions are not satisfied return False continue # "extract" the value. Must be defined here value = "__duecredit_magical_undefined__" if len(fargs) > argpos: value = fargs[argpos] if kwarg in fkwargs: value = fkwargs[kwarg] assert(value != "__duecredit_magical_undefined__") if '.' in kwarg: # we were requested to condition based on the value of the attribute # of the value. So get to the attribute(s) value for attr in kwarg.split('.')[1:]: value = getattr(value, attr) # Value is present but not matching if not (value in values): return False # if checks passed -- we must have matched conditions return True @never_fail @borrowdoc(Citation, "__init__", replace="PLUGDOCSTRING") def dcite(self, *args, **kwargs): """Decorator for references. PLUGDOCSTRING Parameters ---------- conditions: dict, optional If reference should be cited whenever parameters to the function call satisfy given values (all of the specified). Each key in the dictionary is a 2 element tuple with first element, integer, pointing to a position of the argument in the original function call signature, while second provides the name, thus if used as a keyword argument. Use "DC_DEFAULT" keyword as a value to depict default value (e.g. if no explicit value was provided for that positional or keyword argument). If "keyword argument" is of the form "obj.attr1.attr2", then actual value for comparison would be taken by extracting attr1 (and then attr2) attributes from the provided value. So, if desired to condition of the state of the object, you can use `(0, "self.attr1") : {...values...}` Examples -------- >>> from duecredit import due >>> @due.dcite('XXX00', description="Provides an answer for meaningless existence") ... def purpose_of_life(): ... return None Conditional citation given argument to the function >>> @due.dcite('XXX00', description="Relief through the movement", ... conditions={(1, 'method'): {'purge', 'DC_DEFAULT'}}) ... @due.dcite('XXX01', description="Relief through the drug treatment", ... conditions={(1, 'method'): {'drug'}}) ... def relief(x, method='purge'): ... if method == 'purge': return "crap" ... elif method == 'drug': return "swallow" >>> relief("doesn't matter") 'crap' Conditional based on the state of the object >>> class Citeable(object): ... def __init__(self, param=None): ... self.param = param ... @due.dcite('XXX00', description="The same good old relief", ... conditions={(0, 'self.param'): {'magic'}}) ... def __call__(self, data): ... return sum(data) >>> Citeable('magic')([1, 2]) 3 """ def func_wrapper(func): conditions = kwargs.pop('conditions', {}) path = kwargs.get('path', None) if not path: # deduce path from the actual function which was decorated # TODO: must include class name but can't !!!??? modname = func.__module__ path = kwargs['path'] = '%s:%s' % (modname, func.__name__) else: # TODO: we indeed need to separate path logic outside modname = path.split(':', 1)[0] # if decorated function was invoked, it means that we need # to cite that even if it is a module. But do not override # value if user explicitly stated if 'cite_module' not in kwargs: kwargs['cite_module'] = True # TODO: might make use of inspect.getmro # see e.g. # http://stackoverflow.com/questions/961048/get-class-that-defined-method lgr.debug("Decorating func %s within module %s" % (func.__name__, modname)) # TODO: unittest for all the __version__ madness # TODO: check if we better use wrapt module which provides superior "correctness" # of decorating. vcrpy uses wrapt, and that thing seems to wrap @wraps(func) def cite_wrapper(*fargs, **fkwargs): try: if not conditions \ or self._args_match_conditions(conditions, *fargs, **fkwargs): citation = self.cite(*args, **kwargs) except Exception as e: lgr.warning("Failed to cite due to %s" % (e,)) return func(*fargs, **fkwargs) cite_wrapper.__duecredited__ = func return cite_wrapper return func_wrapper @never_fail def __repr__(self): args = [] if self.citations: args.append("citations={0}".format(repr(self.citations))) if self._entries: args.append("entries={0}".format(repr(self._entries))) if args: args = ", ".join(args) else: args = "" return self.__class__.__name__ + '({0})'.format(args) @never_fail def __str__(self): return self.__class__.__name__ + \ ' {0:d} entries, {1:d} citations'.format( len(self._entries), len(self.citations)) # TODO: redo heavily -- got very messy class CollectorSummary(object): """A helper which would take care about exporting citations upon its Death """ def __init__(self, collector, outputs="stdout,pickle", fn=DUECREDIT_FILE): self._due = collector self.fn = fn # for now decide on output "format" right here self._outputs = [ self._get_output_handler( type_.lower().strip(), collector, fn=fn) for type_ in os.environ.get('DUECREDIT_OUTPUTS', outputs).split(',') if type_ ] @staticmethod def _get_output_handler(type_, collector, fn=None): # just a little factory if type_ in ("stdout", "stderr"): return TextOutput(getattr(sys, type_), collector) elif type_ == "pickle": return PickleOutput(collector, fn=fn) else: raise NotImplementedError() def dump(self): for output in self._outputs: output.dump() # TODO: provide HTML, MD, RST etc formattings duecredit-0.8.0/duecredit/config.py000066400000000000000000000011251362001701600172510ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Configuration handling for duecredit""" import os # For now just hardcoded variables CACHE_DIR = os.path.expanduser(os.path.join('~', '.cache', 'duecredit', 'bibtex')) DUECREDIT_FILE = '.duecredit.p' duecredit-0.8.0/duecredit/dueswitch.py000066400000000000000000000111411362001701600200020ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Provides an adapter to switch between two (active, inactive) collectors """ import os import atexit from .log import lgr from .utils import never_fail def _get_duecredit_enable(): env_enable = os.environ.get('DUECREDIT_ENABLE', 'no') if not env_enable.lower() in ('0', '1', 'yes', 'no', 'true', 'false'): lgr.warning("Misunderstood value %s for DUECREDIT_ENABLE. " "Use 'yes' or 'no', or '0' or '1'") return env_enable.lower() in ('1', 'yes', 'true') @never_fail def _get_inactive_due(): from .stub import InactiveDueCreditCollector return InactiveDueCreditCollector() @never_fail def _get_active_due(): from .config import CACHE_DIR, DUECREDIT_FILE from duecredit.collector import CollectorSummary, DueCreditCollector from .io import load_due # TODO: this needs to move to atexit handling, that we load previous # one and them merge with new ones. Informative bits could be -- how # many new citations we got if os.path.exists(DUECREDIT_FILE): try: due_ = load_due(DUECREDIT_FILE) except Exception as e: lgr.warning("Failed to load previously collected %s. " "DueCredit will not be active for this session." % DUECREDIT_FILE) return _get_inactive_due() else: due_ = DueCreditCollector() return due_ class DueSwitch(object): """Adapter between two types of collectors -- Inactive and Active Once activated though, cannot be fully deactivated since it would inject duecredit decorators and register an event atexit. """ def __init__(self, inactive, active, activate=False): self.__active = None self.__collectors = {False: inactive, True: active} self.__activations_done = False if not (inactive and active): raise ValueError( "Both inactive and active collectors should be provided. " "Got active=%r, inactive=%r" % (active, inactive) ) self.activate(activate) @property def active(self): return self.__active @never_fail def dump(self, **kwargs): """Dumps summary of the citations Parameters ---------- **kwargs: dict Passed to `CollectorSummary` constructor. """ from duecredit.collector import CollectorSummary due_summary = CollectorSummary(self.__collectors[True], **kwargs) due_summary.dump() def __prepare_exit_and_injections(self): # Wrapper to create and dump summary... passing method doesn't work: # probably removes instance too early atexit.register(self.dump) # Deal with injector from .injections import DueCreditInjector injector = DueCreditInjector(collector=self.__collectors[True]) injector.activate() @never_fail def activate(self, activate=True): # 1st step -- if activating/deactivating switch between the two collectors if self.__active is not activate: # we need to switch the state # InactiveDueCollector also has those special methods defined # in DueSwitch so that client code could query/call (for no effect). # So we shouldn't delete or bind them either is_public_or_special = \ lambda x: not (x.startswith('_') or x in ('activate', 'active', 'dump')) # Clean up current bindings first for k in filter(is_public_or_special, dir(self)): delattr(self, k) new_due = self.__collectors[activate] for k in filter(is_public_or_special, dir(new_due)): setattr(self, k, getattr(new_due, k)) self.__active = activate # 2nd -- if activating, we might still need to have activations done if activate and not self.__activations_done: try: self.__prepare_exit_and_injections() except Exception as e: lgr.error("Failed to prepare injections etc: %s" % str(e)) finally: self.__activations_done = True due = DueSwitch(_get_inactive_due(), _get_active_due(), _get_duecredit_enable())duecredit-0.8.0/duecredit/entries.py000066400000000000000000000047211362001701600174620ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. Originates from datalad package distributed # under MIT license # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import re from six import PY2 import logging lgr = logging.getLogger('duecredit.entries') class DueCreditEntry(object): def __init__(self, rawentry, key=None): self._rawentry = rawentry self._key = key or rawentry.lower() def __eq__(self, other): return ( (self._rawentry == other._rawentry) and (self._key == other._key) ) def get_key(self): return self._key @property def key(self): return self.get_key() @property def rawentry(self): if PY2: return unicode(self._rawentry) else: return self._rawentry def _process_rawentry(self): pass def __repr__(self): args = [repr(self._rawentry), "key={0}".format(repr(self._key))] args = ", ".join(args) return self.__class__.__name__ + '({0})'.format(args) def format(self): # TODO: return nice formatting of the entry return str(self._rawentry) class BibTeX(DueCreditEntry): def __init__(self, bibtex, key=None): super(BibTeX, self).__init__(bibtex.strip()) self._key = None self._reference = None self._process_rawentry() if key is not None: # use the one provided, not the parsed one lgr.debug("Replacing parsed key %s for BibTeX with the provided %s", self._key, key) self._key = key def _process_rawentry(self): reg = re.match("\s*@(?P\S*)\s*\{\s*(?P\S*)\s*,.*", self._rawentry, flags=re.MULTILINE) assert(reg) matches = reg.groupdict() self._key = matches['key'] class Text(DueCreditEntry): """Just a free text entry without any special super powers in rendering etc """ pass # nothing special I guess class Doi(DueCreditEntry): @property def doi(self): return self._rawentry class Url(DueCreditEntry): @property def url(self): return self._rawentry duecredit-0.8.0/duecredit/injections/000077500000000000000000000000001362001701600176005ustar00rootroot00000000000000duecredit-0.8.0/duecredit/injections/__init__.py000066400000000000000000000010351362001701600217100ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Facility to automagically decorate with references other modules """ __docformat__ = 'restructuredtext' from .injector import DueCreditInjector duecredit-0.8.0/duecredit/injections/injector.py000066400000000000000000000411341362001701600217720ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Importer which would also call decoration on a module upon import """ __docformat__ = 'restructuredtext' import os from os.path import basename, join as pathjoin, dirname from glob import glob import sys from functools import wraps import logging from ..log import lgr from six import iteritems if sys.version_info < (3,): import __builtin__ else: import builtins as __builtin__ __all__ = ['DueCreditInjector', 'find_object'] # TODO: move elsewhere def _short_str(obj, l=30): """Return a shortened str of an object -- for logging""" s = str(obj) if len(s) > l: return s[:l-3] + "..." else: return s def get_modules_for_injection(): """Get local modules which provide "inject" method to provide delayed population of injector """ return sorted([basename(x)[:-3] for x in glob(pathjoin(dirname(__file__), "mod_*.py")) ]) def find_object(mod, path): """Finds object among present within module "mod" given path specification within Returns ------- parent, obj_name, obj """ obj = mod # we will look first within module for obj_name in path.split('.'): parent = obj obj = getattr(parent, obj_name) return parent, obj_name, obj # We will keep a very original __import__ to mitigate cases of buggy python # behavior, see e.g. # https://github.com/duecredit/duecredit/issues/40 # But we will also keep the __import__ as of 'activate' call state so we could # stay friendly to anyone else who might decorate __import__ as well _very_orig_import = __builtin__.__import__ class DueCreditInjector(object): """Takes care about "injecting" duecredit references into 3rd party modules upon their import First entries to be "injected" need to be add'ed to the instance. To not incur significant duecredit startup penalty, those entries are added for a corresponding package only whenever corresponding top-level module gets imported. """ # Made as a class variable to assure being singleton and available upon del __orig_import = None # and interact with its value through the property to ease tracking of actions # performed on it @property def _orig_import(self): return DueCreditInjector.__orig_import @_orig_import.setter def _orig_import(self, value): lgr.log(2, "Reassigning _orig_import from %r to %r", DueCreditInjector.__orig_import, value) DueCreditInjector.__orig_import = value def __init__(self, collector=None): if collector is None: from duecredit import due collector = due self._collector = collector self._delayed_injections = {} self._entry_records = {} # dict: modulename: {object: [('entry', cite kwargs)]} self._processed_modules = set() # We need to process modules only after we are done with all nested imports, otherwise we # might be trying to process them too early -- whenever they are not yet linked to their # parent's namespace. So we will keep track of import level and set of modules which # would need to be processed whenever we are back at __import_level == 1 self.__import_level = 0 self.__queue_to_process = set() self.__processing_queue = False self._active = False lgr.debug("Created injector %r", self) def _populate_delayed_injections(self): self._delayed_injections = {} for inj_mod_name in get_modules_for_injection(): assert(inj_mod_name.startswith('mod_')) mod_name = inj_mod_name[4:] lgr.debug("Adding delayed injection for %s", (mod_name,)) self._delayed_injections[mod_name] = inj_mod_name def add(self, modulename, obj, entry, min_version=None, max_version=None, **kwargs): """Add a citation for a given module or object within it Parameters ---------- modulename : string Name of the module (possibly a sub-module) obj : string or None Name of the object (function, method within a class) or None (if for entire module) min_version, max_version : string or tuple, optional Min (inclusive) / Max (exclusive) version of the module where this citation is applicable **kwargs Keyword arguments to be passed into cite. Note that "path" will be automatically set if not provided """ lgr.debug("Adding citation entry %s for %s:%s", _short_str(entry), modulename, obj) if modulename not in self._entry_records: self._entry_records[modulename] = {} if obj not in self._entry_records[modulename]: self._entry_records[modulename][obj] = [] obj_entries = self._entry_records[modulename][obj] if 'path' not in kwargs: kwargs['path'] = modulename + ((":%s" % obj) if obj else "") obj_entries.append({'entry': entry, 'kwargs': kwargs, 'min_version': min_version, 'max_version': max_version}) @property def _import_level_prefix(self): return "." * self.__import_level def _process_delayed_injection(self, mod_name): lgr.debug("%sProcessing delayed injection for %s", self._import_level_prefix, mod_name) inj_mod_name = self._delayed_injections[mod_name] assert(not hasattr(self._orig_import, '__duecredited__')) try: inj_mod_name_full = "duecredit.injections." + inj_mod_name lgr.log(3, "Importing %s", inj_mod_name_full) # Mark it is a processed already, to avoid its processing etc self._processed_modules.add(inj_mod_name_full) inj_mod = self._orig_import(inj_mod_name_full, fromlist=["duecredit.injections"]) except Exception as e: if os.environ.get('DUECREDIT_ALLOW_FAIL', False): raise raise RuntimeError("Failed to import %s: %r" % (inj_mod_name, e)) # TODO: process min/max_versions etc assert(hasattr(inj_mod, 'inject')) lgr.log(3, "Calling injector of %s", inj_mod_name_full) inj_mod.inject(self) def process(self, mod_name): """Process import of the module, possibly decorating some methods with duecredit entries """ assert(self.__import_level == 0) # we should never process while nested within imports # We need to mark that module as processed EARLY, so we don't try to re-process it # while doing _process_delayed_injection self._processed_modules.add(mod_name) if mod_name in self._delayed_injections: # should be hit only once, "theoretically" unless I guess reimport is used etc self._process_delayed_injection(mod_name) if mod_name not in self._entry_records: return total_number_of_citations = sum(map(len, self._entry_records[mod_name].values())) lgr.log(logging.DEBUG + 5, "Process %d citation injections for %d objects for module %s", total_number_of_citations, len(self._entry_records[mod_name]), mod_name) try: mod = sys.modules[mod_name] except KeyError: lgr.warning("Failed to access module %s among sys.modules" % mod_name) return # go through the known entries and register them within the collector, and # decorate corresponding methods # There could be multiple records per module for obj_path, obj_entry_records in iteritems(self._entry_records[mod_name]): parent, obj_name = None, None if obj_path: # so we point to an object within the mod try: parent, obj_name, obj = find_object(mod, obj_path) except (KeyError, AttributeError) as e: lgr.warning("Could not find %s in module %s: %s" % (obj_path, mod, e)) continue # there could be multiple per func lgr.log(4, "Considering %d records for decoration of %s:%s", len(obj_entry_records), parent, obj_name) for obj_entry_record in obj_entry_records: entry = obj_entry_record['entry'] # Add entry explicitly self._collector.add(entry) if obj_path: # if not entire module -- decorate! decorator = self._collector.dcite(entry.get_key(), **obj_entry_record['kwargs']) lgr.debug("Decorating %s:%s with %s", parent, obj_name, decorator) obj_decorated = decorator(obj) setattr(parent, obj_name, obj_decorated) # override previous obj with the decorated one if there are multiple decorators obj = obj_decorated else: lgr.log(3, "Citing directly %s:%s since obj_path is empty", parent, obj_name) self._collector.cite(entry.get_key(), **obj_entry_record['kwargs']) lgr.log(3, "Done processing injections for module %s", mod_name) def _mitigate_None_orig_import(self, name, *args, **kwargs): lgr.error("For some reason self._orig_import is None" ". Importing using stock importer to mitigate and adjusting _orig_import") self._orig_import = _very_orig_import return _very_orig_import(name, *args, **kwargs) def activate(self, retrospect=True): """ Parameters ---------- retrospect : bool, optional Either consider already loaded modules """ if not self._orig_import: # for paranoid Yarik so we have assurance we are not somehow # overriding our decorator if hasattr(__builtin__.__import__, '__duecredited__'): raise RuntimeError("__import__ is already duecredited") self._orig_import = __builtin__.__import__ @wraps(__builtin__.__import__) def __import(name, *args, **kwargs): if self.__processing_queue or name in self._processed_modules or name in self.__queue_to_process: lgr.debug("Performing undecorated import of %s", name) # return right away without any decoration in such a case if self._orig_import: return _very_orig_import(name, *args, **kwargs) else: return self._mitigate_None_orig_import(name, *args, **kwargs) import_level_prefix = self._import_level_prefix lgr.log(1, "%sProcessing request to import %s", import_level_prefix, name) # importing submodule might result in importing a new one and # name here is not sufficient to determine which module would actually # get imported unless level=0 (absolute import), but that one rarely used # could be old-style or new style relative import! # args[0] -> globals, [1] -> locals(), [2] -> fromlist, [3] -> level level = args[3] if len(args) >= 4 else kwargs.get('level', -1) # fromlist = args[2] if len(args) >= 3 else kwargs.get('fromlist', []) if not retrospect and not self._processed_modules: # we were asked to not consider those modules which were already loaded # so let's assume that they were all processed already self._processed_modules = set(sys.modules) mod = None try: self.__import_level += 1 # TODO: safe-guard all our logic so # if anything goes wrong post-import -- we still return imported module if self._orig_import: mod = self._orig_import(name, *args, **kwargs) else: mod = self._mitigate_None_orig_import(name, *args, **kwargs) self._handle_fresh_imports(name, import_level_prefix, level) finally: self.__import_level -= 1 if self.__import_level == 0 and self.__queue_to_process: self._process_queue() lgr.log(1, "%sReturning %s", import_level_prefix, mod) return mod __import.__duecredited__ = True self._populate_delayed_injections() if retrospect: lgr.debug("Considering previously loaded %d modules", len(sys.modules)) # operate on keys() (not iterator) since we might end up importing delayed injection modules etc for mod_name in sys.modules.keys(): self.process(sys.modules[mod_name]) lgr.debug("Assigning our importer") __builtin__.__import__ = __import self._active = True else: lgr.warning("Seems that we are calling duecredit_importer twice." " No harm is done but shouldn't happen") def _handle_fresh_imports(self, name, import_level_prefix, level): """Check which modules were imported since last point we checked and add them to the queue """ new_imported_modules = set(sys.modules.keys()) - self._processed_modules - self.__queue_to_process if new_imported_modules: lgr.log(4, "%s%d new modules were detected upon import of %s (level=%s)", import_level_prefix, len(new_imported_modules), name, level) # lgr.log(2, "%s%d new modules were detected: %s, upon import of %s (level=%s)", # import_level_prefix, len(new_imported_modules), new_imported_modules, name, level) for imported_mod in new_imported_modules: if imported_mod in self.__queue_to_process: # we saw it already continue # lgr.log(1, "Name %r was imported as %r (path: %s). fromlist: %s, level: %s", # name, mod.__name__, getattr(mod, '__path__', None), fromlist, level) # package package = imported_mod.split('.', 1)[0] if package != imported_mod \ and package not in self._processed_modules \ and package not in self.__queue_to_process: # if its parent package wasn't yet imported before lgr.log(3, "%sParent of %s, %s wasn't yet processed, adding to the queue", import_level_prefix, imported_mod, package) self.__queue_to_process.add(package) self.__queue_to_process.add(imported_mod) def _process_queue(self): """Process the queue of collected imported modules """ # process the queue lgr.debug("Processing queue of imported %d modules", len(self.__queue_to_process)) # We need first to process top-level modules etc, so delayed injections get picked up, # let's sort by the level queue_with_levels = sorted([(m.count('.'), m) for m in self.__queue_to_process]) self.__processing_queue = True try: sorted_queue = [x[1] for x in queue_with_levels] while sorted_queue: mod_name = sorted_queue.pop(0) self.process(mod_name) self.__queue_to_process.remove(mod_name) assert (not len(self.__queue_to_process)) finally: self.__processing_queue = False def deactivate(self): if not self._orig_import: lgr.warning("_orig_import is not yet known, so we haven't decorated default importer yet." " Nothing TODO") return if not self._active: # pragma: no cover lgr.error("Must have not happened, but we will survive!") lgr.debug("Assigning original importer") __builtin__.__import__ = self._orig_import self._orig_import = None self._active = False def __del__(self): if lgr: lgr.debug("%s is asked to be deleted", self) try: if self._active: self.deactivate() except: # noqa: E722 pass try: super(self.__class__, self).__del__() except: # noqa: E722 pass duecredit-0.8.0/duecredit/injections/mod_biosig.py000066400000000000000000000014201362001701600222620ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Automatic injection of bibliography entries for biosig module """ from ..entries import Doi # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('biosig', None, Doi("10.1109/MC.2008.407"), description="I/O library for biosignal data formats") duecredit-0.8.0/duecredit/injections/mod_dipy.py000066400000000000000000000016331362001701600217610ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Automatic injection of bibliography entries for dipy module """ from ..entries import Doi, BibTeX # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): #http://nipy.org/dipy/cite.html#a-note-on-citing-our-work injector.add('dipy', None, Doi('10.3389/fninf.2014.00008'), description='Dipy, a library for the analysis of diffusion MRI data.', tags=['implementation']) duecredit-0.8.0/duecredit/injections/mod_matplotlib.py000066400000000000000000000030121362001701600231540ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Automatic injection of bibliography entries for matplotlib module """ from ..entries import Doi, BibTeX # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None bib_str = """ @Article{Hunter:2007, Author = {Hunter, J. D.}, Title = {Matplotlib: A 2D graphics environment}, Journal = {Computing in Science \\& Engineering}, Volume = {9}, Number = {3}, Pages = {90--95}, abstract = {Matplotlib is a 2D graphics package used for Python for application development, interactive scripting, and publication-quality image generation across user interfaces and operating systems.}, publisher = {IEEE COMPUTER SOC}, doi = {10.1109/MCSE.2007.55}, year = 2007 } """.strip() def inject(injector): injector.add("matplotlib", None, BibTeX(bib_str), description="Plotting with Python", tags=["implementation"]) doi_prefix = "10.5281/zenodo." # latest version injector.add( "matplotlib", None, Doi(doi_prefix + "2893252"), description="Plotting with Python", tags=["implementation"] ) duecredit-0.8.0/duecredit/injections/mod_mdp.py000066400000000000000000000107451362001701600216000ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Automatic injection of bibliography entries for mdp module """ from ..entries import Doi, BibTeX, Url # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('mdp', None, Doi('10.3389/neuro.11.008.2008'), description="Modular toolkit for Data Processing (MDP): a Python data processing framework", tags=['implementation']) injector.add('mdp.nodes', 'PCANode.train', Doi('10.1007/b98835'), description="Principal Component Analysis (and filtering)", tags=['implementation']) injector.add('mdp.nodes', 'NIPALSNode.train', BibTeX(""" @incollection{Word1966, author={Wold, H.}, title={Nonlinear estimation by iterative least squares procedures.}, booktitle={Research Papers in Statistics}, publisher={Wiley} year={1966}, editor={David, F.}, pages={411--444}, } """), description="Principal Component Analysis using the NIPALS algorithm.", tags=['edu']) injector.add('mdp.nodes', 'FastICANode.train', Doi('10.1109/72.761722'), description="Independent Component Analysis using the FastICA algorithm", tags=['implementation']) injector.add('mdp.nodes', 'CuBICANode.train', Doi('10.1109/TSP.2004.826173'), description='Independent Component Analysis using the CuBICA algorithm.', tags=['implementation']) injector.add('mdp.nodes', 'NIPALSNode.train', BibTeX(""" @conference{ZieheMuller1998, author={Ziehe, Andreas and Muller, Klaus-Robert}, title={TDSEP an efficient algorithm for blind separation using time structure.}, booktitle={Proc. 8th Int. Conf. Artificial Neural Networks}, year={1998}, editor={Niklasson, L, Boden, M, and Ziemke, T}, publisher={ICANN} } """), description='Independent Component Analysis using the TDSEP algorithm', tags=['edu']) injector.add('mdp.nodes', 'JADENode.train', Doi('10.1049/ip-f-2.1993.0054'), description='Independent Component Analysis using the JADE algorithm', tags=['implementation']) injector.add('mdp.nodes', 'JADENode.train', Doi('10.1162/089976699300016863'), description='Independent Component Analysis using the JADE algorithm', tags=['implementation']) injector.add('mdp.nodes', 'SFANode.train', Doi('10.1162/089976602317318938'), description='Slow Feature Analysis', tags=['implementation']) injector.add('mdp.nodes', 'SFA2Node.train', Doi('10.1162/089976602317318938'), description='Slow Feature Analysis (via the space of inhomogeneous polynomials)', tags=['implementation']) injector.add('mdp.nodes', 'ISFANode.train', Doi('10.1007/978-3-540-30110-3_94'), description='Independent Slow Feature Analysis', tags=['implementation']) injector.add('mdp.nodes', 'XSFANode.train', BibTeX(""" @article{SprekelerZitoWiskott2009, author={Sprekeler, H., Zito, T., and Wiskott, L.}, title={An Extension of Slow Feature Analysis for Nonlinear Blind Source Separation.}, journal={Journal of Machine Learning Research.}, year={2009}, volume={15}, pages={921--947}, } """), description="Non-linear Blind Source Separation using Slow Feature Analysis", tags=['edu']) injector.add('mdp.nodes', 'FDANode.train', BibTeX(""" @book{Bishop2011, author={Bishop, Christopher M.}, title={Neural Networks for Pattern Recognition}, publisher={Oxford University Press, Inc} year={2011}, pages={105--112}, } """), description="(generalized) Fisher Discriminant Analysis", tags=['edu']) # etc... duecredit-0.8.0/duecredit/injections/mod_mne.py000066400000000000000000000021001362001701600215610ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Automatic injection of bibliography entries for mne module """ from ..entries import Doi, BibTeX # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): #http://martinos.org/mne/stable/cite.html injector.add('mne', None, Doi('10.1016/j.neuroimage.2013.10.027'), description='MNE software for processing MEG and EEG data.', tags=['implementation']) injector.add('mne', None, Doi('10.3389/fnins.2013.00267'), description='MEG and EEG data analysis with MNE-Python.', tags=['implementation']) duecredit-0.8.0/duecredit/injections/mod_nibabel.py000066400000000000000000000016051362001701600224070ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Automatic injection of bibliography entries for nibabel module """ from ..entries import Doi # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('nibabel', None, Doi('10.5281/zenodo.60847'), cite_module=True, description="I/O library to access to common neuroimaging file formats", tags=['implementation']) duecredit-0.8.0/duecredit/injections/mod_nipy.py000066400000000000000000000022121362001701600217650ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Automatic injection of bibliography entries for nipy module """ from ..entries import Doi # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('nipy', None, Doi('10.1016/S1053-8119(09)72223-2'), description="Library fMRI data analysis", tags=['implementation']) for f, d in [('spectral_decomposition', 'PCA decomposition of symbolic HRF shifted over time'), ('taylor_approx', 'A Taylor series approximation of an HRF shifted over time')]: injector.add('nipy.modalities.fmri.fmristat.hrf', f, Doi('10.1006/nimg.2002.1096'), description=d, tags=['implementation'])duecredit-0.8.0/duecredit/injections/mod_nipype.py000066400000000000000000000051011362001701600223120ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Automatic injection of bibliography entries for nipype module """ from ..entries import Doi, BibTeX # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): #http://nipy.org/nipype/about.html injector.add('nipype', None, Doi('10.3389/fninf.2011.00013'), description='Nipype: a flexible, lightweight and extensible neuroimaging data processing framework in Python', tags=['implementation']) #http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/ injector.add('nipype.interfaces', 'fsl', Doi('10.1016/j.neuroimage.2004.07.051'), description='Advances in functional and structural MR image analysis and implementation as FSL', tags=['implementation']) injector.add('nipype.interfaces', 'fsl', Doi('10.1016/j.neuroimage.2008.10.055'), description='Bayesian analysis of neuroimaging data in FSL', tags=['implementation']) injector.add('nipype.interfaces', 'fsl', Doi('10.1016/j.neuroimage.2011.09.015'), description='FSL.', tags=['implementation']) #http://www.fil.ion.ucl.ac.uk/spm injector.add('nipype.interfaces', 'spm', BibTeX(""" @book{FrackowiakFristonFrithDolanMazziotta1997, author={R.S.J. Frackowiak, K.J. Friston, C.D. Frith, R.J. Dolan, and J.C. Mazziotta}, title={Human Brain Function}, publisher={Academic Press USA} year={1997}, } """), description='The fundamental text on Statistical Parametric Mapping (SPM)', tags=['implementation']) #http://surfer.nmr.mgh.harvard.edu/fswiki/FreeSurferMethodsCitation # there are a bunch, not sure what is primary #injector.add('nipype.interfaces', 'freesurfer', Doi(''), #description='', #tags=['implementation']) #http://afni.nimh.nih.gov/afni/about/citations/ # there are a bunch, not sure what is primary #injector.add('nipype.interfaces', 'afni', Doi(''), #description='', #tags=['implementation']) duecredit-0.8.0/duecredit/injections/mod_numpy.py000066400000000000000000000023021362001701600221560ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Automatic injection of bibliography entries for numpy module """ from ..entries import Doi, BibTeX # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('numpy', None, BibTeX(""" @article{van2011numpy, title={The NumPy array: a structure for efficient numerical computation}, author={Van Der Walt, Stefan and Colbert, S Chris and Varoquaux, Gael}, journal={Computing in Science \& Engineering}, volume={13}, number={2}, pages={22--30}, year={2011}, publisher={AIP Publishing}, doi={10.1109/MCSE.2011.37} } """), tags=['implementation'], cite_module=True, description="Scientific tools library") duecredit-0.8.0/duecredit/injections/mod_pandas.py000066400000000000000000000022201362001701600222530ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Automatic injection of bibliography entries for pandas module """ from ..entries import Doi, BibTeX, Url # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('pandas', None, BibTeX(""" @InProceedings{ mckinney-proc-scipy-2010, author = { McKinney, Wes }, title = { Data Structures for Statistical Computing in Python }, booktitle = { Proceedings of the 9th Python in Science Conference }, pages = { 51 -- 56 }, year = { 2010 }, editor = { van der Walt, St\'efan and Millman, Jarrod } } """), description="Data analysis library for tabular data")duecredit-0.8.0/duecredit/injections/mod_psychopy.py000066400000000000000000000020531362001701600226670ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Automatic injection of bibliography entries for psychopy module """ from ..entries import Doi, BibTeX, Url # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('psychopy', None, Doi('doi:10.1016/j.jneumeth.2006.11.017'), description="PsychoPy -- Psychophysics software in Python.", tags=['implementation']) injector.add('psychopy', None, Doi('10.3389/neuro.11.010.2008'), description="Generating stimuli for neuroscience using PsychoPy.", tags=['implementation']) duecredit-0.8.0/duecredit/injections/mod_scipy.py000066400000000000000000000144361362001701600221500ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Automatic injection of bibliography entries for scipy module """ from ..entries import Doi, BibTeX # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('scipy', None, BibTeX(""" @Misc{JOP+01, author = {Eric Jones and Travis Oliphant and Pearu Peterson and others}, title = {{SciPy}: Open source scientific tools for {Python}}, year = {2001--}, url = "http://www.scipy.org/", note = {[Online; accessed 2015-07-13]} }"""), description="Scientific tools library", tags=['implementation']) # scipy.cluster.hierarchy general references # TODO: we should allow to pass a list of entries injector.add('scipy.cluster.hierarchy', None, BibTeX(""" @article{johnson1967hierarchical, title={Hierarchical clustering schemes}, author={Johnson, Stephen C}, journal={Psychometrika}, volume={32}, number={3}, pages={241--254}, year={1967}, publisher={Springer} }"""), min_version='0.4.3', description="Hierarchical clustering", tags=['edu']) injector.add('scipy.cluster.hierarchy', None, BibTeX(""" @article{sneath1962numerical, title={Numerical taxonomy}, author={Sneath, Peter HA and Sokal, Robert R}, journal={Nature}, volume={193}, number={4818}, pages={855--860}, year={1962}, publisher={Nature Publishing Group} }"""), description="Hierarchical clustering", min_version='0.4.3', tags=['edu']) injector.add('scipy.cluster.hierarchy', None, BibTeX(""" @article{batagelj1995comparing, title={Comparing resemblance measures}, author={Batagelj, Vladimir and Bren, Matevz}, journal={Journal of classification}, volume={12}, number={1}, pages={73--90}, year={1995}, publisher={Springer} }"""), description="Hierarchical clustering", min_version='0.4.3', tags=['edu']) injector.add('scipy.cluster.hierarchy', None, BibTeX(""" @book{sokal1958statistical, author = {Sokal, R R and Michener, C D and {University of Kansas}}, title = {{A Statistical Method for Evaluating Systematic Relationships}}, publisher = {University of Kansas}, year = {1958}, series = {University of Kansas science bulletin} }"""), description="Hierarchical clustering", min_version='0.4.3', tags=['edu']) injector.add('scipy.cluster.hierarchy', None, BibTeX(""" @article{edelbrock1979mixture, title={Mixture model tests of hierarchical clustering algorithms: the problem of classifying everybody}, author={Edelbrock, Craig}, journal={Multivariate Behavioral Research}, volume={14}, number={3}, pages={367--384}, year={1979}, publisher={Taylor \& Francis} }"""), description="Hierarchical clustering", min_version='0.4.3', tags=['edu']) injector.add('scipy.cluster.hierarchy', None, BibTeX(""" @book{jain1988algorithms, title={Algorithms for clustering data}, author={Jain, Anil K and Dubes, Richard C}, year={1988}, publisher={Prentice-Hall, Inc.} }"""), description="Hierarchical clustering", min_version='0.4.3', tags=['edu']) injector.add('scipy.cluster.hierarchy', None, BibTeX(""" @article{fisher1936use, title={The use of multiple measurements in taxonomic problems}, author={Fisher, Ronald A}, journal={Annals of eugenics}, volume={7}, number={2}, pages={179--188}, year={1936}, publisher={Wiley Online Library} }"""), description="Hierarchical clustering", min_version='0.4.3', tags=['edu']) # Here options for linkage injector.add('scipy.cluster.hierarchy', 'linkage', BibTeX(""" @article{ward1963hierarchical, title={Hierarchical grouping to optimize an objective function}, author={Ward Jr, Joe H}, journal={Journal of the American statistical association}, volume={58}, number={301}, pages={236--244}, year={1963}, publisher={Taylor \& Francis} }"""), conditions={(1, 'method'): {'ward'}}, description="Ward hierarchical clustering", min_version='0.4.3', tags=['reference']) injector.add('scipy.cluster.hierarchy', 'linkage', BibTeX(""" @article{gower1969minimum, title={Minimum spanning trees and single linkage cluster analysis}, author={Gower, John C and Ross, GJS}, journal={Applied statistics}, pages={54--64}, year={1969}, publisher={JSTOR} }"""), conditions={(1, 'method'): {'single', 'DC_DEFAULT'}}, description="Single linkage hierarchical clustering", min_version='0.4.3', tags=['reference']) injector.add('scipy.cluster.hierarchy', 'linkage', BibTeX(""" @article{sibson1973slink, title={SLINK: an optimally efficient algorithm for the single-link cluster method}, author={Sibson, Robin}, journal={The Computer Journal}, volume={16}, number={1}, pages={30--34}, year={1973}, publisher={Br Computer Soc} }"""), conditions={(1, 'method'): {'single', 'DC_DEFAULT'}}, description="Single linkage hierarchical clustering", min_version='0.4.3', tags=['implementation']) duecredit-0.8.0/duecredit/injections/mod_skimage.py000066400000000000000000000015521362001701600224340ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Automatic injection of bibliography entries for skimage module """ from ..entries import Doi, BibTeX # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): #http://scikit-image.org injector.add('skimage', None, Doi('10.7717/peerj.453'), description='scikit-image: Image processing in Python.', tags=['implementation']) duecredit-0.8.0/duecredit/injections/mod_sklearn.py000066400000000000000000000112471362001701600224550ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Automatic injection of bibliography entries for numpy module """ from ..entries import Doi, BibTeX, Url # If defined, would determine from which to which version of the corresponding # module to care about min_version = None max_version = None def inject(injector): injector.add('sklearn', None, BibTeX(""" @article{pedregosa2011scikit, title={Scikit-learn: Machine learning in Python}, author={Pedregosa, Fabian and Varoquaux, Ga{\"e}l and Gramfort, Alexandre and Michel, Vincent and Thirion, Bertrand and Grisel, Olivier and Blondel, Mathieu and Prettenhofer, Peter and Weiss, Ron and Dubourg, Vincent and others}, journal={The Journal of Machine Learning Research}, volume={12}, pages={2825--2830}, year={2011}, publisher={JMLR.org} } """), description="Machine Learning library") # sklearn.cluster.affinity_propagation_ injector.add('sklearn.cluster.affinity_propagation_', None, Doi('10.1126/science.1136800'), description="Affinity propagation clustering algorithm", tags=['implementation']) # sklearn.cluster.bicluster injector.add('sklearn.cluster.bicluster', 'SpectralCoclustering._fit', Doi('10.1101/gr.648603'), description="Spectral Coclustering algorithm", tags=['implementation']) injector.add('sklearn.cluster.bicluster', 'SpectralBiclustering._fit', Doi('10.1101/gr.648603'), description="Spectral Biclustering algorithm", tags=['implementation']) # sklearn.cluster.birch injector.add('sklearn.cluster.birch', 'Birch._fit', Doi('10.1145/233269.233324'), description="BIRCH clustering algorithm", tags=['implementation']) injector.add('sklearn.cluster.birch', 'Birch._fit', Url('https://code.google.com/p/jbirch/'), description="Java implementation of BIRCH clustering algorithm", tags=['another-implementation']) # sklearn.cluster.dbscan_ injector.add('sklearn.cluster.dbscan_', 'dbscan', BibTeX("""@inproceedings{ester1996density, title={A density-based algorithm for discovering clusters in large spatial databases with noise.}, author={Ester, Martin and Kriegel, Hans-Peter and Sander, J{\"o}rg and Xu, Xiaowei}, booktitle={Kdd}, volume={96}, number={34}, pages={226--231}, year={1996} }"""), description="dbscan clustering algorithm", tags=['implementation']) # sklearn.cluster.mean_shift_ injector.add('sklearn.cluster.mean_shift_', 'mean_shift', Doi('10.1109/34.1000236'), description="Mean shift clustering algorithm", tags=['implementation']) # sklearn.cluster.spectral injector.add('sklearn.cluster.spectral', 'discretize', Doi('10.1109/ICCV.2003.1238361'), description="Multiclass spectral clustering", tags=['reference']) injector.add('sklearn.cluster.spectral', 'spectral_clustering', Doi('10.1109/34.868688'), description="Spectral clustering", tags=['implementation']) injector.add('sklearn.cluster.spectral', 'spectral_clustering', Doi('10.1007/s11222-007-9033-z'), description="Spectral clustering", tags=['implementation']) # sklearn.ensemble.forest and tree Breiman_2001 = Doi("10.1023/A:1010933404324") Breiman_1984 = BibTeX("""@BOOK{breiman-friedman-olshen-stone-1984, author = {L. Breiman and J. Friedman and R. Olshen and C. Stone}, title = {{Classification and Regression Trees}}, publisher = {Wadsworth and Brooks}, address = {Monterey, CA}, year = {1984}, }""") # Not clear here though if those are the original publication on the topic # or just an educational references (books), most probably both ;) injector.add('sklearn.ensemble.forest', 'RandomForestClassifier.predict_proba', Breiman_2001, description="Random forest classifiers", tags=['implementation', 'edu']) injector.add('sklearn.ensemble.forest', 'RandomForestRegressor.predict', Breiman_2001, description="Random forest regressions", tags=['implementation', 'edu']) injector.add('sklearn.tree.tree', 'DecisionTreeClassifier.predict_proba', Breiman_1984, description="Classification and regression trees", tags=['implementation', 'edu']) duecredit-0.8.0/duecredit/io.py000066400000000000000000000343661362001701600164300ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # Just for testing of robust operation import os if 'DUECREDIT_TEST_EARLY_IMPORT_ERROR' in os.environ.keys(): raise ImportError("DUECREDIT_TEST_EARLY_IMPORT_ERROR") import re import locale import time from collections import defaultdict import copy from os.path import dirname, exists import pickle import tempfile from six import PY2, itervalues, iteritems import warnings import platform from time import sleep from .config import CACHE_DIR, DUECREDIT_FILE from .entries import BibTeX, Doi, Text, Url from .log import lgr from .versions import external_versions _PREFERRED_ENCODING = locale.getpreferredencoding() platform_system = platform.system().lower() on_windows = platform_system == 'windows' def get_doi_cache_file(doi): # where to cache bibtex entries if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR) return os.path.join(CACHE_DIR, doi) def import_doi(doi, sleep=0.5, retries=10): import requests cached = get_doi_cache_file(doi) if exists(cached): with open(cached) as f: doi = f.read() if PY2: return doi.decode('utf-8') return doi # else -- fetch it headers = {'Accept': 'application/x-bibtex; charset=utf-8'} url = 'https://doi.org/' + doi while retries > 0: lgr.debug("Submitting GET to %s with headers %s", url, headers) r = requests.get(url, headers=headers) r.encoding = 'UTF-8' bibtex = r.text.strip() if bibtex.startswith('@'): # no more retries necessary break lgr.warning("Failed to obtain bibtex from doi.org, retrying...") time.sleep(sleep) # give some time to the server retries -= 1 status_code = r.status_code if not bibtex.startswith('@'): raise ValueError('Query %(url)s for BibTeX for a DOI %(doi)s (wrong doi?) has failed. ' 'Response code %(status_code)d. ' #'BibTeX response was: %(bibtex)s' % locals()) if not exists(cached): cache_dir = dirname(cached) if not exists(cache_dir): os.makedirs(cache_dir) with open(cached, 'w') as f: if PY2: f.write(bibtex.encode('utf-8')) else: f.write(bibtex) return bibtex def _is_contained(toppath, subpath): if ':' not in toppath: return ((toppath == subpath) or (subpath.startswith(toppath + '.')) or (subpath.startswith(toppath + ':'))) else: return subpath.startswith(toppath + '.') class Output(object): """A generic class for setting up citations that then will be outputted differently (e.g., Bibtex, Text, etc.)""" def __init__(self, fd, collector): self.fd = fd self.collector = collector def _get_collated_citations(self, tags=None, all_=None): """Given all the citations, filter only those that the user wants and those that were actually used""" if not tags: tags = os.environ.get('DUECREDIT_REPORT_TAGS', 'reference-implementation,implementation,dataset').split(',') if all_ is None: # consult env var all_ = os.environ.get('DUECREDIT_REPORT_ALL', '').lower() in {'1', 'true', 'yes', 'on'} tags = set(tags) citations = self.collector.citations if tags != {'*'}: # Filter out citations based on tags citations = dict((k, c) for k, c in iteritems(citations) if tags.intersection(c.tags)) packages = defaultdict(list) modules = defaultdict(list) objects = defaultdict(list) # store the citations according to their path and divide them into # the right level for (path, entry_key), citation in iteritems(citations): if ':' in path: objects[path].append(citation) elif '.' in path: modules[path].append(citation) else: packages[path].append(citation) # now we need to filter out the packages that don't have modules # or objects cited, or are specifically requested cited_packages = list(packages) cited_modobj = list(modules) + list(objects) for package in cited_packages: package_citations = packages[package] if all_ or \ any(filter(lambda x: x.cite_module, package_citations)) or \ any(filter(lambda x: _is_contained(package, x), cited_modobj)): continue else: # we don't need it del packages[package] return packages, modules, objects def dump(self, tags=None): raise NotImplementedError class TextOutput(Output): def __init__(self, fd, collector, style=None): super(TextOutput, self).__init__(fd, collector) self.style = style if 'DUECREDIT_STYLE' in os.environ.keys(): self.style = os.environ['DUECREDIT_STYLE'] else: self.style = 'harvard1' @staticmethod def _format_citations(citations, citation_nr): descriptions = map(str, set(str(r.description) for r in citations)) versions = map(str, set(str(r.version) for r in citations)) refnrs = map(str, [citation_nr[c.entry.key] for c in citations]) path = citations[0].path return '- {0} / {1} (v {2}) [{3}]\n'.format( ", ".join(descriptions), path, ', '.join(versions), ', '.join(refnrs)) def dump(self, tags=None): # get 'model' of citations packages, modules, objects = self._get_collated_citations(tags) # put everything into a single dict pmo = {} pmo.update(packages) pmo.update(modules) pmo.update(objects) # get all the paths paths = sorted(list(pmo)) # get all the entry_keys in order entry_keys = [c.entry.key for p in paths for c in pmo[p]] # make a dictionary entry_key -> citation_nr citation_nr = defaultdict(int) refnr = 1 for entry_key in entry_keys: if entry_key not in citation_nr: citation_nr[entry_key] = refnr refnr += 1 self.fd.write('\nDueCredit Report:\n') start_refnr = 1 for path in paths: # since they're lexicographically sorted by path, dependencies # should be maintained cit = pmo[path] if ':' in path or '.' in path: self.fd.write(' ') self.fd.write(self._format_citations(cit, citation_nr)) start_refnr += len(cit) # Print out some stats stats = [(len(packages), 'package'), (len(modules), 'module'), (len(objects), 'function')] for n, cit_type in stats: self.fd.write('\n{0} {1} cited'.format(n, cit_type if n == 1 else cit_type + 's')) # now print out references printed_keys = [] if len(pmo) > 0: self.fd.write('\n\nReferences\n' + '-' * 10 + '\n') for path in paths: for cit in pmo[path]: ek = cit.entry.key if ek not in printed_keys: self.fd.write('\n[{0}] '.format(citation_nr[ek])) self.fd.write(get_text_rendering(cit, style=self.style)) printed_keys.append(ek) self.fd.write('\n') def get_text_rendering(citation, style='harvard1'): from .collector import Citation entry = citation.entry if isinstance(entry, Doi): bibtex_rendering = get_bibtex_rendering(entry) bibtex_citation = copy.copy(citation) bibtex_citation.set_entry(bibtex_rendering) return get_text_rendering(bibtex_citation) elif isinstance(entry, BibTeX): return format_bibtex(entry, style=style) elif isinstance(entry, Text): return entry.format() elif isinstance(entry, Url): return "URL: {}".format(entry.format()) else: return str(entry) def get_bibtex_rendering(entry): if isinstance(entry, Doi): return BibTeX(import_doi(entry.doi)) elif isinstance(entry, BibTeX): return entry else: raise ValueError("Have no clue how to get bibtex out of %s" % entry) def condition_bibtex(bibtex): """Given a bibtex entry, "condition" it for processing with citeproc Primarily a set of workarounds for either non-standard BibTeX entries or citeproc bugs """ # XXX: workaround atm to fix zenodo bibtexs, convert @data to @misc # and also ; into and if bibtex.startswith('@data'): bibtex = bibtex.replace('@data', '@misc', 1) bibtex = bibtex.replace(';', ' and') bibtex = bibtex.replace(u'\u2013', '--') + "\n" # workaround for citeproc 0.3.0 failing to parse a single page pages field # as for BIDS paper. Workaround to add trailing + after pages number # related issue asking for a new release: https://github.com/brechtm/citeproc-py/issues/72 bibtex = re.sub(r'(pages\s*=\s*["{]\d+)(["}])', r'\1+\2', bibtex) # partial workaround for citeproc failing to parse page numbers when they contain non-numeric characters # remove opening letter, e.g. 'S123' -> '123' # related issue: https://github.com/brechtm/citeproc-py/issues/74 bibtex = re.sub(r'(pages\s*=\s*["{])([a-zA-Z])', r'\g<1>', bibtex) bibtex = bibtex.encode('utf-8') return bibtex def format_bibtex(bibtex_entry, style='harvard1'): try: from citeproc.source.bibtex import BibTeX as cpBibTeX import citeproc as cp except ImportError as e: raise RuntimeError( "For formatted output we need citeproc and all of its dependencies " "(such as lxml) but there is a problem while importing citeproc: %s" % str(e)) decode_exceptions = UnicodeDecodeError try: from citeproc.source.bibtex.bibparse import BibTeXDecodeError decode_exceptions = (decode_exceptions, BibTeXDecodeError) except ImportError: # this version doesn't yet have this exception defined pass key = bibtex_entry.get_key() # need to save it temporarily to use citeproc-py fname = tempfile.mktemp(suffix='.bib') try: with open(fname, 'wb') as f: f.write(condition_bibtex(bibtex_entry.rawentry)) # We need to avoid cpBibTex spitting out warnings old_filters = warnings.filters[:] # store a copy of filters warnings.simplefilter('ignore', UserWarning) try: try: bib_source = cpBibTeX(fname) except decode_exceptions as e: # So .bib must be having UTF-8 characters. With # a recent (not yet released past v0.3.0-68-g9800dad # we should be able to provide encoding argument bib_source = cpBibTeX(fname, encoding='utf-8') except Exception as e: msg = "Failed to process BibTeX file %s: %s." % (fname, e) citeproc_version = external_versions['citeproc'] if 'unexpected keyword argument' in str(e) and \ citeproc_version and citeproc_version < '0.4': err = "need a newer citeproc-py >= 0.4.0" msg += " You might just " + err else: err = str(e) lgr.error(msg) return "ERRORED: %s" % err finally: # return warnings back warnings.filters = old_filters bib_style = cp.CitationStylesStyle(style, validate=False) # TODO: specify which tags of formatter we want bibliography = cp.CitationStylesBibliography(bib_style, bib_source, cp.formatter.plain) citation = cp.Citation([cp.CitationItem(key)]) bibliography.register(citation) finally: if not os.environ.get("DUECREDIT_KEEPTEMP"): exceptions = (OSError, WindowsError) if on_windows else OSError for i in range(50): try: os.unlink(fname) except exceptions: if i < 49: sleep(0.1) continue else: raise break biblio_out = bibliography.bibliography() assert(len(biblio_out) == 1) biblio_out = ''.join(biblio_out[0]) return biblio_out # if biblio_out else str(bibtex_entry) # TODO: harmonize order of arguments class PickleOutput(object): def __init__(self, collector, fn=DUECREDIT_FILE): self.collector = collector self.fn = fn def dump(self): with open(self.fn, 'wb') as f: pickle.dump(self.collector, f) @classmethod def load(cls, filename=DUECREDIT_FILE): with open(filename, 'rb') as f: return pickle.load(f) class BibTeXOutput(Output): def __init__(self, fd, collector): super(BibTeXOutput, self).__init__(fd, collector) def dump(self, tags=None): packages, modules, objects = self._get_collated_citations(tags) # get all the citations in order pmo = {} pmo.update(packages) pmo.update(modules) pmo.update(objects) # get all the paths paths = sorted(list(pmo)) entries = [] for path in paths: for c in pmo[path]: if c.entry not in entries: entries.append(c.entry) for entry in entries: try: bibtex = get_bibtex_rendering(entry) except: lgr.warning("Failed to generate bibtex for %s" % entry) continue self.fd.write(bibtex.rawentry + "\n") def load_due(filename): return PickleOutput.load(filename) duecredit-0.8.0/duecredit/log.py000066400000000000000000000174721362001701600166010ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. This file originates from datalad distributed # under MIT license. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import logging, os, sys, platform import logging.handlers from .utils import is_interactive __all__ = ['ColorFormatter', 'log'] # Snippets from traceback borrowed from PyMVPA upstream/2.4.0-39-g69ad545 MIT license import traceback import re from os.path import basename, dirname def mbasename(s): """Custom function to include directory name if filename is too common Also strip .py at the end """ base = basename(s) if base.endswith('.py'): base = base[:-3] if base in set(['base', '__init__']): base = basename(dirname(s)) + '.' + base return base class TraceBack(object): """Customized traceback to be included in debug messages """ def __init__(self, collide=False): """Initialize TraceBack metric Parameters ---------- collide : bool if True then prefix common with previous invocation gets replaced with ... """ self.__prev = "" self.__collide = collide def __call__(self): ftb = traceback.extract_stack(limit=100)[:-2] entries = [[mbasename(x[0]), str(x[1])] for x in ftb if mbasename(x[0]) != 'logging.__init__'] entries = [ e for e in entries if e[0] != 'unittest' ] # lets make it more concise entries_out = [entries[0]] for entry in entries[1:]: if entry[0] == entries_out[-1][0]: entries_out[-1][1] += ',%s' % entry[1] else: entries_out.append(entry) sftb = '>'.join(['%s:%s' % (mbasename(x[0]), x[1]) for x in entries_out]) if self.__collide: # lets remove part which is common with previous invocation prev_next = sftb common_prefix = os.path.commonprefix((self.__prev, sftb)) common_prefix2 = re.sub('>[^>]*$', '', common_prefix) if common_prefix2 != "": sftb = '...' + sftb[len(common_prefix2):] self.__prev = prev_next return sftb # Recipe from http://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output # by Brandon Thomson # Adjusted for automagic determination either coloring is needed and # prefixing of multiline log lines class ColorFormatter(logging.Formatter): BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) RESET_SEQ = "\033[0m" COLOR_SEQ = "\033[1;%dm" BOLD_SEQ = "\033[1m" COLORS = { 'WARNING': YELLOW, 'INFO': WHITE, 'DEBUG': BLUE, 'CRITICAL': YELLOW, 'ERROR': RED } def __init__(self, use_color=None, log_name=False): if use_color is None: # if 'auto' - use color only if all streams are tty use_color = is_interactive() self.use_color = use_color and platform.system() != 'Windows' # don't use color on windows msg = self.formatter_msg(self._get_format(log_name), self.use_color) self._tb = TraceBack(collide=os.environ.get('DUECREDIT_LOGTRACEBACK', '') == 'collide') \ if os.environ.get('DUECREDIT_LOGTRACEBACK', False) else None logging.Formatter.__init__(self, msg) def _get_format(self, log_name=False): return ("$BOLD%(asctime)-15s$RESET " + ("%(name)-15s " if log_name else "") + "[%(levelname)s] " "%(message)s " "($BOLD%(filename)s$RESET:%(lineno)d)") def formatter_msg(self, fmt, use_color=False): if use_color: fmt = fmt.replace("$RESET", self.RESET_SEQ).replace("$BOLD", self.BOLD_SEQ) else: fmt = fmt.replace("$RESET", "").replace("$BOLD", "") return fmt def format(self, record): if record.msg.startswith('| '): # If we already log smth which supposed to go without formatting, like # output for running a command, just return the message and be done return record.msg levelname = record.levelname if self.use_color and levelname in self.COLORS: fore_color = 30 + self.COLORS[levelname] levelname_color = self.COLOR_SEQ % fore_color + "%-7s" % levelname + self.RESET_SEQ record.levelname = levelname_color record.msg = record.msg.replace("\n", "\n| ") if self._tb: record.msg = self._tb() + " " + record.msg return logging.Formatter.format(self, record) class LoggerHelper(object): """Helper to establish and control a Logger""" def __init__(self, name='duecredit'): self.name = name self.lgr = logging.getLogger(name) def _get_environ(self, var, default=None): return os.environ.get(self.name.upper() + '_%s' % var.upper(), default) def set_level(self, level=None, default='WARNING'): """Helper to set loglevel for an arbitrary logger By default operates for 'duecredit'. TODO: deduce name from upper module name so it could be reused without changes """ if level is None: # see if nothing in the environment level = self._get_environ('LOGLEVEL') if level is None: level = default try: # it might be a string which still represents an int log_level = int(level) except ValueError: # or a string which corresponds to a constant;) log_level = getattr(logging, level.upper()) self.lgr.setLevel(log_level) def get_initialized_logger(self, logtarget=None): """Initialize and return the logger Parameters ---------- target: string, optional Which log target to request logger for logtarget: { 'stdout', 'stderr', str }, optional Where to direct the logs. stdout and stderr stand for standard streams. Any other string is considered a filename. Multiple entries could be specified comma-separated Returns ------- logging.Logger """ # By default mimic previously talkative behavior logtarget = self._get_environ('LOGTARGET', logtarget or 'stdout') # Allow for multiple handlers being specified, comma-separated if ',' in logtarget: for handler_ in logtarget.split(','): self.get_initialized_logger(logtarget=handler_) return self.lgr if logtarget.lower() in ('stdout', 'stderr') : loghandler = logging.StreamHandler(getattr(sys, logtarget.lower())) use_color = is_interactive() # explicitly decide here else: # must be a simple filename # Use RotatingFileHandler for possible future parametrization to keep # log succinct and rotating loghandler = logging.handlers.RotatingFileHandler(logtarget) use_color = False # I had decided not to guard this call and just raise exception to go # out happen that specified file location is not writable etc. # But now improve with colors and useful information such as time loghandler.setFormatter( ColorFormatter(use_color=use_color, log_name=self._get_environ("LOGNAME", False))) #logging.Formatter('%(asctime)-15s %(levelname)-6s %(message)s')) self.lgr.addHandler(loghandler) self.set_level() # set default logging level return self.lgr lgr = LoggerHelper().get_initialized_logger() duecredit-0.8.0/duecredit/parsers.py000066400000000000000000000015411362001701600174650ustar00rootroot00000000000000import re def extract_references_from_rst(rst): # for now will be very simple, just trying to separate # then up until the end or another section starting pass def test_extract_references_from_rst(): # some obscure examples of how people specify references samples = [ """ References ---------- .. [1] line1 line2 .. [2] line11 line12 """, """ References ---------- - line1 line2 """, """ References ---------- .. [xyz1] line1 line2 .. [xyz2] line11 line12 Buga duga --------- """, """ References ---------- line1 line2 line11 line12 """ ] extract_references_from_rst(samples[0]) duecredit-0.8.0/duecredit/stub.py000066400000000000000000000037421362001701600167700ustar00rootroot00000000000000# emacs: at the end of the file # ex: set sts=4 ts=4 sw=4 et: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### # """ Stub file for a guaranteed safe import of duecredit constructs: if duecredit is not available. To use it, place it into your project codebase to be imported, e.g. copy as cp stub.py /path/tomodule/module/due.py Note that it might be better to avoid naming it duecredit.py to avoid shadowing installed duecredit. Then use in your code as from .due import due, Doi, BibTeX, Text See https://github.com/duecredit/duecredit/blob/master/README.md for examples. Origin: Originally a part of the duecredit Copyright: 2015-2019 DueCredit developers License: BSD-2 """ __version__ = '0.0.8' class InactiveDueCreditCollector(object): """Just a stub at the Collector which would not do anything""" def _donothing(self, *args, **kwargs): """Perform no good and no bad""" pass def dcite(self, *args, **kwargs): """If I could cite I would""" def nondecorating_decorator(func): return func return nondecorating_decorator active = False activate = add = cite = dump = load = _donothing def __repr__(self): return self.__class__.__name__ + '()' def _donothing_func(*args, **kwargs): """Perform no good and no bad""" pass try: from duecredit import due, BibTeX, Doi, Url, Text if 'due' in locals() and not hasattr(due, 'cite'): raise RuntimeError( "Imported due lacks .cite. DueCredit is now disabled") except Exception as e: if not isinstance(e, ImportError): import logging logging.getLogger("duecredit").error( "Failed to import duecredit due to %s" % str(e)) # Initiate due stub due = InactiveDueCreditCollector() BibTeX = Doi = Url = Text = _donothing_func # Emacs mode definitions # Local Variables: # mode: python # py-indent-offset: 4 # tab-width: 4 # indent-tabs-mode: nil # End: duecredit-0.8.0/duecredit/tests/000077500000000000000000000000001362001701600165755ustar00rootroot00000000000000duecredit-0.8.0/duecredit/tests/__init__.py000066400000000000000000000000001362001701600206740ustar00rootroot00000000000000duecredit-0.8.0/duecredit/tests/envs/000077500000000000000000000000001362001701600175505ustar00rootroot00000000000000duecredit-0.8.0/duecredit/tests/envs/nolxml/000077500000000000000000000000001362001701600210615ustar00rootroot00000000000000duecredit-0.8.0/duecredit/tests/envs/nolxml/lxml.py000066400000000000000000000000221362001701600224010ustar00rootroot00000000000000raise ImportError duecredit-0.8.0/duecredit/tests/mod/000077500000000000000000000000001362001701600173545ustar00rootroot00000000000000duecredit-0.8.0/duecredit/tests/mod/__init__.py000066400000000000000000000001641362001701600214660ustar00rootroot00000000000000"""Module to test various duecredit functionality, e.g. injections""" from .imported import * __version__ = '0.5' duecredit-0.8.0/duecredit/tests/mod/imported.py000066400000000000000000000011151362001701600215470ustar00rootroot00000000000000def testfunc1(arg1, kwarg1=None): """custom docstring""" return "testfunc1: %s, %s" % (arg1, kwarg1) class TestClass1(object): """wrong custom docstring""" def testmeth1(self, arg1, kwarg1=None): """custom docstring""" return "TestClass1.testmeth1: %s, %s" % (arg1, kwarg1) class TestClass12(object): """wrong custom docstring""" class Embed(object): """wrong custom docstring""" def testmeth1(self, arg1, kwarg1=None): """custom docstring""" return "TestClass12.Embed.testmeth1: %s, %s" % (arg1, kwarg1) duecredit-0.8.0/duecredit/tests/mod/submod.py000066400000000000000000000004021362001701600212130ustar00rootroot00000000000000"""Some test submodule""" def testfunc(arg1, kwarg1=None): """testfunc docstring""" return "testfunc: %s, %s" % (arg1, kwarg1) class TestClass(object): def testmeth(self, arg1, kwarg1=None): return "testmeth: %s, %s" % (arg1, kwarg1) duecredit-0.8.0/duecredit/tests/test__main__.py000066400000000000000000000033671362001701600216000ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import sys import pytest from six.moves import StringIO from .. import __main__, __version__ from .. import due def test_main_help(monkeypatch): # Patch stdout fakestdout = StringIO() monkeypatch.setattr(sys, "stdout", fakestdout) pytest.raises(SystemExit, __main__.main, ['__main__.py', '--help']) assert( fakestdout.getvalue().startswith( "Usage: %s -m duecredit [OPTIONS] [ARGS]\n" % sys.executable)) def test_main_version(monkeypatch): # Patch stdout fakestdout = StringIO() monkeypatch.setattr(sys, "stdout", fakestdout) pytest.raises(SystemExit, __main__.main, ['__main__.py', '--version']) assert fakestdout.getvalue().rstrip() == "duecredit %s" % __version__ def test_main_run_a_script(tmpdir, monkeypatch): tempfile = str(tmpdir.mkdir("sub").join("tempfile.txt")) content = b'print("Running the script")\n' with open(tempfile, 'wb') as f: f.write(content) # Patch stdout fakestdout = StringIO() monkeypatch.setattr(sys, "stdout", fakestdout) # Patch due.activate count = [0] def count_calls(*args, **kwargs): count[0] += 1 monkeypatch.setattr(due, "activate", count_calls) __main__.main(['__main__.py', tempfile]) assert fakestdout.getvalue().rstrip() == "Running the script" # And we have "activated" the due assert count[0] == 1 duecredit-0.8.0/duecredit/tests/test_api.py000066400000000000000000000157561362001701600207750ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import os import sys import pytest import shutil from os.path import dirname, join as pathjoin, pardir, normpath from subprocess import Popen, PIPE from duecredit.collector import DueCreditCollector from duecredit.stub import InactiveDueCreditCollector from duecredit.entries import BibTeX, Doi from ..utils import on_windows import tempfile # temporary location where stuff would be copied badlxml_path = pathjoin(dirname(__file__), 'envs', 'nolxml') stubbed_dir = tempfile.mktemp() stubbed_script = pathjoin(pathjoin(stubbed_dir, 'script.py')) @pytest.fixture(scope="module") def stubbed_env(): """Create stubbed module with a sample script""" os.makedirs(stubbed_dir) with open(stubbed_script, 'wb') as f: f.write(""" from due import due, Doi kwargs = dict( entry=Doi("10.1007/s12021-008-9041-y"), description="Multivariate pattern analysis of neural data", tags=["use"] ) due.cite(path="test", **kwargs) @due.dcite(**kwargs) def method(arg): return arg+1 assert method(1) == 2 print("done123") """.encode()) # copy stub.py under stubbed shutil.copy( pathjoin(dirname(__file__), os.pardir, 'stub.py'), pathjoin(stubbed_dir, 'due.py') ) yield stubbed_script # cleanup shutil.rmtree(stubbed_dir) @pytest.mark.parametrize( 'collector_class', [DueCreditCollector, InactiveDueCreditCollector] ) def test_api(collector_class): due = collector_class() # add references due.add(BibTeX('@article{XXX00, ...}')) # could even be by DOI -- we need to fetch and cache those due.add(Doi("xxx.yyy/zzz.1", key="XXX01")) # and/or load multiple from a file due.load('/home/siiioul/deep/good_intentions.bib') # Cite entire module due.cite('XXX00', description="Answers to existential questions", path="module") # Cite some method within some submodule due.cite('XXX01', description="More answers to existential questions", path="module.submodule:class1.whoknowswhat2.func1") # dcite for decorator cite # cite specific functionality if/when it gets called up @due.dcite('XXX00', description="Provides an answer for meaningless existence") def purpose_of_life(): return None class Child(object): # Conception process is usually way too easy to be referenced def __init__(self): pass # including functionality within/by the methods @due.dcite('XXX00') def birth(self, gender): return "Rachel was born" kid = Child() kid.birth("female") def run_python_command(cmd=None, script=None): """Just a tiny helper which runs command and returns exit code, stdout, stderr""" assert bool(cmd) != bool(script) # one or another, not both args = ['-c', cmd] if cmd else [script] try: # run script from some temporary directory so we do not breed .duecredit.p # in current directory tmpdir = tempfile.mkdtemp() python = Popen([sys.executable] + args, stdout=PIPE, stderr=PIPE, cwd=tmpdir) stdout, stderr = python.communicate() # wait() ret = python.poll() finally: shutil.rmtree(tmpdir) # TODO stdout cannot decode on Windows special character /x introduced return ret, stdout.decode(errors='ignore'), stderr.decode() # Since duecredit and possibly lxml already loaded, let's just test # ability to import in absence of lxml via external call to python def test_noincorrect_import_if_no_lxml(monkeypatch): if on_windows: pytest.xfail("Fails for some reason on Windows") monkeypatch.setitem(os.environ, 'PYTHONPATH', "%s:%s" % (badlxml_path, os.environ.get('PYTHONPATH', ''))) ret, out, err = run_python_command('import lxml') assert ret == 1 assert 'ImportError' in err ret, out, err = run_python_command('import duecredit') assert err == '' assert out == '' assert ret == 0 @pytest.mark.parametrize( "env", [{}, {'DUECREDIT_ENABLE': 'yes'}, {'DUECREDIT_ENABLE': 'yes', 'DUECREDIT_REPORT_TAGS' :'*'}, {'DUECREDIT_TEST_EARLY_IMPORT_ERROR': 'yes'} ]) @pytest.mark.parametrize( "kwargs", [ # direct command to evaluate {'cmd': 'import duecredit; import numpy as np; print("done123")'}, # script with decorated funcs etc -- should be importable {'script': stubbed_script} ]) def test_noincorrect_import_if_no_lxml_numpy(monkeypatch, kwargs, env, stubbed_env): # Now make sure that we would not crash entire process at the end when unable to # produce sensible output when we have something to cite # we do inject for numpy try: import numpy except ImportError: pytest.skip("We need to have numpy to test correct operation") fake_env_nolxml_ = {'PYTHONPATH': "%s:%s" % (badlxml_path, os.environ.get('PYTHONPATH', ''))}.copy() fake_env_nolxml_.update(env) for key in fake_env_nolxml_: monkeypatch.setitem(os.environ, key, fake_env_nolxml_[key]) ret, out, err = run_python_command(**kwargs) direct_duecredit_import = 'import duecredit' in kwargs.get('cmd', '') if direct_duecredit_import and env.get('DUECREDIT_TEST_EARLY_IMPORT_ERROR', ''): # We do fail then upon regular import but stubbed script should be ok # since should use the stub assert 'Both inactive and active collectors should be provided' in err assert ret == 1 else: assert err == '' assert ret == 0 # but we must not fail overall regardless if os.environ.get('DUECREDIT_ENABLE', False) and on_windows: # TODO this test fails on windows pytest.xfail("Fails for some reason on Windows") elif os.environ.get('DUECREDIT_ENABLE', False): # we enabled duecredit if (os.environ.get('DUECREDIT_REPORT_TAGS', None) == '*' and kwargs.get('script')) \ or 'numpy' in kwargs.get('cmd', ''): # we requested to have all tags output, and used bibtex in our entry assert 'For formatted output we need citeproc' in out else: # there was nothing to format so we did not fail for no reason assert 'For formatted output we need citeproc' not in out assert '0 packages cited' in out assert 'done123' in out elif os.environ.get('DUECREDIT_TEST_EARLY_IMPORT_ERROR'): assert 'ImportError' in out assert 'DUECREDIT_TEST_EARLY_IMPORT_ERROR' in out if direct_duecredit_import: assert 'Please report' in out else: assert 'done123' in out else: assert 'done123\n' or 'done123\r\n' == out if __name__ == '__main__': from duecredit import due test_api(due) duecredit-0.8.0/duecredit/tests/test_cmdline.py000066400000000000000000000030051362001701600216170ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import sys from six.moves import StringIO import pytest from .. import __version__ from ..cmdline import main def test_import(): import duecredit.cmdline import duecredit.cmdline.main def test_main_help(monkeypatch): # Patch stdout fakestdout = StringIO() monkeypatch.setattr(sys, "stdout", fakestdout) pytest.raises(SystemExit, main.main, ['--help']) assert fakestdout.getvalue().lstrip().startswith("Usage: ") def test_main_version(monkeypatch): # Patch stdout or stderr for different Python versions -- catching both fakestdout = StringIO() fakeout = 'std' + ('err' if sys.version_info < (3, 4) else 'out') monkeypatch.setattr(sys, fakeout, fakestdout) pytest.raises(SystemExit, main.main, ['--version']) assert (fakestdout.getvalue()).split('\n')[0] == "duecredit %s" % __version__ # smoke test the cmd_summary # TODO: carry sample .duecredit.p, point to that file, monkeypatch TextOutput and BibTeXOutput .dumps def test_smoke_cmd_summary(): main.main(['summary']) def test_cmd_test(): # test the not implemented cmd_test pytest.raises(SystemExit, main.main, ['test']) duecredit-0.8.0/duecredit/tests/test_collector.py000066400000000000000000000242361362001701600222030ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## from ..collector import DueCreditCollector, InactiveDueCreditCollector, \ CollectorSummary, Citation from ..dueswitch import DueSwitch from ..entries import BibTeX, Doi from ..io import PickleOutput import os import pytest def _test_entry(due, entry): due.add(entry) _sample_bibtex = """ @ARTICLE{XXX0, author = {Halchenko, Yaroslav O. and Hanke, Michael}, title = {Open is not enough. Let{'}s take the next step: An integrated, community-driven computing platform for neuroscience}, journal = {Frontiers in Neuroinformatics}, year = {2012}, volume = {6}, number = {00022}, doi = {10.3389/fninf.2012.00022}, issn = {1662-5196}, localfile = {HH12.pdf}, } """ _sample_bibtex2 = """ @ARTICLE{Atkins_2002, title = {title}, volume = {666}, url = {https://doi.org/10.1038/nrd842}, DOI = {10.1038/nrd842}, number = {3009}, journal = {My Fancy. Journ.}, publisher = {The Publisher}, author = {Atkins, Joshua H. and Gershell, Leland J.}, year = {2002}, month = {Jul}, } """ _sample_doi = "10.3389/fninf.2012.00022" def test_citation_paths(): entry = BibTeX(_sample_bibtex) cit1 = Citation(entry, path="somemodule") assert cit1.cites_module assert cit1.module == "somemodule" cit2 = Citation(entry, path="somemodule.submodule") assert cit2.cites_module assert cit2.module == "somemodule.submodule" assert cit1 in cit1 assert cit2 in cit1 assert cit1 not in cit2 cit3 = Citation(entry, path="somemodule.submodule:class2.func2") assert not cit3.cites_module assert cit3.module == "somemodule.submodule" assert cit2 in cit1 assert cit3 in cit1 assert cit3 in cit2 assert cit2 not in cit3 cit4 = Citation(entry, path="somemodule2:class2.func2") assert not cit4.cites_module assert cit4.module == "somemodule2" assert cit1 not in cit4 assert cit4 not in cit1 def test_entry(): entry = BibTeX(_sample_bibtex) _test_entry(DueCreditCollector(), entry) entries = [BibTeX(_sample_bibtex), BibTeX(_sample_bibtex), Doi(_sample_doi)] _test_entry(DueCreditCollector(), entries) def _test_dcite_basic(due, callable): assert callable("magical", 1) == "load" # verify that @wraps correctly passes all the docstrings etc assert callable.__name__ == "method" assert callable.__doc__ == "docstring" def test_dcite_method(): # Test basic wrapping that we don't mask out the arguments for due in [DueCreditCollector(), InactiveDueCreditCollector()]: active = isinstance(due, DueCreditCollector) due.add(BibTeX(_sample_bibtex)) @due.dcite("XXX0", path='method') def method(arg1, kwarg2="blah"): """docstring""" assert arg1 == "magical" assert kwarg2 == 1 return "load" class SomeClass(object): @due.dcite("XXX0", path='someclass:method') def method(self, arg1, kwarg2="blah"): """docstring""" assert arg1 == "magical" assert kwarg2 == 1 return "load" if active: assert due.citations == {} assert len(due._entries) == 1 _test_dcite_basic(due, method) if active: assert len(due.citations) == 1 assert len(due._entries) == 1 citation = due.citations[("method", "XXX0")] assert citation.count == 1 # TODO: this is probably incomplete path but unlikely we would know # any better assert citation.path == "method" instance = SomeClass() _test_dcite_basic(due, instance.method) if active: assert len(due.citations) == 2 assert len(due._entries) == 1 # TODO: we should actually get path/counts pairs so here citation = due.citations[("someclass:method", "XXX0")] assert citation.path == "someclass:method" assert citation.count == 1 # And we explicitly stated that module need to be cited assert citation.cite_module class SomeClass2(object): # Used to test for classes that are not instantiated @due.dcite("XXX0", path="some.module.without.method") def method2(self, arg1, kwarg2="blah"): assert arg1 == "magical" return "load" # and a method pointing to the module instance2 = SomeClass() _test_dcite_basic(due, instance2.method) if active: assert len(due.citations) == 2 # different paths assert len(due._entries) == 1 # the same entry # TODO: we should actually get path/counts pairs so here # it is already a different path # And we still explicitly stated that module need to be cited assert citation.cite_module def _test_args_match_conditions(conds): args_match_conditions = DueCreditCollector._args_match_conditions assert args_match_conditions(conds) assert args_match_conditions(conds, None) assert args_match_conditions(conds, someirrelevant=True) assert args_match_conditions(conds, method='purge') assert args_match_conditions(conds, method='fullpurge') assert args_match_conditions(conds, None, 'purge') assert args_match_conditions(conds, None, 'fullpurge') assert args_match_conditions(conds, None, 'fullpurge', someirrelevant="buga") assert not args_match_conditions(conds, None, 'push') assert not args_match_conditions(conds, method='push') if len(conds) < 2: return # got compound case assert args_match_conditions(conds, scope='life') assert not args_match_conditions(conds, scope='someother') # should be "and", so if one not matching -- both not matching assert not args_match_conditions(conds, method="wrong", scope='life') assert not args_match_conditions(conds, method="purge", scope='someother') # assert args_match_conditions(conds, None, None, 'life') # ambiguous/conflicting def test_args_match_conditions(): _test_args_match_conditions({(1, 'method'): {'purge', 'fullpurge', 'DC_DEFAULT'}}) _test_args_match_conditions({(1, 'method'): {'purge', 'fullpurge', 'DC_DEFAULT'}, (2, 'scope'): {'life', 'DC_DEFAULT'}}) def _test_dcite_match_conditions(due, callable, path): assert due.citations == {} assert len(due._entries) == 1 assert callable("magical", "unknown") == "load unknown" assert due.citations == {} assert len(due._entries) == 1 assert callable("magical") == "load blah" assert len(due.citations) == 1 assert len(due._entries) == 1 entry = due._entries['XXX0'] assert due.citations[(path, 'XXX0')].count == 1 # Cause the same citation assert callable("magical", "blah") == "load blah" # Nothing should change assert len(due.citations) == 1 assert len(due._entries) == 1 assert due.citations[(path, 'XXX0')].count == 2 # Besides the count # Now cause new citation given another value assert callable("magical", "boo") == "load boo" assert len(due.citations) == 2 assert len(due._entries) == 2 assert due.citations[(path, 'XXX0')].count == 2 # Count should stay the same for XXX0 assert due.citations[(path, "10.3389/fninf.2012.00022")].count == 1 # but we get a new one def test_dcite_match_conditions_function(): due = DueCreditCollector() due.add(BibTeX(_sample_bibtex)) @due.dcite("XXX0", path='callable', conditions={(1, "kwarg2"): {"blah", "DC_DEFAULT"}}) @due.dcite(Doi(_sample_doi), path='callable', conditions={(1, "kwarg2"): {"boo"}}) def method(arg1, kwarg2="blah"): """docstring""" assert arg1 == "magical" return "load %s" % kwarg2 _test_dcite_match_conditions(due, method, 'callable') def test_dcite_match_conditions_method(): due = DueCreditCollector() due.add(BibTeX(_sample_bibtex)) class Citeable(object): def __init__(self, param=None): self.param = param @due.dcite("XXX0", path='obj.callable', conditions={(2, "kwarg2"): {"blah", "DC_DEFAULT"}, (0, 'self.param'): {"paramvalue"} # must be matched }) @due.dcite(Doi(_sample_doi), path='obj.callable', conditions={(2, "kwarg2"): {"boo"}}) def method(self, arg1, kwarg2="blah"): """docstring""" assert arg1 == "magical" return "load %s" % kwarg2 citeable = Citeable(param="paramvalue") _test_dcite_match_conditions(due, citeable.method, 'obj.callable') # now test for self.param - def test_get_output_handler_method(tmpdir, monkeypatch): tempfile = str(tmpdir.mkdir("sub").join("tempfile.txt")) monkeypatch.setitem(os.environ, 'DUECREDIT_OUTPUTS', 'pickle') entry = BibTeX(_sample_bibtex) collector = DueCreditCollector() collector.cite(entry, path='module') summary = CollectorSummary(collector, fn=tempfile) handlers = [summary._get_output_handler(type_, collector) for type_ in ['pickle']] # assert isinstance(handlers[0], TextOutput) assert isinstance(handlers[0], PickleOutput) pytest.raises(NotImplementedError, summary._get_output_handler, 'nothing', collector) def test_collectors_uniform_api(): get_api = lambda objs: [x for x in sorted(sum((dir(obj) for obj in objs), [])) if not x.startswith('_') or x in '__call__'] assert get_api([DueCreditCollector, DueSwitch]) == get_api([InactiveDueCreditCollector]) def _test__docs__(method): assert "entry:" in method.__doc__ assert "tags: " in method.__doc__ def test__docs__(): _test__docs__(DueCreditCollector.cite) _test__docs__(DueCreditCollector.dcite) _test__docs__(Citation.__init__) duecredit-0.8.0/duecredit/tests/test_dueswitch.py000066400000000000000000000027571362001701600222200ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import atexit import pytest from ..injections.injector import DueCreditInjector from ..dueswitch import DueSwitch, due def test_dueswitch_activate(monkeypatch): if due.active: pytest.skip("due is already active, can't test more at this point") state = dict(activate=0, register=0, register_func=None) # Patch DueCreditInjector.activate def activate_calls(*args, **kwargs): state["activate"] += 1 monkeypatch.setattr(DueCreditInjector, "activate", activate_calls) # Patch atexit.register def register(func): state["register"] += 1 state["register_func"] = func monkeypatch.setattr(atexit, "register", register) due.activate() # was not active, so should have called activate of the injector class assert state["activate"] == 1 assert state["register"] == 1 assert state["register_func"] == due.dump def test_a_bad_one(): # We might get neither of those and should fail # see https://github.com/duecredit/duecredit/issues/142 # So let's through ValueError right away pytest.raises(ValueError, DueSwitch, None, None, True)duecredit-0.8.0/duecredit/tests/test_entries.py000066400000000000000000000023021362001701600216540ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil; coding: utf-8 -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import random import re import pickle import os import pytest from six.moves import StringIO from six import text_type import duecredit.io from ..collector import DueCreditCollector, Citation from .test_collector import _sample_bibtex, _sample_doi, _sample_bibtex2 from ..entries import BibTeX, Doi, Text, Url def test_comparison(): assert Text("123") == Text("123") assert Text("123") != Text("124") assert Text("123", 'key') == Text("123", 'key') assert Text("123", 'key') != Text("123", 'key1') assert Text("123", 'key') != Text("124", 'key') assert Doi("123/1", 'key') == Doi("123/1", 'key') assert Url("http://123/1", 'key') == Url("http://123/1", 'key') def test_sugaring_api(): assert Url("http://1.com").url == "http://1.com" assert Doi("1.com").doi == "1.com"duecredit-0.8.0/duecredit/tests/test_import_doi000066400000000000000000000325751362001701600217400ustar00rootroot00000000000000interactions: - request: body: null headers: Accept: [application/x-bibtex; charset=utf-8] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] User-Agent: [python-requests/2.18.4] method: GET uri: https://doi.org/10.1038/nrd842 response: body: {string: !!python/unicode 'Handle Redirect https://data.crossref.org/10.1038%2Fnrd842'} headers: cf-ray: [438fadfe5a8b5a68-BOS] connection: [keep-alive] content-length: ['169'] content-type: [text/html;charset=utf-8] date: ['Thu, 12 Jul 2018 01:19:06 GMT'] expect-ct: ['max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"'] expires: ['Thu, 12 Jul 2018 01:37:12 GMT'] location: ['https://data.crossref.org/10.1038%2Fnrd842'] server: [cloudflare] set-cookie: ['__cfduid=dcbb38a7253a023869b367a39d8a2b2ae1531358345; expires=Fri, 12-Jul-19 01:19:05 GMT; path=/; domain=.doi.org; HttpOnly'] vary: [Accept] status: {code: 302, message: ''} - request: body: null headers: Accept: [application/x-bibtex; charset=utf-8] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] User-Agent: [python-requests/2.18.4] method: GET uri: https://data.crossref.org/10.1038%2Fnrd842 response: body: {string: !!python/unicode "@article{Atkins_2002,\n\tdoi = {10.1038/nrd842},\n\turl = {https://doi.org/10.1038%2Fnrd842},\n\tyear = 2002,\n\tmonth = {jul},\n\tpublisher = {Springer Nature},\n\tvolume = {1},\n\tnumber = {7},\n\tpages = {491--492},\n\tauthor = {Joshua H. Atkins and Leland J. Gershell},\n\ttitle = {Selective anticancer drugs},\n\tjournal = {Nature Reviews Drug Discovery}\n}"} headers: access-control-allow-headers: ['X-Requested-With, Accept, Accept-Encoding, Accept-Charset, Accept-Language, Accept-Ranges, Cache-Control'] access-control-allow-origin: ['*'] access-control-expose-headers: [Link] connection: [close] content-length: ['339'] content-type: [application/x-bibtex] date: ['Thu, 12 Jul 2018 01:19:06 GMT'] link: ['; rel="canonical", ; version="vor"; type="application/pdf"; rel="item", ; version="vor"; type="text/html"; rel="item", ; version="vor"; type="application/pdf"; rel="item", ; rel="license"'] server: [http-kit] x-rate-limit-interval: [1s] x-rate-limit-limit: ['50'] status: {code: 200, message: OK} - request: body: null headers: Accept: [application/x-bibtex; charset=utf-8] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] User-Agent: [python-requests/2.18.4] method: GET uri: https://doi.org/fasljfdldaksj response: body: string: !!binary | H4sIAAAAAAAAA6xYW3PbuBV+1684i53uUyjqlrQrk5yRbXnjqW25stw0TxkIOBSxIQEuAEpWZ3/8 DkDSuthx7LR6ES8493M+fGD00/nsbPH5dgqZLXK4vT+9ujwDEoThp+FZGJ4vzuE/HxfXV9Dv9mCh qTTCCiVpHobTG9KB5kcya8txGG42m+5m2FV6FS7m4YPT2XdKmsvA7mnocstJ0oncG/eHlCedyAqb YzLVWukxnM8u4UZZuFCV5FFYv+t0ogItBWcywD8qsY7JmZIWpQ0W2xIJsPouJhYfbOj0nwDLqDZo 4/vFRfAPAqFTkwv5FTTmMRFMSQKZxjQmobHUChaKYhWmdO1edUu58jJ7IiZT2rLKwnOyrZxgioDd lhgTUdAVhg9BvTxMoFF2KGjsNsdQ4ibwV4MuM4Y0Bt0DkyHaVqUPzy9wroV1BjvRUvFt0ul0Ii7W 4KVisqTs60q7NI5/TtlyNPjgUi+KFRjNDmNeUilRB6P+sLsSKQGa25hcqZUisBHcZjH5MOgRyFCs MhuT/vDvBJZKc9Qx6TWucLFODh2ol4/75cPJvi89/yNJI/IKifej94NR+haJ9MOv/T4+SnQ60U9B AIvJ6dUULmZzuJn8+/K3yeJydgOnkzkEgWtDusyxjbff6/3tIEaGeV5SzoVcPd6bkrL2XvCYSLr2 SlwCxUrGhKG0qF3arU785ESWtyaGo11GBx8ILFdM5UrH5OfBsJ8OXIaeLZYfqJJqlHavWo+V6u/V yZUmCi2vbT868KJVYDk1pg4GHyxp/KZN1+5NPVfCj72QHB+6buZI8nF2PY1CmsAvcmnKkz/rv5ek s2UrOrk5P53N/vk28ZQy6yfENGouJmeLu4/T6eLurYr+eNTwL/NU9sUUaDSq0gxbJ+bTu9n9/Gz6 Rh8qg7pVcX83nb9RnEqpKsmw0XAz/fRGBYKnQYHFErU5qOr19Pp0Or+DyXw6cRrrlnCN1fRVFLoG j0Lf/kk9bdOb85cn7jWDjEM6GvGXRn94JDHio19Hg73RP579s9nNYnqzcC7UO9n/Ovq7EUr9r513 N2pMuaUyJt8G3+/Nc28Ped18HgOvG+86/7VZb3fdIJBVJXGPskFytLNmg7YCzcBzsRbcoVXTJ7v8 ZcOk3wtTavLfU55z+tX8HoXZ8LXynahMFpkwfm9nrkktLBFS5wcICTZD/+puaywWXYBbZYxwFdFI jZIGqMZxFJYeyKu83sfbNmgqEyyVtaoYQ/c9FickWTRKhQEhmdIamXXGtqrSUA9rF+6QapZBqrR3 QlgsYLkFSQt8B556vAOlQdkMNTgGwqmlUBkhV0DB1NIoV0JiNwpz8SbPNtQAU6VAvvMw33bhLEP2 FawCgwg2o9b7Zqx2VoVkecXRAM1z/9yRHMosagNLTJVGoJIDTS3WMZmcmsw/k06jY0kMoawksxV1 rAwKqr+at7ufUQN1JVECZVasqUUOW7SugjlSg2D1FuiKCgk5tajfeT80lkrXQZVaLXMsQKT+Fh0F 9FROyAp3PoW+5q/qtqhMPqsKCrrd2RGm0WyVN6PRlErWHeYimeNKGKvrbExWKNm2qbFbnSpdwBJz tekCXNbpr7sICypyoJxrNMYp18hQrNFFkApd1ApdyCkidxjVrZt4H8XaLOeY2jGMfIo7nchbdVlV MiahVNZPC3FNmCkek1IZSwAlq2khLctcMG8wfAg2m03gFASVzlEyxZET39Qxkcr6+b9QuiB+nmrs O4K7R3gbHsHf0INbTTdbkEue03LIgI6VHBt5VNrozKBVoB3wETBMuTi12pAkyukSc4dn4yisr6PQ ZrUvkZBlZZtoC2FcHT9SyR0r21Fo4gCyQoeuh7hGwIj/YkxGA0fBKVcy38akvdrxqWbDa739rrP3 8ytQKXzCJdzSFcKVMLbtsFcEojFF7QDACT8byP/d8c+uxae+xSd1i4+/4ZxjLX5hs+5Z/xpE8O3w zPjsu/9WVw83uwnnzWkTLqWbgmaul6qqQac+aO7F4vykGmkTDlNFgdIa4jfvmAx7BLTaGL+HR2G7 +tDJsJ2JlgJ1nsTg9+WDIJKoPMykQTfjdfZMtSzELn93/rZ2HuYe2upMld93xGVhd0TrPPrmC/kD lGTwEiM58qZ14il0B2mV50/w+5CrXcxmi2nLFmuMeQszOz6G7XjZMTy1NG3goKhlyGGGedlQ4B1B gVutHrZwh3qNGs4Vq1y7+C7z3PgpI9sz6GB+l7xR75us0J2+vyyu688QPv3ns8svXBj2Jd8/mA/2 T5GjPUr4xGrSiVyMz288vfLhBFIlbeDmcAx9R8RJciCxRm0Fo3ngtY7BVCXqE5L8onF1EoVuafLO odmPSXVn899+QNLtr/7DzI8YdswSrKYcPQtyEO0g4tJ1haQNjLjS+03TP+g2op3H848/QhwdgMJ2 CutPXX8BAAD//wMABwukynQTAAA= headers: cf-ray: [438fae01aa93ae20-BOS] connection: [keep-alive] content-encoding: [gzip] content-type: [text/html;charset=utf-8] date: ['Thu, 12 Jul 2018 01:19:07 GMT'] expect-ct: ['max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"'] server: [cloudflare] set-cookie: ['__cfduid=d41616872f8f3089311dd1db1ed02c6a11531358346; expires=Fri, 12-Jul-19 01:19:06 GMT; path=/; domain=.doi.org; HttpOnly'] status: {code: 404, message: ''} - request: body: null headers: Accept: [application/x-bibtex; charset=utf-8] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] User-Agent: [python-requests/2.18.4] method: GET uri: https://doi.org/fasljfdldaksj response: body: string: !!binary | H4sIAAAAAAAAA6xYW3PbuBV+1684i53uUyjqlrQrk5yRbXnjqW25stw0TxkIOBSxIQEuAEpWZ3/8 DkDSuthx7LR6ES8493M+fGD00/nsbPH5dgqZLXK4vT+9ujwDEoThp+FZGJ4vzuE/HxfXV9Dv9mCh qTTCCiVpHobTG9KB5kcya8txGG42m+5m2FV6FS7m4YPT2XdKmsvA7mnocstJ0oncG/eHlCedyAqb YzLVWukxnM8u4UZZuFCV5FFYv+t0ogItBWcywD8qsY7JmZIWpQ0W2xIJsPouJhYfbOj0nwDLqDZo 4/vFRfAPAqFTkwv5FTTmMRFMSQKZxjQmobHUChaKYhWmdO1edUu58jJ7IiZT2rLKwnOyrZxgioDd lhgTUdAVhg9BvTxMoFF2KGjsNsdQ4ibwV4MuM4Y0Bt0DkyHaVqUPzy9wroV1BjvRUvFt0ul0Ii7W 4KVisqTs60q7NI5/TtlyNPjgUi+KFRjNDmNeUilRB6P+sLsSKQGa25hcqZUisBHcZjH5MOgRyFCs MhuT/vDvBJZKc9Qx6TWucLFODh2ol4/75cPJvi89/yNJI/IKifej94NR+haJ9MOv/T4+SnQ60U9B AIvJ6dUULmZzuJn8+/K3yeJydgOnkzkEgWtDusyxjbff6/3tIEaGeV5SzoVcPd6bkrL2XvCYSLr2 SlwCxUrGhKG0qF3arU785ESWtyaGo11GBx8ILFdM5UrH5OfBsJ8OXIaeLZYfqJJqlHavWo+V6u/V yZUmCi2vbT868KJVYDk1pg4GHyxp/KZN1+5NPVfCj72QHB+6buZI8nF2PY1CmsAvcmnKkz/rv5ek s2UrOrk5P53N/vk28ZQy6yfENGouJmeLu4/T6eLurYr+eNTwL/NU9sUUaDSq0gxbJ+bTu9n9/Gz6 Rh8qg7pVcX83nb9RnEqpKsmw0XAz/fRGBYKnQYHFErU5qOr19Pp0Or+DyXw6cRrrlnCN1fRVFLoG j0Lf/kk9bdOb85cn7jWDjEM6GvGXRn94JDHio19Hg73RP579s9nNYnqzcC7UO9n/Ovq7EUr9r513 N2pMuaUyJt8G3+/Nc28Ped18HgOvG+86/7VZb3fdIJBVJXGPskFytLNmg7YCzcBzsRbcoVXTJ7v8 ZcOk3wtTavLfU55z+tX8HoXZ8LXynahMFpkwfm9nrkktLBFS5wcICTZD/+puaywWXYBbZYxwFdFI jZIGqMZxFJYeyKu83sfbNmgqEyyVtaoYQ/c9FickWTRKhQEhmdIamXXGtqrSUA9rF+6QapZBqrR3 QlgsYLkFSQt8B556vAOlQdkMNTgGwqmlUBkhV0DB1NIoV0JiNwpz8SbPNtQAU6VAvvMw33bhLEP2 FawCgwg2o9b7Zqx2VoVkecXRAM1z/9yRHMosagNLTJVGoJIDTS3WMZmcmsw/k06jY0kMoawksxV1 rAwKqr+at7ufUQN1JVECZVasqUUOW7SugjlSg2D1FuiKCgk5tajfeT80lkrXQZVaLXMsQKT+Fh0F 9FROyAp3PoW+5q/qtqhMPqsKCrrd2RGm0WyVN6PRlErWHeYimeNKGKvrbExWKNm2qbFbnSpdwBJz tekCXNbpr7sICypyoJxrNMYp18hQrNFFkApd1ApdyCkidxjVrZt4H8XaLOeY2jGMfIo7nchbdVlV MiahVNZPC3FNmCkek1IZSwAlq2khLctcMG8wfAg2m03gFASVzlEyxZET39Qxkcr6+b9QuiB+nmrs O4K7R3gbHsHf0INbTTdbkEue03LIgI6VHBt5VNrozKBVoB3wETBMuTi12pAkyukSc4dn4yisr6PQ ZrUvkZBlZZtoC2FcHT9SyR0r21Fo4gCyQoeuh7hGwIj/YkxGA0fBKVcy38akvdrxqWbDa739rrP3 8ytQKXzCJdzSFcKVMLbtsFcEojFF7QDACT8byP/d8c+uxae+xSd1i4+/4ZxjLX5hs+5Z/xpE8O3w zPjsu/9WVw83uwnnzWkTLqWbgmaul6qqQac+aO7F4vykGmkTDlNFgdIa4jfvmAx7BLTaGL+HR2G7 +tDJsJ2JlgJ1nsTg9+WDIJKoPMykQTfjdfZMtSzELn93/rZ2HuYe2upMld93xGVhd0TrPPrmC/kD lGTwEiM58qZ14il0B2mV50/w+5CrXcxmi2nLFmuMeQszOz6G7XjZMTy1NG3goKhlyGGGedlQ4B1B gVutHrZwh3qNGs4Vq1y7+C7z3PgpI9sz6GB+l7xR75us0J2+vyyu688QPv3ns8svXBj2Jd8/mA/2 T5GjPUr4xGrSiVyMz288vfLhBFIlbeDmcAx9R8RJciCxRm0Fo3ngtY7BVCXqE5L8onF1EoVuafLO odmPSXVn899+QNLtr/7DzI8YdswSrKYcPQtyEO0g4tJ1haQNjLjS+03TP+g2op3H848/QhwdgMJ2 CutPXX8BAAD//wMABwukynQTAAA= headers: cf-ray: [438fae063d57ae38-BOS] connection: [keep-alive] content-encoding: [gzip] content-type: [text/html;charset=utf-8] date: ['Thu, 12 Jul 2018 01:19:07 GMT'] expect-ct: ['max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"'] server: [cloudflare] set-cookie: ['__cfduid=d65b995bd178e3cd707285c55f233c7621531358347; expires=Fri, 12-Jul-19 01:19:07 GMT; path=/; domain=.doi.org; HttpOnly'] status: {code: 404, message: ''} - request: body: null headers: Accept: [application/x-bibtex; charset=utf-8] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] User-Agent: [python-requests/2.18.4] method: GET uri: https://doi.org/10.5281/zenodo.50186 response: body: {string: !!python/unicode 'Handle Redirect https://data.datacite.org/10.5281%2Fzenodo.50186'} headers: cf-ray: [438fae0948585a68-BOS] connection: [keep-alive] content-length: ['181'] content-type: [text/html;charset=utf-8] date: ['Thu, 12 Jul 2018 01:19:07 GMT'] expect-ct: ['max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"'] expires: ['Thu, 12 Jul 2018 01:49:59 GMT'] location: ['https://data.datacite.org/10.5281%2Fzenodo.50186'] server: [cloudflare] set-cookie: ['__cfduid=d98c43f90ddddadde258529e39bcd39b91531358347; expires=Fri, 12-Jul-19 01:19:07 GMT; path=/; domain=.doi.org; HttpOnly'] vary: [Accept] status: {code: 302, message: ''} - request: body: null headers: Accept: [application/x-bibtex; charset=utf-8] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] User-Agent: [python-requests/2.18.4] method: GET uri: https://data.datacite.org/10.5281%2Fzenodo.50186 response: body: string: !!binary | H4sIAI6sRlsAA2xVwW7bOBC95yv4AYpsB2hRFChQJ3GU2I5jxMV2N7exNJYmokiFpOzYRb6mp/7A nvaWH9shJWXj3b0E1gw58+a9x8nXimz6o3Cutp8Hg0xTrE0+GA3jD2efRoMDKp3p+MNw9OljdCIE 58UX8eP/0i8+3xjp8325Lu8rGky1yQb/HIXGFdr40wnntcSdLSkSM3PY24PTGzGNBahMTKzDNahI 3NkUTAidN0bZSFwUhqzTdYFt+IEwl2giMTFUhsiSlAWTReIcLEkMsVvIyGqud0Eqwzb2HWypq0jc UloAyravlMRNLmFLmUggb09eSDBliIbPS9h7aO/v3YIqXn+iPV3xTR2JscRnjpv2/kI75yH2N5YF SarrkJvqQgVk16BsWx53uGf0EkoUkxC6BpkWqEqu/AcYbSUjuYu7VAUSSKEfDozUITpDW8DWoxwr 4p/HYyjqYF83yqFaNyaPxLSRBOK2r6pKPB5xwb+1ZHLOYx5IgupJtGgqUNzqEivqwhPumRes7Njn OopMSbij9PBfEX/joZ4aaJ4jJr3rN065h1cR1SNU1Ba50maNjGGK1lLajnWvS6x8p36qmVY8z++M JT99YNpCMDFQbbRx/9ZmJhEtYelrdgKs/NAbL1h3tEO5orKESKwKMJC1VkODh9OkwS2HIj+iY6BX yOq21RMJLnjxmx+4Y+LmaQ2yrUNPITKnhr/piVpOkdXuTRga88k6EktoZEe6UR7eVNuiaUmYgXWV 1/vtGcyhqlnCOWqWu0dbkn9Dc1YQXNHBueBpMp0yA/cxW6hCo9vE7PUv50Kje1Svv9pXyCQTbp3V W0ZY7Z1pDZcg2fAM37krQVOE4VcO677bnLxYVwbUgWzZ2C7IdjM2e/3TBN6noLRNi943OVsT7Vvt VevRscrW6J27YnvwCmGCe/utHGQBzZQr5l0IUeKx9fzba6cymkUmz8F34hUAVcuM1rWvkrASnf+k NhheFftn170h8Hab02NHc6LVASSyzW+2vffTBfOKR+wsoYZM17qR2oo7s/Ej8wGqyBnq9JIyaPqG k11US3TuGGZSsA88Dc7AI7mwaEvc73j1Wr9qFTZGUwU5eaAcLjdS7yJRs0slb45wwZHjXcmnF1Tv a/wsxjwsPtPakzanvHA79H/96GLyzGvD+pxYvCvO4zkQS6NT/zz5m2Wu0DcUN0os97z7VSyG8egs Hp7ep6PQuG7WvHO9HNz8IfzvCPE9QgidDUcfX05eTv4GAAD//wMA46P4VLQGAAA= headers: accept: [application/x-bibtex] cache-control: [private] connection: [keep-alive] content-disposition: [attachment; filename=_10.5281_zenodo.50186.bib] content-encoding: [gzip] content-transfer-encoding: [binary] content-type: [application/x-bibtex] date: ['Thu, 12 Jul 2018 01:19:10 GMT'] etag: [W/"a60a721479c694d11c63cfa61bc95c18"] server: [nginx/1.14.0 + Phusion Passenger 5.3.3] status: [200 OK] vary: ['Accept-Encoding, Origin'] x-powered-by: [Phusion Passenger 5.3.3] x-request-id: [83620554-e6e4-4b99-82ba-e8d9c255bdab] x-runtime: ['1.929840'] status: {code: 200, message: OK} version: 1 duecredit-0.8.0/duecredit/tests/test_injections.py000066400000000000000000000314431362001701600223600ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import gc import sys import pytest from six import viewvalues, PY2 if PY2: import __builtin__ else: import builtins as __builtin__ _orig__import__ = __builtin__.__import__ from duecredit.collector import DueCreditCollector from duecredit.entries import Doi import duecredit.tests.mod as mod from ..injections.injector import DueCreditInjector, find_object, get_modules_for_injection from .. import __version__ try: import mvpa2 _have_mvpa2 = True except ImportError: _have_mvpa2 = False from logging import getLogger lgr = getLogger('duecredit.tests.injector') class TestActiveInjector(object): def setup(self): lgr.log(5, "Setting up for a TestActiveInjector test") self._cleanup_modules() self.due = DueCreditCollector() self.injector = DueCreditInjector(collector=self.due) self.injector.activate(retrospect=False) # numpy might be already loaded... def teardown(self): lgr.log(5, "Tearing down after a TestActiveInjector test") # gc might not pick up inj after some tests complete # so we will always deactivate explicitly self.injector.deactivate() assert __builtin__.__import__ is _orig__import__ self._cleanup_modules() def _cleanup_modules(self): if 'duecredit.tests.mod' in sys.modules: sys.modules.pop('duecredit.tests.mod') def _test_simple_injection(self, func, import_stmt, func_call=None): assert 'duecredit.tests.mod' not in sys.modules self.injector.add('duecredit.tests.mod', func, Doi('1.2.3.4'), description="Testing %s" % func, min_version='0.1', max_version='1.0', tags=["implementation", "very custom"]) assert 'duecredit.tests.mod' not in sys.modules # no import happening assert len(self.due._entries) == 0 assert len(self.due.citations) == 0 globals_, locals_ = {}, {} exec(import_stmt, globals_, locals_) assert len(self.due._entries) == 1 # we should get an entry now assert len(self.due.citations) == 0 # but not yet a citation import duecredit.tests.mod as mod _, _, obj = find_object(mod, func) assert obj.__duecredited__ # we wrapped assert obj.__duecredited__ is not obj # and it is not pointing to the same func assert obj.__doc__ == "custom docstring" # we preserved docstring # TODO: test decoration features -- preserver __doc__ etc exec('ret = %s(None, "somevalue")' % (func_call or func), globals_, locals_) # TODO: awkwardly 'ret' is not found in the scope while running pytest # under python3.4, although present in locals()... WTF? assert locals_['ret'] == "%s: None, somevalue" % func assert len(self.due._entries) == 1 assert len(self.due.citations) == 1 # TODO: there must be a cleaner way to get first value citation = list(viewvalues(self.due.citations))[0] # TODO: ATM we don't allow versioning of the submodules -- we should # assert_equal(citation.version, '0.5') # ATM it will be the duecredit's version assert citation.version == __version__ assert(citation.tags == ['implementation', 'very custom']) def _test_double_injection(self, func, import_stmt, func_call=None): assert 'duecredit.tests.mod' not in sys.modules # add one injection self.injector.add('duecredit.tests.mod', func, Doi('1.2.3.4'), description="Testing %s" % func, min_version='0.1', max_version='1.0', tags=["implementation", "very custom"]) # add another one self.injector.add('duecredit.tests.mod', func, Doi('1.2.3.5'), description="Testing %s" % func, min_version='0.1', max_version='1.0', tags=["implementation", "very custom"]) assert 'duecredit.tests.mod' not in sys.modules # no import happening assert len(self.due._entries) == 0 assert len(self.due.citations) == 0 globals_, locals_ = {}, {} exec(import_stmt, globals_, locals_) assert len(self.due._entries) == 2 # we should get two entries now assert len(self.due.citations) == 0 # but not yet a citation import duecredit.tests.mod as mod _, _, obj = find_object(mod, func) assert obj.__duecredited__ # we wrapped assert obj.__duecredited__ is not obj # and it is not pointing to the same func assert obj.__doc__ == "custom docstring" # we preserved docstring # TODO: test decoration features -- preserver __doc__ etc exec('ret = %s(None, "somevalue")' % (func_call or func), globals_, locals_) # TODO: awkwardly 'ret' is not found in the scope while running pytest # under python3.4, although present in locals()... WTF? assert locals_['ret'] == "%s: None, somevalue" % func assert len(self.due._entries) == 2 assert len(self.due.citations) == 2 # TODO: there must be a cleaner way to get first value citation = list(viewvalues(self.due.citations))[0] # TODO: ATM we don't allow versioning of the submodules -- we should # assert_equal(citation.version, '0.5') # ATM it will be the duecredit's version assert citation.version, __version__ assert (citation.tags == ['implementation', 'very custom']) test1 = ('testfunc1', 'from duecredit.tests.mod import testfunc1', None) test2 = ("TestClass1.testmeth1", 'from duecredit.tests.mod import TestClass1; c = TestClass1()', 'c.testmeth1') test3 = ("TestClass12.Embed.testmeth1", 'from duecredit.tests.mod import TestClass12; c = TestClass12.Embed()', 'c.testmeth1') @pytest.mark.parametrize("func, import_stmt, func_call", [test1, test2, test3]) def test_simple_injection(self, func, import_stmt, func_call): self._test_simple_injection(func, import_stmt, func_call) @pytest.mark.parametrize("func, import_stmt, func_call", [test1, test2, test3]) def test_double_injection(self, func, import_stmt, func_call): self._test_double_injection(func, import_stmt, func_call) def test_delayed_entries(self): # verify that addition of delayed injections happened modules_for_injection = get_modules_for_injection() assert len(self.injector._delayed_injections) == len(modules_for_injection) assert self.injector._entry_records == {} # but no entries were added assert 'scipy' in self.injector._delayed_injections # We must have it ATM try: # We do have injections for scipy import scipy except ImportError as e: pytest.skip("scipy was not found: %s" % (e,)) def test_import_mvpa2_suite(self): if not _have_mvpa2: pytest.skip("no mvpa2 found") # just a smoke test for now import mvpa2.suite as mv def _test_incorrect_path(self, mod, obj): ref = Doi('1.2.3.4') # none of them should lead to a failure self.injector.add(mod, obj, ref) # now cause the import handling -- it must not fail # TODO: catch/analyze warnings exec('from duecredit.tests.mod import testfunc1', {}, {}) @pytest.mark.parametrize("mod, obj", [("nonexistingmodule", None), ("duecredit.tests.mod.nonexistingmodule", None), ("duecredit.tests.mod", "nonexisting"), ("duecredit.tests.mod", "nonexisting.whocares")]) def test_incorrect_path(self, mod, obj): self._test_incorrect_path(mod, obj) def test_find_iobject(): assert find_object(mod, 'testfunc1') == (mod, 'testfunc1', mod.testfunc1) assert find_object(mod, 'TestClass1') == (mod, 'TestClass1', mod.TestClass1) assert find_object(mod, 'TestClass1.testmeth1') == (mod.TestClass1, 'testmeth1', mod.TestClass1.testmeth1) assert find_object(mod, 'TestClass12.Embed.testmeth1') == (mod.TestClass12.Embed, 'testmeth1', mod.TestClass12.Embed.testmeth1) def test_no_double_activation(): orig__import__ = __builtin__.__import__ try: due = DueCreditCollector() injector = DueCreditInjector(collector=due) injector.activate() assert __builtin__.__import__ is not orig__import__ duecredited__import__ = __builtin__.__import__ # TODO: catch/analyze/swallow warning injector.activate() assert __builtin__.__import__ is duecredited__import__ # we didn't decorate again finally: injector.deactivate() __builtin__.__import__ = orig__import__ def test_get_modules_for_injection(): # output order is sorted by name (not that it matters for functionality) assert get_modules_for_injection() == ['mod_biosig', 'mod_dipy', 'mod_matplotlib', 'mod_mdp', 'mod_mne', 'mod_nibabel', 'mod_nipy', 'mod_nipype', 'mod_numpy', 'mod_pandas', 'mod_psychopy', 'mod_scipy', 'mod_skimage', 'mod_sklearn', ] def test_cover_our_injections(): # this one tests only import/syntax/api for the injections due = DueCreditCollector() inj = DueCreditInjector(collector=due) for modname in get_modules_for_injection(): mod = __import__('duecredit.injections.' + modname, fromlist=["duecredit.injections"]) mod.inject(inj) def test_no_harm_from_deactivate(): # if we have not activated one -- shouldn't blow if we deactivate it # TODO: catch warning being spitted out DueCreditInjector().deactivate() def test_injector_del(): orig__import__ = __builtin__.__import__ try: due = DueCreditCollector() inj = DueCreditInjector(collector=due) del inj # delete inactive assert __builtin__.__import__ is orig__import__ inj = DueCreditInjector(collector=due) inj.activate(retrospect=False) assert __builtin__.__import__ is not orig__import__ assert inj._orig_import is not None del inj # delete active but not used inj = None __builtin__.__import__ = None # We need to do that since otherwise gc will not pick up inj gc.collect() # To cause __del__ assert __builtin__.__import__ is orig__import__ import abc # and new imports work just fine finally: __builtin__.__import__ = orig__import__ def test_injector_delayed_del(): # interesting case -- if we still have an instance of injector hanging around # and then create a new one, activate it but then finally delete/gc old one # it would (currently) reset import back (because atm defined as class var) # which would ruin operation of the new injector orig__import__ = __builtin__.__import__ try: due = DueCreditCollector() inj = DueCreditInjector(collector=due) inj.activate(retrospect=False) assert __builtin__.__import__ is not orig__import__ assert inj._orig_import is not None inj.deactivate() assert __builtin__.__import__ is orig__import__ assert inj._orig_import is None # create 2nd one inj2 = DueCreditInjector(collector=due) inj2.activate(retrospect=False) assert __builtin__.__import__ is not orig__import__ assert inj2._orig_import is not None del inj inj = None gc.collect() # To cause __del__ assert __builtin__.__import__ is not orig__import__ # would fail if del had side-effect assert inj2._orig_import is not None inj2.deactivate() assert __builtin__.__import__ is orig__import__ import abc # and new imports work just fine finally: __builtin__.__import__ = orig__import__ duecredit-0.8.0/duecredit/tests/test_io.py000066400000000000000000000516011362001701600206200ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil; coding: utf-8 -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import random import re import pickle import os import pytest from six.moves import StringIO from six import text_type import duecredit.io from ..collector import DueCreditCollector, Citation from .test_collector import _sample_bibtex, _sample_doi, _sample_bibtex2 from ..entries import BibTeX, Doi, Text, Url from ..io import TextOutput, PickleOutput, import_doi, \ get_text_rendering, format_bibtex, _is_contained, Output, BibTeXOutput try: import vcr @vcr.use_cassette() def test_import_doi(): doi_good = '10.1038/nrd842' kw = dict(sleep=0.00001, retries=2) assert isinstance(import_doi(doi_good, **kw), text_type) doi_bad = 'fasljfdldaksj' with pytest.raises(ValueError): import_doi(doi_bad, **kw) doi_zenodo = '10.5281/zenodo.50186' assert isinstance(import_doi(doi_zenodo, **kw), text_type) except ImportError: # no vcr, and that is in 2015! pass def test_pickleoutput(tmpdir): #entry = BibTeX('@article{XXX0, ...}') entry = BibTeX("@article{Atkins_2002,\n" "title=title,\n" "volume=1, \n" "url=https://doi.org/10.1038/nrd842, \n" "DOI=10.1038/nrd842, \n" "number=7, \n" "journal={Nat. Rev. Drug Disc.}, \n" "publisher={Nature Publishing Group}, \n" "author={Atkins, Joshua H. and Gershell, Leland J.}, \n" "year={2002}, \n" "month={Jul}, \n" "pages={491--492}\n}") collector_ = DueCreditCollector() collector_.add(entry) collector_.cite(entry, path='module') # test it doesn't puke with an empty collector collectors = [collector_, DueCreditCollector()] tempfile = str(tmpdir.mkdir("sub").join("tempfile.txt")) for collector in collectors: pickler = PickleOutput(collector, fn=tempfile) assert pickler.fn == tempfile assert pickler.dump() is None with open(tempfile, 'rb') as f: collector_loaded = pickle.load(f) assert collector.citations.keys() == collector_loaded.citations.keys() # TODO: implement comparison of citations assert collector._entries.keys() == collector_loaded._entries.keys() os.unlink(tempfile) def test_output(): entry = BibTeX(_sample_bibtex) entry2 = BibTeX(_sample_bibtex2) # normal use collector = DueCreditCollector() collector.cite(entry, path='package') collector.cite(entry, path='package.module') output = Output(None, collector) packages, modules, objects = output._get_collated_citations(tags=['*']) assert len(packages) == 1 assert len(modules) == 1 assert len(objects) == 0 assert packages['package'][0] == collector.citations[('package', entry.get_key())] assert modules['package.module'][0] == collector.citations[('package.module', entry.get_key())] # no toppackage collector = DueCreditCollector() collector.cite(entry, path='package') collector.cite(entry, path='package2.module') output = Output(None, collector) packages, modules, objects = output._get_collated_citations(tags=['*']) assert len(packages) == 0 assert len(modules) == 1 assert len(objects) == 0 assert modules['package2.module'][0] == collector.citations[('package2.module', entry.get_key())] # toppackage because required collector = DueCreditCollector() collector.cite(entry, path='package', cite_module=True) collector.cite(entry, path='package2.module') output = Output(None, collector) packages, modules, objects = output._get_collated_citations(tags=['*']) assert len(packages) == 1 assert len(modules) == 1 assert len(objects) == 0 assert packages['package'][0] == collector.citations[('package', entry.get_key())] assert modules['package2.module'][0] == collector.citations[('package2.module', entry.get_key())] # check it returns multiple entries collector = DueCreditCollector() collector.cite(entry, path='package') collector.cite(entry2, path='package') collector.cite(entry, path='package.module') output = Output(None, collector) packages, modules, objects = output._get_collated_citations(tags=['*']) assert len(packages) == 1 assert len(packages['package']) == 2 assert len(modules) == 1 assert len(objects) == 0 # sort them in order so we know who is who # entry2 key is Atk... # entry key is XX.. packs = sorted(packages['package'], key=lambda x: x.entry.key) assert packs[0] == collector.citations[('package', entry2.get_key())] assert packs[1] == collector.citations[('package', entry.get_key())] assert modules['package.module'][0] == collector.citations[('package.module', entry.get_key())] # check that filtering works collector = DueCreditCollector() collector.cite(entry, path='package', tags=['edu']) collector.cite(entry2, path='package') collector.cite(entry, path='package.module', tags=['edu']) output = Output(None, collector) packages, modules, objects = output._get_collated_citations(tags=['edu']) assert len(packages) == 1 assert len(packages['package']) == 1 assert len(modules) == 1 assert len(objects) == 0 assert packages['package'][0] == collector.citations[('package', entry.get_key())] assert modules['package.module'][0] == collector.citations[('package.module', entry.get_key())] def test_output_return_all(monkeypatch): entry = BibTeX(_sample_bibtex) entry2 = BibTeX(_sample_bibtex2) # normal use collector = DueCreditCollector() collector.cite(entry, path='package') collector.cite(entry2, path='package2') output = Output(None, collector) packages, modules, objects = output._get_collated_citations(tags=['*']) assert not packages assert not modules assert not objects for flag in ['1', 'True', 'TRUE', 'true', 'on', 'yes']: monkeypatch.setitem(os.environ, 'DUECREDIT_REPORT_ALL', flag) # if _all is None then get the environment packages, modules, objects = output._get_collated_citations(tags=['*']) assert len(packages) == 2 assert not modules assert not objects # however if _all is set it shouldn't work packages, modules, objects = output._get_collated_citations(tags=['*'], all_=False) assert not packages assert not modules assert not objects def test_output_tags(monkeypatch): entry = BibTeX(_sample_bibtex) entry2 = BibTeX(_sample_bibtex2) # normal use collector = DueCreditCollector() collector.cite(entry, path='package', cite_module=True, tags=['edu']) collector.cite(entry2, path='package.module', tags=['wip']) output = Output(None, collector) packages, modules, objects = output._get_collated_citations(tags=['*']) assert len(packages) == 1 assert len(modules) == 1 assert not objects packages, modules, objects = output._get_collated_citations() assert not packages assert not modules assert not objects for tags in ['edu', 'wip', 'edu,wip']: monkeypatch.setitem(os.environ, 'DUECREDIT_REPORT_TAGS', tags) # if tags is None then get the environment packages, modules, objects = output._get_collated_citations() assert len(packages) == (1 if 'edu' in tags else 0) assert len(modules) == (1 if 'wip' in tags else 0) assert not objects # however if tags is set it shouldn't work packages, modules, objects = output._get_collated_citations(tags=['implementation']) assert not packages assert not modules assert not objects def test_text_output(): entry = BibTeX(_sample_bibtex) entry2 = BibTeX(_sample_bibtex2) # in this case, since we're not citing any module or method, we shouldn't # output anything collector = DueCreditCollector() collector.cite(entry, path='package') strio = StringIO() TextOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() assert "0 packages cited" in value, "value was %s" % value assert "0 modules cited" in value, "value was %s" % value assert "0 functions cited" in value, "value was %s" % value # but it should be cited if cite_module=True collector = DueCreditCollector() collector.cite(entry, path='package', cite_module=True) strio = StringIO() TextOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() assert "1 package cited" in value, "value was %s" % value assert "0 modules cited" in value, "value was %s" % value assert "0 functions cited" in value, "value was %s" % value # in this case, we should be citing the package since we are also citing a # submodule collector = DueCreditCollector() collector.cite(entry, path='package') collector.cite(entry, path='package.module') strio = StringIO() TextOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() assert "1 package cited" in value, "value was %s" % value assert "1 module cited" in value, "value was %s" % value assert "0 functions cited" in value, "value was %s" % value assert "Halchenko, Y.O." in value, "value was %s" % value assert value.strip().endswith("Frontiers in Neuroinformatics, 6(22).") # in this case, we should be citing the package since we are also citing a # submodule collector = DueCreditCollector() collector.cite(entry, path='package') collector.cite(entry2, path='package') collector.cite(entry, path='package.module') strio = StringIO() TextOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() assert "1 package cited" in value, "value was %s" % value assert "1 module cited" in value, "value was %s" % value assert "0 functions cited" in value, "value was %s" % value assert "Halchenko, Y.O." in value, "value was %s" % value assert '[1, 2]' in value, "value was %s" %value assert '[3]' not in value, "value was %s" %value def test_text_output_dump_formatting(): due = DueCreditCollector() # XXX: atm just to see if it spits out stuff @due.dcite(BibTeX(_sample_bibtex), description='solution to life', path='mymodule', version='0.0.16') def mymodule(arg1, kwarg2="blah"): """docstring""" assert arg1 == "magical" assert kwarg2 == 1 @due.dcite(BibTeX(_sample_bibtex2), description='solution to life', path='mymodule:myfunction') def myfunction(arg42): pass myfunction('argh') return "load" # check we don't have anything output strio = StringIO() TextOutput(strio, due).dump(tags=['*']) value = strio.getvalue() assert '0 modules cited' in value, 'value was {0}'.format(value) assert '0 functions cited' in value, 'value was {0}'.format(value) # now we call it -- check it prints stuff strio = StringIO() mymodule('magical', kwarg2=1) TextOutput(strio, due).dump(tags=['*']) value = strio.getvalue() assert '1 package cited' in value, 'value was {0}'.format(value) assert '1 function cited' in value, 'value was {0}'.format(value) assert '(v 0.0.16)' in value, 'value was {0}'.format(value) assert len(value.split('\n')) == 16, 'value was {0}'.format(len(value.split('\n'))) # test we get the reference numbering right samples_bibtex = [_generate_sample_bibtex() for x in range(6)] # this sucks but at the moment it's the only way to have multiple # references for a function @due.dcite(BibTeX(samples_bibtex[0]), description='another solution', path='myothermodule', version='0.0.666') def myothermodule(arg1, kwarg2="blah"): """docstring""" assert arg1 == "magical" assert kwarg2 == 1 @due.dcite(BibTeX(samples_bibtex[1]), description='solution to life', path='myothermodule:myotherfunction') @due.dcite(BibTeX(samples_bibtex[2]), description='solution to life', path='myothermodule:myotherfunction') @due.dcite(BibTeX(samples_bibtex[3]), description='solution to life', path='myothermodule:myotherfunction') @due.dcite(BibTeX(samples_bibtex[4]), description='solution to life', path='myothermodule:myotherfunction') @due.dcite(BibTeX(samples_bibtex[5]), description='solution to life', path='myothermodule:myotherfunction') def myotherfunction(arg42): pass myotherfunction('argh') return "load" myothermodule('magical', kwarg2=1) strio = StringIO() TextOutput(strio, due).dump(tags=['*']) value = strio.getvalue() lines = value.split('\n') citation_numbers = [] reference_numbers = [] references = [] for line in lines: match_citation = re.search('\[([0-9, ]+)\]$', line) match_reference = re.search('^\[([0-9])\]', line) if match_citation: citation_numbers.extend(match_citation.group(1).split(', ')) elif match_reference: reference_numbers.append(match_reference.group(1)) references.append(line.replace(match_reference.group(), "")) assert set(citation_numbers) == set(reference_numbers) assert len(set(references)) == len(set(citation_numbers)) assert len(citation_numbers) == 8 # verify that we have returned to previous state of filters import warnings assert ('ignore', None, UserWarning, None, 0) not in warnings.filters def test_bibtex_output(): entry = BibTeX(_sample_bibtex) entry2 = BibTeX(_sample_bibtex2) # in this case, since we're not citing any module or method, we shouldn't # output anything collector = DueCreditCollector() collector.cite(entry, path='package') strio = StringIO() BibTeXOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() assert value == '', 'Value was {0}'.format(value) # impose citing collector = DueCreditCollector() collector.cite(entry, path='package', cite_module=True) strio = StringIO() BibTeXOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() assert value.strip() == _sample_bibtex.strip(), 'Value was {0}'.format(value) # impose filtering collector = DueCreditCollector() collector.cite(entry, path='package', cite_module=True, tags=['edu']) collector.cite(entry2, path='package.module') strio = StringIO() BibTeXOutput(strio, collector).dump(tags=['edu']) value = strio.getvalue() assert value.strip() == _sample_bibtex.strip(), 'Value was {0}'.format(value) # no filtering strio = StringIO() BibTeXOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() assert value.strip() == _sample_bibtex.strip() + _sample_bibtex2.rstrip(), 'Value was {0}'.format(value) # check the we output only unique bibtex entries collector.cite(entry2, path='package') strio = StringIO() BibTeXOutput(strio, collector).dump(tags=['*']) value = strio.getvalue() value_ = sorted(value.strip().split('\n')) bibtex = sorted((_sample_bibtex.strip() + _sample_bibtex2.rstrip()).split('\n')) assert value_ == bibtex, 'Value was {0}'.format(value_, bibtex) # assert_equal(value_, bibtex, # msg='Value was {0}'.format(value_, bibtex)) def _generate_sample_bibtex(): """ Generate a random sample bibtex to test multiple references """ letters = 'abcdefghilmnopqrstuvxz' numbers = '0123456789' letters_numbers = letters + letters.upper() + numbers letters_numbers_spaces = letters_numbers + ' ' key = "".join(random.sample(letters_numbers, 7)) title = "".join(random.sample(letters_numbers_spaces, 20)) journal = "".join(random.sample(letters_numbers_spaces, 20)) publisher = "".join(random.sample(letters_numbers_spaces, 10)) author = "".join(random.sample(letters, 6)) + ', ' + \ "".join(random.sample(letters, 4)) year = "".join(random.sample(numbers, 4)) elements = [('title', title), ('journal', journal), ('publisher', publisher), ('author', author), ('year', year)] sample_bibtex = "@ARTICLE{%s,\n" % key for string, value in elements: sample_bibtex += "%s={%s},\n" % (string, value) sample_bibtex += "}" return sample_bibtex def test_get_text_rendering(monkeypatch): # Patch bibtex_rendering sample_bibtex = BibTeX(_sample_bibtex) def get_bibtex_rendering(*args, **kwargs): return sample_bibtex monkeypatch.setattr(duecredit.io, 'get_bibtex_rendering', get_bibtex_rendering) # Patch format_bibtex fmt_args = {} def format_bibtex(entry, style): fmt_args["entry"] = entry fmt_args["style"] = style monkeypatch.setattr(duecredit.io, 'format_bibtex', format_bibtex) # test if bibtex type is passed citation_bibtex = Citation(sample_bibtex, path='mypath') bibtex_output = get_text_rendering(citation_bibtex) assert fmt_args["entry"] == citation_bibtex.entry assert fmt_args["style"] == 'harvard1' fmt_args.clear() # test if doi type is passed citation_doi = Citation(Doi(_sample_doi), path='mypath') doi_output = get_text_rendering(citation_doi) assert fmt_args["entry"] == citation_bibtex.entry assert fmt_args["style"] == 'harvard1' assert bibtex_output == doi_output def test_text_text_rendering(): text = "I am so free" citation = Citation(Text(text), path='mypath') assert get_text_rendering(citation) == text def test_url_text_rendering(): url = "http://example.com" citation = Citation(Url(url), path='mypath') assert get_text_rendering(citation) == "URL: " + url def test_format_bibtex_zenodo_doi(): """ test that we can correctly parse bibtex entries obtained from a zenodo doi """ # this was fetched on 2016-05-10 bibtex_zenodo = """ @data{0b1284ba-5ce5-4367-84f3-c44b4962ad90, doi = {10.5281/zenodo.50186}, url = {https://doi.org/10.5281/zenodo.50186}, author = {Satrajit Ghosh; Chris Filo Gorgolewski; Oscar Esteban; Erik Ziegler; David Ellis; cindeem; Michael Waskom; Dav Clark; Michael; Fred Loney; Alexandre M. S.; Michael Notter; Hans Johnson; Anisha Keshavan; Yaroslav Halchenko; Carlo Hamalainen; Blake Dewey; Ben Cipollini; Daniel Clark; Julia Huntenburg; Drew Erickson; Michael Hanke; moloney; Jason W; Demian Wassermann; cdla; Nolan Nichols; Chris Markiewicz; Jarrod Millman; Arman Eshaghi; }, publisher = {Zenodo}, title = {nipype: Release candidate 1 for version 0.12.0}, year = {2016} } """ assert (format_bibtex(BibTeX(bibtex_zenodo)) == """Ghosh, S. et al., 2016. nipype: Release candidate 1 for version 0.12.0.""") def test_format_bibtex_with_utf_characters(): """ test that we can correctly parse bibtex entry if it contains utf-8 characters """ # this was fetched on 2017-08-16 # replaced Brett with Brótt to have utf-8 characters in first author's name as well bibtex_utf8 = u"@misc{https://doi.org/10.5281/zenodo.60847,\n doi = {10.5281/zenodo.60847},\n url = {" \ u"http://zenodo.org/record/60847},\n author = {Brótt, Matthew and Hanke, Michael and Cipollini, " \ u"Ben and {Marc-Alexandre Côté} and Markiewicz, Chris and Gerhard, Stephan and Larson, " \ u"Eric and Lee, Gregory R. and Halchenko, Yaroslav and Kastman, Erik and {Cindeem} and Morency, " \ u"Félix C. and {Moloney} and Millman, Jarrod and Rokem, Ariel and {Jaeilepp} and Gramfort, " \ u"Alexandre and Bosch, Jasper J.F. Van Den and {Krish Subramaniam} and Nichols, Nolan and {Embaker} " \ u"and {Bpinsard} and {Chaselgrove} and Oosterhof, Nikolaas N. and St-Jean, Samuel and {Bago " \ u"Amirbekian} and Nimmo-Smith, Ian and {Satrajit Ghosh}},\n keywords = {},\n title = {nibabel " \ u"2.0.1},\n publisher = {Zenodo},\n year = {2015}\n} " assert (format_bibtex(BibTeX(bibtex_utf8)) == u'Brótt, M. et al., 2015. nibabel 2.0.1.') def test_is_contained(): toppath = 'package' assert _is_contained(toppath, 'package.module') assert _is_contained(toppath, 'package.module.submodule') assert _is_contained(toppath, 'package.module.submodule:object') assert _is_contained(toppath, 'package:object') assert _is_contained(toppath, toppath) assert not _is_contained(toppath, 'package2') assert not _is_contained(toppath, 'package2:anotherobject') assert not _is_contained(toppath, 'package2.module:anotherobject') duecredit-0.8.0/duecredit/tests/test_utils.py000066400000000000000000000020761362001701600213530ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. Originates from datalad package distributed # under MIT license # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import sys from ..utils import is_interactive def test_is_interactive_crippled_stdout(monkeypatch): class MockedOut(object): """the one which has no isatty """ def write(self, *args, **kwargs): pass class MockedIsaTTY(MockedOut): def isatty(self): return True for inout in ('in', 'out', 'err'): monkeypatch.setattr(sys, 'std%s' % inout, MockedOut()) assert not is_interactive() # just for paranoids for inout in ('in', 'out', 'err'): monkeypatch.setattr(sys, 'std%s' % inout, MockedIsaTTY()) assert is_interactive() duecredit-0.8.0/duecredit/tests/test_versions.py000066400000000000000000000060411362001701600220570ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## from os import linesep import pytest from ..version import __version__ from ..versions import ExternalVersions, StrictVersion from six import PY3 if PY3: # just to ease testing def cmp(a, b): return (a > b) - (a < b) def test_external_versions_basic(): ev = ExternalVersions() assert ev._versions == {} assert ev['duecredit'] == __version__ # and it could be compared assert ev['duecredit'] >= __version__ assert ev['duecredit'] > '0.1' assert list(ev.keys()) == ['duecredit'] assert 'duecredit' in ev assert 'unknown' not in ev # StrictVersion might remove training .0 version_str = str(ev['duecredit']) \ if isinstance(ev['duecredit'], StrictVersion) \ else __version__ assert ev.dumps() == "Versions: duecredit=%s" % version_str # For non-existing one we get None assert ev['duecreditnonexisting'] is None # and nothing gets added to _versions for nonexisting assert set(ev._versions.keys()) == {'duecredit'} # but if it is a module without version, we get it set to UNKNOWN assert ev['os'] == ev.UNKNOWN # And get a record on that inside assert ev._versions.get('os') == ev.UNKNOWN # And that thing is "True", i.e. present assert(ev['os']) # but not comparable with anything besides itself (was above) with pytest.raises(TypeError): cmp(ev['os'], '0') # assert_raises(TypeError, assert_greater, ev['os'], '0') # And we can get versions based on modules themselves from duecredit.tests import mod assert ev[mod] == mod.__version__ # Check that we can get a copy of the versions versions_dict = ev.versions versions_dict['duecredit'] = "0.0.1" assert versions_dict['duecredit'] == "0.0.1" assert ev['duecredit'] == __version__ def test_external_versions_unknown(): assert str(ExternalVersions.UNKNOWN) == 'UNKNOWN' def _test_external(ev, modname): try: exec("import %s" % modname, globals(), locals()) except ImportError: modname = pytest.importorskip(modname) except Exception as e: pytest.skip("External %s fails to import: %s" % (modname, e)) assert ev[modname] is not ev.UNKNOWN assert ev[modname] > '0.0.1' assert '1000000.0' > ev[modname] # unlikely in our lifetimes @pytest.mark.parametrize("modname", ['scipy', 'numpy', 'mvpa2', 'sklearn', 'statsmodels', 'pandas', 'matplotlib', 'psychopy']) def test_external_versions_popular_packages(modname): ev = ExternalVersions() _test_external(ev, modname) # more of a smoke test assert linesep not in ev.dumps() assert ev.dumps(indent=True).endswith(linesep) duecredit-0.8.0/duecredit/utils.py000066400000000000000000000220451362001701600171500ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. Originates from datalad package distributed # under MIT license # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## import glob import os import logging import sys import platform import tempfile from six import binary_type from os.path import exists, join as opj, isabs, expandvars, expanduser, abspath from os.path import realpath from functools import wraps # # Some useful variables # platform_system = platform.system() on_windows = platform_system == 'Windows' on_osx = platform_system == 'Darwin' on_linux = platform_system == 'Linux' lgr = logging.getLogger("duecredit.utils") # # Little helpers # def is_interactive(): """Return True if all in/outs are tty""" if any(not hasattr(inout, 'isatty') for inout in (sys.stdin, sys.stdout, sys.stderr)): lgr.warning("Assuming non interactive session since isatty found missing") return False # TODO: check on windows if hasattr check would work correctly and add value: # return sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty() def expandpath(path, force_absolute=True): """Expand all variables and user handles in a path. By default return an absolute path """ path = expandvars(expanduser(path)) if force_absolute: path = abspath(path) return path def is_explicit_path(path): """Return whether a path explicitly points to a location Any absolute path, or relative path starting with either '../' or './' is assumed to indicate a location on the filesystem. Any other path format is not considered explicit.""" path = expandpath(path, force_absolute=False) return isabs(path) \ or path.startswith(os.curdir + os.sep) \ or path.startswith(os.pardir + os.sep) def rotree(path, ro=True, chmod_files=True): """To make tree read-only or writable Parameters ---------- path : string Path to the tree/directory to chmod ro : bool, optional Either to make it R/O (default) or RW chmod_files : bool, optional Either to operate also on files (not just directories) """ if ro: chmod = lambda f: os.chmod(f, os.stat(f).st_mode & ~stat.S_IWRITE) else: chmod = lambda f: os.chmod(f, os.stat(f).st_mode | stat.S_IWRITE | stat.S_IREAD) for root, dirs, files in os.walk(path, followlinks=False): if chmod_files: for f in files: fullf = opj(root, f) # might be the "broken" symlink which would fail to stat etc if exists(fullf): chmod(fullf) chmod(root) def rmtree(path, chmod_files='auto', *args, **kwargs): """To remove git-annex .git it is needed to make all files and directories writable again first Parameters ---------- chmod_files : string or bool, optional Either to make files writable also before removal. Usually it is just a matter of directories to have write permissions. If 'auto' it would chmod files on windows by default `*args` : `**kwargs` : Passed into shutil.rmtree call """ # Give W permissions back only to directories, no need to bother with files if chmod_files == 'auto': chmod_files = on_windows if not os.path.islink(path): rotree(path, ro=False, chmod_files=chmod_files) shutil.rmtree(path, *args, **kwargs) else: # just remove the symlink os.unlink(path) def rmtemp(f, *args, **kwargs): """Wrapper to centralize removing of temp files so we could keep them around It will not remove the temporary file/directory if DATALAD_TESTS_KEEPTEMP environment variable is defined """ if not os.environ.get('DATALAD_TESTS_KEEPTEMP'): if not os.path.lexists(f): lgr.debug("Path %s does not exist, so can't be removed" % f) return lgr.log(5, "Removing temp file: %s" % f) # Can also be a directory if os.path.isdir(f): rmtree(f, *args, **kwargs) else: for i in range(10): try: os.unlink(f) except OSError as e: if i < 9: sleep(0.1) continue else: raise break else: lgr.info("Keeping temp file: %s" % f) # # Decorators # # Borrowed from pandas # Copyright: 2011-2014, Lambda Foundry, Inc. and PyData Development Team # License: BSD-3 def optional_args(decorator): """allows a decorator to take optional positional and keyword arguments. Assumes that taking a single, callable, positional argument means that it is decorating a function, i.e. something like this:: @my_decorator def function(): pass Calls decorator with decorator(f, *args, **kwargs)""" @wraps(decorator) def wrapper(*args, **kwargs): def dec(f): return decorator(f, *args, **kwargs) is_decorating = not kwargs and len(args) == 1 and callable(args[0]) if is_decorating: f = args[0] args = [] return dec(f) else: return dec return wrapper def never_fail(f): """Assure that function never fails -- all exceptions are caught""" @wraps(f) def wrapped_func(*args, **kwargs): try: return f(*args, **kwargs) except Exception as e: lgr.warning("DueCredit internal failure while running %s: %r. " "Please report to developers at https://github.com/duecredit/duecredit/issues" % (f, e)) if os.environ.get('DUECREDIT_ALLOW_FAIL', False): return f else: return wrapped_func def borrowdoc(cls, methodname=None, replace=None): """Return a decorator to borrow docstring from another `cls`.`methodname` Common use is to borrow a docstring from the class's method for an adapter function (e.g. sphere_searchlight borrows from Searchlight) Examples -------- To borrow `__repr__` docstring from parent class `Mapper`, do:: @borrowdoc(Mapper) def __repr__(self): ... Parameters ---------- cls Usually a parent class methodname : None or str Name of the method from which to borrow. If None, would use the same name as of the decorated method replace : None or str, optional If not None, then not entire docstring gets replaced but only the matching to "replace" value string """ def _borrowdoc(method): """Decorator which assigns to the `method` docstring from another """ if methodname is None: other_method = getattr(cls, method.__name__) else: other_method = getattr(cls, methodname) if hasattr(other_method, '__doc__'): if not replace: method.__doc__ = other_method.__doc__ else: method.__doc__ = method.__doc__.replace(replace, other_method.__doc__) return method return _borrowdoc # TODO: just provide decorators for tempfile.mk* functions. This is ugly! def get_tempfile_kwargs(tkwargs={}, prefix="", wrapped=None): """Updates kwargs to be passed to tempfile. calls depending on env vars """ # operate on a copy of tkwargs to avoid any side-effects tkwargs_ = tkwargs.copy() # TODO: don't remember why I had this one originally # if len(targs)<2 and \ if not 'prefix' in tkwargs_: tkwargs_['prefix'] = '_'.join( ['duecredit_temp'] + ([prefix] if prefix else []) + ([''] if (on_windows or not wrapped) else [wrapped.__name__])) directory = os.environ.get('DUECREDIT_TESTS_TEMPDIR') if directory and 'dir' not in tkwargs_: tkwargs_['dir'] = directory return tkwargs_ # # Context Managers # # # Additional handlers # _sys_excepthook = sys.excepthook # Just in case we ever need original one def setup_exceptionhook(): """Overloads default sys.excepthook with our exceptionhook handler. If interactive, our exceptionhook handler will invoke pdb.post_mortem; if not interactive, then invokes default handler. """ def _duecredit_pdb_excepthook(type, value, tb): if is_interactive(): import traceback, pdb traceback.print_exception(type, value, tb) print pdb.post_mortem(tb) else: lgr.warn("We cannot setup exception hook since not in interactive mode") # we are in interactive mode or we don't have a tty-like # device, so we call the default hook #sys.__excepthook__(type, value, tb) _sys_excepthook(type, value, tb) sys.excepthook = _duecredit_pdb_excepthook duecredit-0.8.0/duecredit/versions.py000066400000000000000000000105011362001701600176520ustar00rootroot00000000000000# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- # ex: set sts=4 ts=4 sw=4 noet: # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## # # See COPYING file distributed along with the duecredit package for the # copyright and license terms. # # ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """Module to help maintain a registry of versions for external modules etc """ import sys import pkg_resources from os import linesep from six import string_types from distutils.version import StrictVersion, LooseVersion # To depict an unknown version, which can't be compared by mistake etc class UnknownVersion: """For internal use """ def __str__(self): return "UNKNOWN" def __cmp__(self, other): if other is self: return 0 raise TypeError("UNKNOWN version is not comparable") class ExternalVersions(object): """Helper to figure out/use versions of the external modules. It maintains a dictionary of `distuil.version.StrictVersion`s to make comparisons easy. If version string doesn't conform the StrictVersion LooseVersion will be used. If version can't be deduced for the module, 'None' is assigned """ UNKNOWN = UnknownVersion() def __init__(self): self._versions = {} @classmethod def _deduce_version(klass, module): version = None for attr in ('__version__', 'version'): if hasattr(module, attr): version = getattr(module, attr) break if isinstance(version, tuple) or isinstance(version, list): # Generate string representation version = ".".join(str(x) for x in version) if not version: # Try pkg_resources # module name might be different, and I found no way to # deduce it for citeproc which comes from "citeproc-py" # distribution modname = module.__name__ try: version = pkg_resources.get_distribution( {'citeproc': 'citeproc-py'}.get(modname, modname) ).version except Exception: pass # oh well - no luck either if version: try: return StrictVersion(version) except ValueError: # let's then go with Loose one return LooseVersion(version) else: return klass.UNKNOWN def __getitem__(self, module): # when ran straight in its source code -- fails to discover nipy's version.. TODO #if module == 'nipy': if not isinstance(module, string_types): modname = module.__name__ else: modname = module module = None if modname not in self._versions: if module is None: if modname not in sys.modules: try: module = __import__(modname) except ImportError: return None else: module = sys.modules[modname] self._versions[modname] = self._deduce_version(module) return self._versions.get(modname, self.UNKNOWN) def keys(self): """Return names of the known modules""" return self._versions.keys() def __contains__(self, item): return item in self._versions @property def versions(self): """Return dictionary (copy) of versions""" return self._versions.copy() def dumps(self, indent=False, preamble="Versions:"): """Return listing of versions as a string Parameters ---------- indent: bool or str, optional If set would instruct on how to indent entries (if just True, ' ' is used). Otherwise returned in a single line preamble: str, optional What preamble to the listing to use """ if indent and (indent is True): indent = ' ' items = ["%s=%s" % (k, self._versions[k]) for k in sorted(self._versions)] out = "%s" % preamble if indent: out += (linesep + indent).join([''] + items) + linesep else: out += " " + ' '.join(items) return out external_versions = ExternalVersions() duecredit-0.8.0/examples/000077500000000000000000000000001362001701600153015ustar00rootroot00000000000000duecredit-0.8.0/examples/duecredit_example.gif000066400000000000000000016673651362001701600215030ustar00rootroot00000000000000GIF89a' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|||2&CK?q\Vk4]g^vűvBB}-fRtA>%hS1ވu9Z(rb^ 8paX#ypN-SJrMbu6ݘs6u iv~gc_v0 jo(eY!jFhtHA0G ӐQ)a u:jyd.]f8jJ4NiV%z>Z]gj癪l{NhH&K:ٸJjZmlsm1G81J.nm/Z0jІ<';c3\gpF\pv`4d4LF!(AM@q'#i 6`sĉgru$%:0T*s줿K)P#[ $pyd3R Cgj0 fφ['8ETeAB(Z5ga5;1 L_!={E5G^p G4i|"R?NFDŠtw{ZUu`'f\`2jD]v'V2rUqe[L89G :,^],3X21aB(#|j"-I1$\ϹNvc^DY,ҏ~f)F6A(buznxIKRa ?|#<XEBrՅdk)E*p-%+R.T.s%dύYET)Dsfp!&2d(F@$Äi 7>:m=$Ga΂*Ovr[4l؈AM{Xޑ۞ĶGE sx [hT ^ұs4$KR$uGA7sɏDeU{= 43cVJ@"0 bX .MgJ5l.gW/:fFD3= x*B}^;WݨAz֠PS4uïx;yʠߧwZcrF[TV*wزvG -ͷ@< 4Qt"N|5L@i_Onnn\|־Քcxo!Uߕ ){L4g9xs"oz8;ţ[ 1ֱ8W>fk+RaGچ7vX 0[b o "/D)BזU _kv] -9E.OoNH΍hԑ[ck$yK.׎ϋS8C=1. ;*]3hy%D7y٬ yB5WqԱ[G>ynM\J.b-W(mH 7dd1}Ĺ H1>e~8jk'AwɄ7l_e+}U68j:f 4NbebaD{Fqu05N8sgQ+:sGmPQkt(ELL#x_Hdއ.t}0WP?{r9Ng|^{ 5F@VsR_Q?;U=vP@ =x<(Z7M9|W/SX V}SXeUsc6j{0piXֆ#V6'$Fc(kg0XG#}'r675rsfU8TWl]vfWUXo:n& MH` 0s^uyK{XHS\҇XŌHO~e^؈e~4ѷG]8TS@؉ah}]oXO5|GxB^HGT7oS_Vn=$wQ!gYP30b6;YF|qzX>eC6erGb;"$u9h:Z6Z0/>FgeiŢl  bV(Ve567 ĔTF~慃[F{=!S!VWp`U{CXQq/;__gY+x#/؀3Wg^t\{ yImkDXhڸl閇&NLh>feeih|>s.'x:+ؖgjؖtd,'y(_gB,M!ɕËpnhN8XGp+oiriUA=If&W8$s7%1?Yre|7>V ٝU|8cߩ:VOu̔_er4}hVaf.lex9T#FGyHIxU{ewՔIrLϷ@<'Wy(vgSE!H6GOwx>izu#TWd;!'U]$UVw0h)7|AFiwbhV:Ȏ1d)MJbX~Fi#GoiGyu6qfe{ZeפȗG\8LZJhz%ƥyfKQFQ҆ClTAnz8JO*hh8Y 2C9RHUMt/N_g: Ud~5fՊ kftNs ]t QʢjOJJQ3tӇr\רw(j Z9KOJb۱v8yuIWi9@; [:d7ZHY/("66Xxu^ىY)fq{V9!a8_ %kR&R y]Iś 3 iklfӘy5B7eI~;^Bƶt19W{WIȀU[t{@շ[Q pS6Hc[_y!ù3n0Dy9ZdlK Ve Ph dĊaۉ;wc8c#i*H1+[^>oY}X(j8O| ~E=\sC85;TW6jHP!o7拜vy98ŚŻ]&TӘqlT2{b0ۻ9*?Ҋ$,#똦.ʦ=du%#lz'YU"|XVšlKhX>e !1q2+]^LjX̜+5werqY9j~Qk\\dil[<m%c<Jsp]l|DŽj75TNi_t&ԍ.Wv_Uʮ,ˌu'X2̹v|5ERzGv}º Xѡd gmP~TId|V ܪf~WйŒF*RF7Lr7Њz]헪 =/کI@\& )2;[lc |m`BCMXYq[D-NߤGMK iHQ+jZ͊jk:AmEMF--@tXZ[@@vմȬ|ZMta<$6ō͠ul.Ҙݧ-$Khp-K}<تsUٓ n8/UKڷp1iW+?J:_ 담=:Еn׭͎ua8\ĩOǘ=ͻSyE$m'}& 0Y1wՒV(jpRenU;|ĶBVzxjk!LFJ"i*46ĨT'nIYJeO..-r?~] eR)&;:SJ|gtÁ忕j( *||pJ+<]~> tkl)I}hf~WkjJ\5Jn:c hh^2[p}AIjHYah~V9v\o,F:q^Xڕ-n٧N5̂ȹ~;=7S Nv'nΕSn(Ex9ߥ3p 9'NCͼ8\2WK`\4&ZUY^. ԥ7 >zov+n{,v$x_'h}t&L!4v۪,[S*TUY`dv\CM߁Xx~I/+=ՅtvOIfOb ȂݭP,>$֢QcL|խGK ejՄ2] =߆okםK+z%sNsPM:!:K2h,]ӕ4#m٥=2KVq }艽珧,HI/qJ,x 0"Jn|zPJREJUY<!°!˚Ӡm)v(\IZk"I#ݺѢH6ӎExXlaVd:r߻ҴlT `~nճgIF}DĊ~l7s=*%`m&=Էo݉z&PQ?{%W[17|\3^݂vKsLWw_-ӹdtEw5k7_+-5n뼓@- 쎾N> K=9, ALۊT9 I _t"@۱#jHT b Oʏ,Mބt\pIĊ+3 L6JMC.sK>\6 69r,QNi ,G.5trQ"u͓3QV8=oWvc,Jj$ U7]VCŜJ&=Z:ͣo:r5пKnAhmWhT"]1tW-}Pe \q7N4SBSzaj*4 '|[NMO,6ϴJ6eWiL۶ΙY#=_SF9ٳ.E^p6n!U+uko}\A ^Ӌj\Z[̻JyuzR(98|ʒE=?eͫ}Y944uғWL_Ip"oNC<PŸXBA_tՉ_H0vY ]CapT4Rp; CA)S=rD$r)@ϕ+xVD,o!Ėژ5E} _V5+hINBPщ#;"E41FcE#Qz$kp"7Bmi " )E)A'pUժhr 7:y΀ D3%g_(IUvFs,O'K%fAQeNve+lZ' pnҋW?IT0'[ՠr'A%Ǹc`L[;M>ӐJܗg#iX1<}F]I9rSC ͧ?3'C*)k2 Ap<)6SzlSB8ɷLҁV, &0ƺR:6 U^ԟ2Tj:=&7fS5hZIbSȱ:tQvQS}p&|0)T)Tu%^$y Us=g<`M\S2nhZcReeDctČ&ZeXtîJJ)j3'(Rr[v #A͘Y%k&YņA,\bzH0aO TrVSQ^<++5 5FV:keV ~5տ$mk,} *)kbcz۩j}~tU Ͻ BwXla4e=n=1vd'bt 9j$}^z픤%m"9I4<Η`OɾT2;:uȖ SUrun&E5Ub9^|n3bHe IW%2Ju; Ze3KT+VkQGV{xOцAզWd*;rKs|&LiH[SӂOحUo4 __a ܤ0b4g::au)&۶o4ܵ m;w\˃Zo.mu"%4"-} ZLLdد=Vĝ7oVUW. llBLv6)p ;{4*;2ל7?Yl7eE]^~oY>t\OWbsk*]%.jGš[5 (0H _sGdNBmr?!{1ԿZG]sfxN:eΓ_|ލF-,: 7p7֞zec !Q0ɧANثF\ 40t=T,Wй}B)cEW:^|:>ߺq4@%QɌB3@;@QI= 3y"t"-~  .)c "ךL"7jAA~1.A,B+GK$Y. t BщT 1Y c[% :,4d/ocr ST"9A;C+a8_Sð!DCCD478?|Ce! oY1;KMCE%XzSo%„r\1&K2OZE\U(([7 :[ FNE0deZFg,O :Flt&mmp5"éK5RGXcGMCZyd{ǎ9G GwlDkT|DȄ-zGiHE9)!`+.ȍȱЉ`ǩIr\2AcA'!pD)<Q8qn˘)0əҷA*=#֣%l:ȧȨʏ$,dJ K dICɱ|Ae4JĮz :Kž@)d$}\j≮4Lꡬ@t1t8F_Ig +v.τ)F,q$}*TD'1s;!*'\IՉ!| :3 4cr<U+ΒZ5GV$O+lΪ?)T: NķH㜽M2 b3)42͔JDkPOD( ETNsP OiH(QPNu !LQQ*4K QT+u RL[! 8MMr,r̤ Oۃ+B0#3哣 - yhB %!- d=]>} +ۢq>8 ء)r.O֒Tә9a XLT=O* bPL,L4Α ДՆȔH ks(˻9E2)EF% dݱX YSH,”r̸W1 7=#@ep?Ke5,U@ΌH $' Z3خ1*cyکU^rY@5#㙈DEڟDe`ٗY1NR'4]<+YEjɜQm#x<΅ a)$xZl=OHMw03.3< ^\t9B_EߥZDaVb]PZњ9MFS]d=w,*0}BV v;-CD,4{uO0RUɑޢuޗl5eSK$Q< /嫍m eOVЪ.6ҵ}4jI#Vh^ LY&S[=ƤYŇ%IZ*Է8갮Y{%j%*T٥#%[?^\ T!5Չ7ϡhG[lK.N_֜X'ញenFmgsŋE8aWiӶv39n٦}/h;3zQi&xlݍU]B{E`n'qHRV!GU(XK q6ۇ%RTiS QȢ}U~!]p8W0g~gҏeH9Tm6mVKM~X%֚~B"Hn % ]hڑ>$}O0 LnP gWeWZiyZzؑ{)m靣-iEe5i__s ]ub顔fc IjmY*iv Q㤷ņe^cHJ e!&ciqA9&RlN H%% !ݯ~$:neΪYS1Z;Bc$9ypYr,!nUm{+nOh(JF*+iiffTS _{QYl|TTr bDYAG\u<&YLo%9 ʠpфXt mZ/ #4JW&4>zYEȒiR?\f%ht^qŤ|!nDa\_'/IaEfRRɡ;R-E^GaјБsӪOc˫Ivz3;󡷭uPmH=/:EUO. Ě8hñzjߙ Qv\MҖ"i9 ^ fXIIjJ咒FXOYsm2CR\ZPVU&@;;됪ICR~vSp4EHmP=ߌ6FRw}(8Wm4.|ZPJq{1݋˻n;i185[N8d[1|J׺S}f rREجkcޯ5Mz5@wP@ZXhjS\RvԌkE>)&֕1{DY2JNX2wMLԏOgnDKd 84d^)_bƁITݍȷa̯KhuhBwgFX6cYHmyWX5ա7hC @c3G-j՘kiAU宮sT[M*6^V%>g1Z9>ac'ZN}4giFՍ ʭPl1ǘLx0{NM4/U9܀654y2Bq1-ԵCo3qT3xUSknNuW1̸,nOഃ-x[pE nr>k)Hd'-2y.&?g3I՟~6}-2IvNJy͌h5?}TfO]u2ݵKGxn_zu$m!"2`ns,[tQ,uzC-\fXyNEJZ _[ۮw~ { .;(ƙ]q-l7p0}y!$r1㬑]œm PٝwX zKHuƜ fp ؙZ\ ȝOx &^}* 8 ]͑s̝׉$ǀ]ѩ= )\ļ^jPӀ򎕸MCޜa [O/aƐذȔ!l9VL "pZO [mR~\O %YrFXuAͅ#t5ItRZ5[;ͧSeUCɢLo`M#6bI߰.V2"lS+݅!i/Zu #C UP ʣqئ2F0E5=50-d=R DNS8L>A#6b-RGB9$AU 7JUdB&T!j^FF$]1dG .R@Ac?- *OdMdBZ$=1bFP>!fR=Pi4 x4. Nc^}eE6S"HETA.MH̰?ŝ-VhS%D]љR &ϴ ռɖO'fF -^` H؋qj\Qn0g$[UobqaheaBWOeA]!tZ GZ$^[DUӝP5"e淠Q(leX(Iۧ}h N~ڌ݄fq6yм|bD|E)MS!bOۂFaE0"ٟЋV;OٛߋV]AI~_MNx%jWQz5[*,ܑPĤdkg٠&׹W*ڬրp UP~XhюZFR܈"^^ k^[>+=W1+&ޖH뮊!֐FHVfb}K^ܽ q9rm([ynʗUsƲոnU|sU!&\`ݙ2,֏gö5bUj6f,,RY"g]Y%肖la{@=$r_U*hjެ6`~ FB+Gֱy,v|-*ڎ m흢mȽfBUͧ=_j҅>BwT.&r 噝&릪%!D9}`+qI.uo禠,2k_]jꊮKvoyك* B`쪧q UB׀ jZ,+00`(HzpriZ"Y'F!IB 2骫!دʬgv֬nbogP^O{zʊg[r bLoD1@͟ޙE,F1])Xh5om^z1< _qd^` rh9Լ'tWH`-Zjݫu/ cFy)Z/Fݦb2݊N4J]r Yeq]fr%)0i6*'s +&nW(ь/ 2/vM({ͫisפ ^0W҉-ʞط3tE b޺'⺫AX -.q,MT_ra*>W_?3&kr2\яʪDo.@tI9e#սFaLYդoEWp9k/ϊUEk0،~&>Uϴ ޝ 4PG]x.V)Tsa]hy7 Hp\P8O21q GiZ~MņєZE,%I:6-bWq ( Ըxicivmd7Zjr @I_S& sԵ [i,r^4H/FoXwwt/('"Lb 2'I_I4| 栮vGnk/݆wEw=bv~[zx V"8s7o#Cj϶ ųN{w.w2šqE8i){ C:͎c6vL}N42#tIs!/2孯voP-$L@"o _|9xwWzX9 SX[ޫ/s':s):e] mZK/ݶ5kz5[  ?ǝdo'1''^ܿYyb\ lwzR\2L6_/K/eg':{{g"Wwykق"f{~#t_Gӽ̺<#ƷZy&>;/4uV^.n w>5=~Y)7>~g%ո, aCW=%'bk@Vlщcq qi1ƘsiX/WOW?6r>lNvb]lbaAٶ9ȁoH)'GhSi@(97"P' *QQBJ{a Nhp HodJ-DhrȐk2N-u4̢C_t͚)+B]fTJ:jW_zRiYPϦu˶om{n]0Uźso_%ѓ UE7gL@{F.)SY1{]Sϐ>[]l4fZ&KyhI$=ЀFB, ؇}/ܙaBT >5P㥝~.e3Q*=TK7ǻ>'˩FYp>G? hj裏;sAѺ3/=փP: y8 Co= 39ͺꋳj{70-/1$&K).H; &#mI'͛KI83²rKzI&%z92+M5ф2!Zm9j^cߪ+'F,ұ@DK$HBP:/L$t@k2jģf),Bo;ݐBm=19_MlĬI+d/U?>-$ce:]-]zOs. 7uR;MD$nnpʡ3@,JoԤT6_=-)S" )6ߓBḏ'3$.@'}oM }dh+ cĬOM"_=1#}kc%Ƞɩ[xJΘ*B'=lgE8Y nK"B*i|U+&v3yq.oB:^O6gSլlY ?+R&2&&BiOJȅI.9(Nˤ6Fbbv@IZRQuQl򷿁mk,g/|w Q&GMaV2QKL# ]f*2I,,Ld0~\eSI m~Aj̼Ui-okҼ;O#GL~2tILw‚4{h`i̜PiPduV2_ԑ]Tau>-RJjƤNʫ>D?+PsһdNkz%-kZkU"k"f>R(H9 J7iMV``>6i)4jiMfyXp-FJeJhh;a TAmKFM"yrJ KZusFȲѹgW{٩䖓U{{ò.!h**0"F $w%e(-UTf^To.+ðaqCEzzD)|ƃNH?2rmeJ:!L/O Y7f#YNxM5<4' V>5n;oȣ6zWRlŗwќk_MZOSİkU|dn/A酈*줿O7)#?P{nEqڛNz}LoAY&?&era)őh>xȾ:p  RLn^dGBcD>(atrpoƥC*cϿ+- w ,ИXH2bpބ/<yLyZyeqntLLml %w1CPa]HgfUj`M3נJgvdfmVLe4M]Ku.hT* -*ε(28f" %r4JnnO:RCjZrcĊq(iBa_oݴBu(¨2pI2-M Տ,uҳ *Đ-D>/=Ztm-щ@ޒVJF$+J`jN(+i邯vN-ވOP j\nHpk5 ԰kQTPGd((.rsSw皩'ՈL8HClӉ&pN1FI[lIlŽrV1@-IR@ T>%Aϭ0 q$4B}jĔ2;Ő9m3. /?s?Eɕ@шZ,DqWrf_B5x](ENE' +T彔C@sjO,01Yg> WΜԜ>S/tJv QhK}MQhiJmP.4'Q98]d6r.)L+I R,.cJ^ҬTwTD^;7U9=Vxdu]m 5{!ψF U3UGhU٭Ui+l 7T1;2Y2VBJA% 4֠6?Ls/1O2W%x/۸ ِ%V{mIvя $X 'ej3bFܰoŖ1SlFq7/P3dZ99җy~(?r uʪ/vj ʔ`v|G=KH܅=YXSXأl-$eZ;9l3zD^rWOVM`p~0}9:X#hA@A|O yAz%(!b\'wUIGvU) kz(H"]0.9qPk3axQ:'w>ҞjZݘEQU(t,b2` XF&Ъejvy5{!iV] MFeѬZ^TO)[iϨ,۬QZ[Y6p sr"۶ 5YTQsgɓmV}Зж|K"SAr%״%GQј?$a"+7TJs_8Jwow)gJ|XSt-'MGAF39kNbrZwmdt(slyYIݳ]V+ Hu L|o `)EJo1 ƅg*훃G&WI@yH>x\I[.R"N}JUk8j)KT]kt]c7}\*Z01Q;2裱MH!>e ȉ!KXO&x5/5(4!76嵺bA_1z :+:vxu/Q8~fOvg$Ux=ְyV/bc! hP J6qÄ .Lxb D7؈1%",0EKF|YqC *Lx&̙Y4 C4G6\ϝMR|jQeʂ8yZBCd:r&ҭ;or|tiCSOj=ZbԈw{> nнMfmJޥ g( aj)KZκo`oU5ϛIPl)bd3IjnV ~;ۓvJr_ƶ[yTԣ?KyrO_}W׶~[69Few/)%sJ%aQk`O#!QWqea_mz^ZQwWSW{.Z{͈YSHOÑ% Y\mJ5#Lڒ0fxXWeUISɷH#lgIWVɦXjH^Dr T^bW#.5 =rAf[Euh؟|'F._$$XVrrheHq_~FZFjG_ L:&X%~J"iUZl,^6xk+z*q iYJkС*cZD(elj`uwu筫:=u)rS_-^5p?&.S/gߙ:QseF4V,RJF_  RWgaZYUBCe$hmTtivspY' dcRn!좒nYwgޅvt\ޭK9UFiYWqQp~8vyfnxǺyin8ݽBx98zzƫO#0o";WqnAJ*g خOk=E&Zv+S}e=ZRϿ9#n>IR\¸6p9nǷAiQe(Es 7 TABʢDo_+TV?{tJJRSgp𹌀iV7ۄ/*֨4y|C7 KJ\r@%s$4(*$*1;`X50gQ1Gv2D<~ɍDa$JĤ81=b$!cGlO"ga;_+H9u?[y^.aR11eN_L& gKPbW:N'[+F1! Jp3!h P,Jp5$J~IXv'}}, r&贚-6e-xc]qX֤#>qVusu1*f,T:_TTIM.-y;l[ BW'=e,;Ԯ94"UI.T!.vՖUC(yj۞1O py'ڒQS5ԶBiI2-7%6^eNVIuOݞ7zv-M*N5@* ޵>RTmOT)JJݥ"VWADx-Lh,nI ~sڃp jb1V0ЀĥW"HC-2wkt=K]K%eGP>U.[RaXԦ1TZv[TܩbԆ]1匱˰OEke6&^*ʛ /x 6S-1S%0c{EÑCMy<6U4FZiI/@+BznYvq'Jb;Kό2΢GU9S!7VΕ 6gm dDpaz)vqtdݡT$ZO0%AAVz/G~dgN|~xzAZz$!:[zG{fm{~ԍF ot1òPFx+(Z^lO?Ama\BI6HU[H!Vjl:}}BW>/%uM>T6N0|eG%Y˵|U0wVL :Wns5rO(rɨ>RO1H&MBv,ѫwފJjcr䊪5[,"yujhRx6D|ٚ|LmmodIq 75;םenTm2O2z`!l%Zym6>ez.˺͊{x9V~мB`/O\kletVTJV[:NwCwSU!U-ZQ݅*y3xV*W!L净BuQXuHK!z.Sj7rcd]u>6t9H8[Bn?EXTe`gEafgS{K"3Z\HX#ZJE79i3U`hs~'Dc-rGmpf[z^XjZ|8]jXkxr Tjgn7Wg4 4{8ȇU8)Bk#E3n`OD$1-GCCW׊Vg:h؋Ȍ66B8t֌xXhhӈc4kH4(eJ /Yz4c~S3L/_bE&a#T>Gcm{xe42vs1 PF3 w3fE|,- %y2fc9b&wjfq694(M5GH8TD%SA$ѕzuS]* ];Yykf wjDCw2qe R5w9K \`2/y8k`( rGf7"] lSgÔu_#23q_glECJ ^^WwANFzBɔ jsO7FK$hN*^jFeT!R(F`R&IwB4&$MBfozJk7bON4@©d;ciNQNPJJ9KŀegHih4V0o":e'x&uBU}t1#@c6_֙V%YNf*sg#qCqNVuE%?婈F^1ԑj~ri%T_:s:8WT:-ʫK z#'LveB~=55m#Gv. 2/5zZZxS٣R"jEs30XIg0udY284W6dɂP^= sfH} iAYUN2VHiatS!'^Y).KE-J&u6m.UP^³p:^Gh88_9[ua[7Mb(ִ]{ot`?2\_V]Uo db!\ZG@0/kfvj92$Q2v: :%o0Շu#GlrGeI M‘W?FbeSVL҅LI/Ӻb3)Dbi%aP ʹce@I/3v_laW[ɚ:zguַbˑ Ŗjjk[=er*ԕ MvK^I{AxMBG:Cy~gR y_|@m4p߻Mg24{ +>_4^!+LYA41nVInsiXW;P?cEobZJŴQUd[Hh[]jPO@k?Ul;۩v [%j?-`hǟ^eT[A0 :ɪjë;ÊRʐOurS_jïtͧAb$iPcI⺪<]Vv~pjJ-yϾ[~;"| v\`( lU4h7phVm*>[(eXSt2S9Vܤ PBNj~$$;t~=@V#g~Il}'[$F{v/ گ6+s `m_ʹ)șJj=?W Aۤ[m =:n 8`TpI~9ɠ:'g: z,}p+:)b7;\nr"m,foǦl* ʵ_rNQӟ47C ^t%_"֨&sߒMs+\a_ #F˓Ǿ`j3IsR mzǙo=&OM${c&:K\ 㹭ЙkӨ{*̽JPM?S}:uɕB2DFyY1=ncOѐ{Gu/^snɑ+mPm ~ͣH}"[fcʉ_HpKMKw'^Ӈ=Dtaq+@_07}-e+rri)7{K-VJ;Vg'7ӡ䦹VxJfC^{\@ͭ7_}r)ȟ:oY|k7N(!8Po ,HP  F@+Fxp!ABdHrcdž ؒ%˂&M:Y F2sz M:GNHÓIKPŦ }B "JD]Nxkѩ-IXSJ ɖ {&$"ḩtGڑ+څaW: 9WC<5IDo\0ߜ#ZE:.۳W:xSt|ݐ#j֩M]h7WqiW}x y<cUw)y}{->CcO@~@4̍fJK^D5 @u ;~J`A/r#Li,#6š Ӧ$"&Ja;mr vB]0o݈up~q_D):S_ ,(v̄EA.TR9\ܨ]Z((vEQ`yd 8q6G!e#1Rrp٩Ly5II%ƺH47T Z}L?ki۬608N) =iV-il ,IC|鹍̍O%'/s#sR^3f9E&4 ڸmBȒ$E=5MGLZ='?&QViͤw %<$uhEJ Ȥ0lo1PI25Qq{%!S! {dhTfbuoO)OSX7%cLU]厀B#ִppPmIL@3HP Hƛଯ\(S6MhaŊ@$WWŧ|Hzȥ.w8*PJvYC|ͳmrT~c&YElu\A-U.fvOR$i0 W-Ҕ]lv34ytdL#7 wdz0u3`ݦp5|k6QG8ۯωƴl'(L -3eфhؼZ'C>13`$4py.Unk:bk kY/nkn&W l(-7SRuy\$eP2eU u-8EnEhl-&7'Qԑc tC2e?3Nh5)`0Mz]޵@0E.؇+s=AhiѶ1٬-$5zx̸KjZ3ˮbZf^)Shw2X\AK=ph 6u:h,#JIh5<mM*KצI}g~/DDAVZhuRcۻ!lW)9}êfڝX!vǮz++ܐ $#<#mS cn;Vt٣I3t =?=CabEQ/앬YrbHzٸEj5o|m_s7|4oxKcDv ɩoVz3PNg8<> O!{BͪZ$.27=X[?Q7Aj"^Z%b{+* ѿ7íZrLwYa[SX ɋʣf,. ʢqzj:'Q,zbBR[$Ǻ"*Ӝ5k$1Iy8,Rq!'Ks&;;T8ᷞYA'b!z#9_ڠ(i  q#;&7AEz22sHR8H 1uӚy Ȍ 64 }Jksk?8DpbϹ5B/YkƜ$-a>e[ж߁KPxKdSSJD|+.7 {*K8Ft79i)~82{BP ZÚy)k$ OG,j{Q7z\k̲1M9L7jMMGʌ˰͙kJݠINELJ$!tSE/ O+O#J,)C(G;=zVqQuڋIq`>7i!1˞# 3.M,SKv+[!H'MzG'-ۣ@8s2ݥ3Ey c<ȅ 7qS]H/9IAxAAl |Jوtb 4_)Q\TS[c0 (;@-U)P^,͘L;UAվR4C: . < N!9W}⃠؏r3+DU T ŒNdpY)Q]R$p͓oRY3&yjN-5ET .|z=;t: T=#Q!EAUT9l%:8ml|XMX8ג GL\}*֔Q:)er%ٖ5م,WP}N[D'؎MpVצuX٪mRu YIiW-z~ڄڨUڲM[ׯ]׀ZZ[)ɓԲUTd[qܩEZM[FZ Yi[[̅-۳pݚ* "Cm3N٧Ѻ 3N-6S2]IG#A/y^ڊ2,<ݺPjQ+ k'KCB[.KQ8ݝ+3G۵H7)TUK`QQ9bS>D\CS r Z 8Vc伔jZ6@\`ökԎy Fp- :a'?+ٮJ9!,+1JBd ˎz52U?AG~:4|A8 `3E{JS 34@D{RAF]F2dcF=Dqs:r:m;E^ Hz%o=;FX{~ͯ+#En4R-7VDCH.Bhޣ&,Z-w|ޫShn<6R*R8ر[j v5Z %i4^(X[ ;KoV֠&j_#jJd>UtZC}DI&nVQL||JJezeTAI:5r\ 6]8vZlXkd#DXl:=Gl^Ʉ*l,i8LY'AXJ۫v Վ"8,;>kj֘zY(c.=mO5ųj&ŕHFS+Y`nf /+Feپ5NFwyeo]jxM=\y.-ɡ]Sc/, k>18nh A{Tc0V8@Ͽyh --&HHhbqg)n>Iw5wb5HҴ|--$r [Yh֛$L(rl|LV&3Nv a걫s:a3s=f0:gmW'=a`ʥKrwEOLm-/Ԅ^c2l^NT) g@]36-vl=e+qq-cԜp++OL%@֔$iqc!nD,&NEG Tpwd&&@|s11]vaq]uXP'vBPd7+eMeazPO$~LLNwUfPk~phvfd]{?*eOyěO;8yTx@vlt9x'xM^L(o&S=+/8SR#-qr1ISoG{Q>ckٳ۸uUcWz(9ono#zfNvUkr7wHNM+/n9<}K}k?V֧c}Tғyo_窚l{Qs7xVugOߟM|7~gYoA^,WQߦ_jnrLD,@( !<Fd(!Ċ7>xƎ?b<СȒ!OD9rA#Ti2%M2Q:,1'F34XA5Y&̟OZ2*ԩ:RaQKڄӤ؍33LbZH/|{hYr҅k7/Ɇe VؓEB w* /\ -H^ WOHIu,-2.[lmΙq˙678ԷY׮lT_93,_mc%Zȭk' /t˱uz96[py)ɻu]݀U']֓o "egnA^f{'C=A&vu RၗaT{*#zA8h6]^eORꇡG)\M5rWcE/qןM}Qh^,1D7ՎgG)hzd[:ix%&paddJL(z3ݙ桍7 ԃg>Gq)a7入R!W_J+©eXQwPOVd 5tvdמ-yJzy-iLZQuZwFt~h(v.{ "jmnze)'1-M #oeq!Z2'- /^W{ftjn:bM]ylAsp:H.!r IV=rLΈnSEA JܭU{w\R_4 V栏ˇ љ}KտL.t|l=R!@Eщqӿ ..cXǩXCV93bܐW"rN'>QaVh<Š N֨"X,A"K s <$iKS✯0qk+h]Ԛv16]0_Ԯ6?)ʙ,ՖjY8š~i̯ ը,NJH`!i T6=n{Q\"MbU)#Ngg&X$ղ # NR2ub/^JrhLakt:ThnyPG _4!>6a!2 j\4ڊȊ'$)}T+2' pTBP&^E?lJ |n5RF0GxߋmY4Vߚt#w*$Q . HeZAו%*\ʄ%6'"trfc-;ԔŌqSA1@2KRru3k_ .%#id{kϘi}Jfb{J3fY&0b8P 1 SߜelR8NQ;n`uD&9٩2[WP{/^ꝲ01q;uÌ3r@# 5J]Ք!(Z[% Z A4ϐ q7duZhS8>РbkZv >~؟D VzlEueL1 x[D]Mi, pZLHO\R1U\cF)NQM]M՝DYdJhZHRQjQXE۝Q`EfilUImF=յQM8Gbb[TlFLUkdՖIi]MLTӾ)IU RԡimI6JE #ZŘQg_rE'NGR ܕ1k}Тf AÀMv90Ŵ{ZiȌ'G Xf^`-DyYq,l]}T(%: hK*VOa1UCq_sX B^@d WZۢ"#XS!TABdpJ~Sǽ=x#ᘓZL`ع}[ 5J& XK1:NP+b}WZd׼ĥNmY<%bSRMD @=8]0M َYRO K}t[ca2ٞb9MItI͝m`LLe!>c#u[:r$fϡ8QN-o\N̬mC^X1)\%hdq!R-gZ%K/yN4iXd{ޛT'ĥ+*U~:f_ EϿS0YUg;y: 7@h<'yK51ԃ%HU2f:W&RLSUUV%T* f# hIm#9I' !\cN fhhw|8!9e'PSp`s8$'ѕh$i$q#C&xUWP1ΧHh{SdMN>و^besv^:-BJ*c]ш*R֢^% )IL 9y6#.F i8>%g4eQ&ʉgjJ=$5JiFYKQ Of *F*!v_Y븖rFQ!*Zi"JY+*b+[2 .OFlFg%NW+vV"3S&*uI$n `gF"Necj jVڰlve0.VJeBb_ikq<;N-Z_gV ru.*\&y iBj']d8fccL*rYW5JI2QNQXm-aAn:(ܺa }q.Ldb0قU )\zb5veޛZ,Qne] 1eR9/Q3r/d * ^Xꚯ.%.ؙҌ$FZ^YbJ#r&7-KWxiz^PU[,)pU!fQ!8>KMfɎh"Gю=^30 dVY+)bnφ0аNz`$džjn>BnMF8ŧcݶiՋ_zݒu} ?JL1}J`FJ"]MZR_y g%q U1NnӦ1IaOB![:DFky) ÕM%okWtƬ+5lK=R)dlzrerZFTL^xK`:Q%U hz0rsru؃A=NN1V;jVRPzӳ* pj^S'7x"R3$/fBk٬Fe3T ,^]J/Dq}܃9‡6qHQ2!,~AQ_bRe IqT4@q=I kFfeAuWS4^Zߑ]z68<&3  Jwhu!U5fM%evcwZ1ҞFmmZ(U[>Jd$ۡ`ЌYm۳˸yֹ5Kd7rDwMמ5^j Y ^ E^5^5ɺ"2w!SZ $ӠEح,oamp{àHM3)D}'tzl(D<(mQ2[WT_8i{бx (nKbF6xzFMfLMkc]fGY "UN>ӻε|tʴ%ȥ% C)\p%LMx.Y謱3 g0I[6D \\(jbΓ8^#cNj2'&Gd8p"U[i86oo$3ʠ&`{j %AfOTy){QQ"a i{R$d-IoB`fi0C0{h䫾](0Lg R3lѽ%õ{B" NܦۚQ7 U4mL1F9l0SsWn׳DVbg(XV&?^6uzJ_6kRj ^J^=8uĉJ$hp`7N$C% F4Xņ9:PDR<aC)YnITiƒ23lΘ;I֜yNCz\(bӅ6Vر!ӊQ%n g¢%U*͌2FZ\+ԒN믫8"0ēm<o<ꦪ/s)  :ĉ5+4K!k 3Tk:믿J I G|R({Jӯdܲ˦HƺS4N xKJ9F2rL P.i]N2U`K l)}Xؾ17_'N]8MūY0SpX0p\sT`!M8I}4עQ2ȆXɋ+=)d]UYa8IftK QKXszn 2 h'9=T˽H&0dV=F.j1j5d/̞< V>]Q,۴v{vlo!OQo=a;q{<v8E4mIr^\uo ܎pWQhS/4%|RCߐt")@7"@9L<,qw-Zڷ-ܗrW5,>ߞzHΓ^?Wnǭ1Ғ/ ?zQ:fޠƗy^^AP7Be#/ӰłjȈ ]]}be@)L葶K"xGm}3r_Y-|hN'vS3)R92}?LNMpNd=*{P Ea5LNU)H+f91MI6a_f ;vR3]$&=Ze0<-bz'Y*Z TZپ*OصF֋*]L,Q1vslJ婿k)fa="V=uR%&fn-S&U$nt[:ȓ<\1I6e QaWtB ;V$5+6)@Ъy=i%:=q~9Jzn^'T+ZhZy5rF&x^M@xFW`.16ɢy^IVVdя'iR5FuG&WLj)hi6?jâ#6 kZS9L8>PthO~=aE,{\cX ;s+\जy)Urj妵{]{Ɛ: Een4 B Rbfqϒ-ꛄiZ5C'tEXFlDPq:λU A۳֞|Xm,uh\zʼ6U2L(kNtG۴Za܏6<Tps`^H9 gW^0gt=$  X-qiF]{5UnWw♊MAyߡ4qB L~k~"!|wwH{mkiO޾ҾY(YÈX^0mZũ>,urXX>d'²bc[So#ln_@ưvg引rer6bDmb2l+ y p+k䚋|ȣ<r`Ԉ")0G(=B vc槷>EPGA]x SFn|3 f>lQoeoZF+hHOpf4p%vfQVFe B40f,3 З(ppȰ%&cߐ 0pP0jqlXZR 1g/mDl,'L⚄8"@LJH vX˨+%s.Boj VBM~qDد&rqޠ#̑C0q?Ŋoaj t.MDG\MbDƉo-IgBxo~8h/W%F6̮)*&h`i:leuVfj4 INFSZ"0IJ8'c ElDgt0&7GK~4!YHsfMtx :{Ip11> <24IzY(E )O)FgIt1g.wSʹE$HJD+(I&?2~6."E#R./AMl$ClA'3")H lJu Q=1Mrʤ/B&pRLdy`J /$L 'VɞB7QOnԟڔBI1JӔ"=P{)L3 ."1Ot&> H`*׮,aL'eN0dUO1=凤0C1SṰ<%Q 3p|TKO^Y8d5M؄C|*rXnDSv ,[=~xSi6RMie{8)AFY8xgZK2T3X6ʃC *$)&hvE3{5_8:-N48L.8CgFj*(; })3yr;C:vu8gmX,A479D=qS8ЎUŜ OE,4 l^^ WGc/6X3xWYQgKucyVyH{TQ$Bs<C7*SgK@*KP Ey>˖trhW>WhR.GζR/'gHKmzlkC $@wwq+~c#7,iĤKeɪO*lEVAD:_ԧ}kf[ZLHjqpE/ۄfT4Hr*]NMŧ3puIlwy.pCC(Ц5<~/U rQׄ 05ɢO:e]zg6 /0#{u ;["v#F=u C\+{uB*1:^PIC{{R60J3 [ bҦӆ֘Bdack`me<P''q(Tb2#ھ f j;QdUfviQEFJ¥fp-ggAT рq~ksweesQkQFXux=bx|i͑;1HOp.˓,HlQ6<+[ZJsi%E}#FdHOl~R([׮v/.c-AoOѕnhrE4w%=mw1! ~0"N{C}Zh^1,\w}kx9$Sr $xBu6 ,+h6)y-\ 23F)wl=2\ڃt$m,R*k0O+36mP\n>Z>TcL~: iEu8@M~6P^eSIcuPػ|2G mrD=H^|tdϳflҭVaP |ob= hG'(F *fWZ! 116^ўB1Ngȯ7HJE}Imul2 W挭 ݢn 1sK^\=k4+4U gR VEU$no>,QkEwTEObMU][^Q_d NPĀ,8AP F| ʼn-B(q`Ï+V 鰤GJd1eJ%-6T eKc@?%tĎE;,)P 5:H<61P`ם.MV%RRF5^TZ9}vJ6cҫcrm[i;.f ]yxeРeR5SG/׽7dQ8PI7}BFOͳZ8v'5~{y1.֖k>v߽YH!']޹ǷݾYa]fS9s֙HwҦ'ET&6MV#Yrue#Mge&yRi#6K JZSjژ_ᎆ}2Ԑf^}ሇ+ڕkm٥Z UEZ_e˲l;Vvy)*.n=[] ׭RXmuywnnGqc T(ZV:=VOf",cI7ľ!aƣMLyV]Z!"?&&סnH;ٳ/mΒZXI*~Ί\t" UiWYVec1jBhU4hfD6KOV}JWºӃ1taĭnԺзj<_zM'M&v̂m9#[`jfm IJ>OT WDZؕuᤸ]`-,[d5QsJ\P2qPΪgH!heV`l&[D 3QeZ8bgW _hm=M*^:2ۯ7͟dJlV@$k n뿼P,d5$V ͷzWk]ޝfMruuQwlγf^^meY0[-ΗGd..u :=GvqNe/g`iƃ3Ps5/(CWO.nDD#̄(Z7{zSڄJ|O| -Yc/ҡ+QȲD .;f60`"|рaGXk6ь.yC;ˆ>\9q64~83I6~M4vMJnw Hm~~+\ms__=[⤧*<$'f1Io49c\ŪoIU}w~u6tPRL|4!X9  {$"-舕D-9eSjX(>O,/$(JT.2l Vx̾٭%gf!s7,"@(ݬ׉ {m,n"kp~iwfGd:SOl$Qb`?W&HU"4w +jV_,m.ZoE-j5zzBʡ^f䳤]E*ߤ>z΄NzJzxiRix?qUC%'1SwPTUT atWf4DD$-yp1+vB'6#m!HuIpU.]t-nuS1x#ntx>rWi=EiKE5#edQ!x#8X0STKo#p,?_>h{u3pQo^ӒL{ۗAVnqV4X)~YhVfї~ƖƲ=z TthkP*Ws[ siyu3)ei]$Ո۵{ aFrɔ&T Ca{Ɯ<шA^qJ%ǂVB@ԔaɘY5'` k95 ySFs2t&iSF9fxW47tbeh*Hـ2(wsL\[mndV_JNh*7F~R!Gu )t9G+P!䗾e CXa(vYsxoіf> T*6` an'`*/XhywCyQh@l*RO4̈́[qu4JEYJlM4c[njO@v%INVE-D;?*azɤ]3x2|Beu{4,)⦈JLsGU>sRǓPM>*}S hQדQ؇? 5~5)yB5SCSvGPt!KRG&VB%U:[۸W㘸)̉r <d(\H=V0,RF}\/>U܎O ywVn!k {fuݿ\JMZΠq캽66%u)s[q*Ԃ_۝}Aڡ-Qk Ω hn>\=ʺ.|R3yߎ82CDg!\s6MÒk|{Y^ ~ ۭ6\ݹ_d*mjn.fJdU"⬽hXȆȲ=#>-8ҮMV Ź4N5S(=ؤw}ΎMOVNQZ9ⶪTv+C<" sa}sbvyihgm.x.vuC,Wlij*)rAGmTm@[z H;;қp=B<'j˴ju\ȹ+_d4h3 Sq0"x!JaLP)o?aUo]{u-$\MN#W_1S/ʮii_S_צO/U?Rm 1ČDVIr m(? P|uϤ O"4+$Oo Q7I,}`%FxWqC'$hč.l%*dhĊ/R\1GG" A%Y2tL4_ޔYSgN6yDPDIdh0Τ+\ɗ"#*թ\jJFW=k#ڵjjL cW*TB@ h NxRMܶ&Y\T3b͛v>ZזG=4jZLuT)G4iܷ5훷Blj#FXS7lplc㮎_|Buj?V%<ׇlu'H Gş n4Z(:1+iv͋)* Z*C JÎ@$Kđ ,@ϻ{q+1*kɯv' UTl)?K gJqq$'e2(M+PU'SL*,Ӧ2YjR-A xA<8?L8$KL5, 㪹A/4ǴmGσ1@-:#B]Q 5ERUզ\U0l,FN?ݔNu*HwX0RfKs`}ԣ"JBWnC0əTR\-C8uScuC\#G[^I%;OqO`2%__\U9)]WoC`{UT_|[֗KcπD 4079ewK.0 H2nɳ2Xc#춨\/D[- U袳® nؼ^n5o5x>[]5GÇK#-]sUA*yy\~=u}C/f`C? |3M)׭*VEv3ћ2E,scU:M gS2mdlӵAeHHAfJZHf1RM}h5+f!`#Ҝ.B5 Ժ0FC]fC($y<ݤ1I,&BhsqHc Yqrh9$2F))c߁bG, #UELh; !t9D bQ( ,uF/,#C#j' neȐPR0)ȊoZ[oY-tcbLE-@z>ywXgq?2MC`́+gNFm's!Qmhi7h=\-OLGX~@/HMs疦WrOjWҴcIHTjthJ"IN}L!@]Gyu#TGnz̬)c0O4,Jʫ4Unnjzx$1L}|gRĔgteK S'T,B]m&95*C)׻4ڂ (l x81Ja.V?+ەõA-5mr!/O_6m5ח}6;,tF[)ijٱmXQ`b%8,QYc>fj(IZ"沤 Yxӻ!cߚ7tS7z"2."A Կf5Ѷ(+AgzsAza46dۿߘ #9Am3  e>$s#4%㰺/ 0k0H;0:*ö {  C0;237y0 c2̥ ':kcCÃZu9 D9i D)@c+6x%MA34J)*Yڙt${C`|FY=B)7>=3Y4B<9 4)X,2 <ƁaFFᚣP%dЏP4$$7 j z+5T6틹ȑ"㺗*bAڵ//'6b[ r5;-@Bo ;<GH'&Lh LU\A$‘ԩz$<ԓE+|9جb;l M2,S͹{1NDL§:Ġcl/ǘxc/\3A y?LHC9&ʠrOt#*dO JkܫO3#L8} rYJ_$O.)y@qІۭÿOɏA >y69ԭE,&0VsK2-Կ[ "%RėȪc*'zFlR킎"R9RymcO mOx /4B\$Z=95HD-;5;.*B[7MBUDS.H"T!T,@ Ȕ Έ?[(A/ #cURbXU03z74 2ěӳj DNU0WmH1"`AՁ:1geCAV+:geLb1"o0=FaDa1"7zEaxURp,Q-R}GSP%1<{C_ŅitXu3~>4bFȍrEEزuԒ6ߓuנ3!F "ak2ME&Pr7-Z˘Wiܪ6 uҪCIuSE (ثE$Nk۫ }J<;}==5K8d& kqSҡ”DDЌ}7a# 5'km ~s8?t5(\^18L4 [ą lK&t L2܃D[P;ԛʔLM w7)ڄMcӎ+@E[z4amV=LC=Ng )*[]_2։FߦQCxUa q}5>$PJlF5;X${ؒ YZ]̎=ŃMGkeVZ.c msǛ##"<ݽfcL Yû骜SV #)fVUYܒ=cűz GPHRԸCQ}0Ѥ?˨Q²5{CQ,JN0LdES!3[EBE\S{dC׽6ɠ-ڗKҢ %kjύ%K,>)t֝`5bdE=\e#y˼Uv8@R/`xK J#طe脻z+mm;9e65[xMj:o>^ּb܈ޠKH%tdzGd1]Zܺ|_״DΥ^@Q,:cs)Ml;q?f ;̋q]Jφ[d`-8b%&&^ 6=:3ҤO*\4nHml\.욄/EZ_ wmҭ/>[׎?v !U_VlXw{K:0~`VmBn?xJw_0DͶ6lhwj]2֖Bg9ю ۢz eNx3lU["\_ESޫߋ: w~׭##&NtK%XSU3z=أ&@$؜X)r^ɚ{G.#y_sJ-/YJYru(cTM5\BOt67uDIkE\yY;lsj!m}mGWeOIԞB`[`9/Hх皕D \`n+b=Y/ $W 悲%d%;)"fp+3N]ʅi7ZfDxLOOctVL[2Tzj(%1ִ/iAQklW-nyeҖtɛ{琜y !;^XE sb|&%a+M&)| 墂t#Jmt'"͐bIp7zZƊ-Ũ^DJ EЄp1 LEPK搨F͉K]<ÅlZ9d(L&`[ܗ2UFN -YT:մFƬ8iBguݺ%V2}.l*`Xf`+,C&Kb90Fu\MaB;ъVsVv__9x-.#-nK[5aZtQ\vmX]I2}.$ѷZ.kKCJ+o5[ 7Z D6ʲײe oURkͭS,m`b.la8>x˫o}AL_KT PlXm/h;/ii񀌼'gpR{MfrGs=bXuW3Ja==6'YJvPݲnUv7"YsP=p9 qt݀IfyeBi+nj* (ZiR*.) 5fm6f\RWw&uM=F]ӹ|`AzЫ]fxfO/[ٝsl_\no1l۠Dբ0IDa ޞ̛T2v"\7ۑm9bM|%HVGb ZbL#]NfV:UӄlvQQ4X.eJL P0 qH `M`@В[^`;ŧ=aI<D \?"zwS~dDf0՟4G<)щ>y٬&_aI&e*RQI(؅l3zDtR8bҐ2>gmytpd#z-en~2TJ6_Y=$[$vF|%z3-ET] 2dGʕ<_TR΁7 5Q7ibh7dӵTNA:K gyZ9v+} cq!ўTU~I L]xj\TN֦}.%t^uDajȣ`&~Sx5J~8hi* O=^gLI5qԌʄe"SyV&zb(#fɹ"X]junOTl arh\ Y8L$MݏkfVٱYQΆq"TFI^EV\lSk4b~"f8D8]u^M&ʡ:,anf,E,Mܞn6΢ &*^-5 I+pr8 ɚ'(Nٜ S-id}WdZF0$ ^ ҄ˢo^$5e z&rTpR|٪|^d-.Yq`$xݛ!6Fxk"oޘzFo!R]8ɑ3RB04֧ฝxVYx5j}1"NghH9ao%ֆH+0'i-x \/08 `?u3V/a/=&(Uk[fь?#QK6.0/'OI/0Yco[$o~&A^ !j{hgu!1n $c;LG/q!!& Α3dpWr|CnM)3$5B eݽX=_d0'.ڢǯȋh!@kn\Jܡ!Y+xSo4%YB`'7|3sgNӄbtt<3eϑ {CLu?p'/DۗL\qIYLg) ¶%+*P#fC#O4©G_ u)rM#tK,A!% Jڹz {5"6[ C.8<@9t[kɯƌ^e{!VܪMBӬ@8?ؽZyp-כI3 :v,1M{66ڣڏ,@(x JX`†!(p,G;B$Onp_Tx"&M-ARSgϜ?)4(͢Cʄy̚5UF ueRU% i%Q^Q*$V0 wBҎ4ߺLt\sMҽ[ݾ{lצǾ$0c7^ {&q=ΟQM BIiXqc۵%^koݯ7},-ްy?QtsW+|\C\ cֻ=9ݾ*qyاO>;wmX!Ȓ}_}3' 2z4 JƋ< )D?nskQ02HR)+ Oq;Fp0Ǹh!/o7Ӫ+IJI4"fRFOpo ^/ =A- MT4DAp" I)(01O> <~C顓L8VOLԹ$ [mU6t_L OtW7YL Y@%s.X {Z)[ Ta?F%mnM]H'2D75m͗'2.6pcv8T,&;`SZYvB t(n [iMbupOp_b.f'hkY`So&3UDL6SY.zn$ԲKOs ƺ^5.mU.#A.ث߉ئ<`2C2*OX 𨽼)W۟HH<2RpqtcS͐9ZdtAHm"Q>rMpi2L3LagI0$34ǦEX&3X F= E=LoPۦuL' S` $` Zv3s~a07L p4{ϗ1͓PdFꋦJM k\>JɃrF?RЗ/H~ٲWƫ]|(<2i*(}ccDF:Hi{Q$}tJaiBTjDQG@ozIA3|-xI%ۑ8oQ|"J@ b{ kFB| jjn\([bn0ۚ J SR8FD;2gkG!T❲VL0@$OOBWcj)&3`JzMÔQ5%W9?˱qV6=%WK1f =_B'-,QlY"O-I}-`3]V|Y콃,ҋyg BEdF08hLlf6 *뜳]T_$7h3wRz@,8"qrZ݄8mvQ\- >{Q=JcaϘRJЙYRΧe,r>VkyuOVJ4mL#ƸvڴtZ0@Bin&׹3ƜqRؿ͑9жK!=YZQF,j[*n|yoFI~DCzJܘ ..`,˸ A,\%2Notk o˦Mo839]R5Jkټ»EYJz X_ uڅ= 7V KWOtS;Xש0`#w.o< e2-<IӝMCMi}[ύy .Bu9#Xt#|.c3 ȌrYx뱋#H]C`?1o3 MY`GdKB# dO7xvy*Eߪ z? ?io=T:2#;~ˀȭ߃ŊtP[/PO~B&zO p0NZNodeBf.mF7Fϔ4eфE莅nE jNghL/ V l% 30"B- ҤVBNd̮ɬd0I޼+Tͦ&ߦ,(LcRF堌^F`ii  4>e+ ¼¶0gee26z/&FL8 g͚ڔi PKoPd%ƦXqXm C8}^| z,R`N !j`h¢aP ffl!)g{mRy-Io:v#|#P&J"f8d1ߔϤo$k*Qc \c~TNgb#&&USn)` g٢4/g"Ƿ`))H>@О8E hT0T̘,Ή8+ 40 nr*O&V'hh+v*R>pўp, ( 8sb2 ha'_xrj_q X0qJ6ۍ(6.F3@(%xŋnd5y(0!g) j )3WL3H ': RI 8Q-k'\p-&nɚfEQ\pcTiH+cQRS?op(Ԣ%:slt/NO[íDD7#f.:l(i4(3Lh -cP OkoBZ 8f?ϫDGԔ#ʧHP,;\IԏJFoݞC4I:KtXjuQLMkX`(qrX911;p$Q,*^hK L66DgB'eTX8SSP+3 ;^n;)QU' o˗ >`u(Jn O: SSK-!T$%]0=GT"n)2VUʹ~\1b(%'9W3ƂN[E„5['Չ0YU ˴H)IHn=ӏVIP>9"eCCFHlt,t|vFT mϏNLfI.)D<7=//s<4zd elO6h2%@v"CT&w~ό t-RЀMnM ~|/Cx `r 4mwqGq/ؖPԘn274n1tY2x v?%wnvT'a'E7 oY2m.Ӌr4Hi䷘2x!)*2ɥ*c2IUSpJ4zQPJkm,zKKyNܔgltt7}HfWYwNH~-*v3InE*E4ЛSj]iWЂO5z/XP|SKSOU8WV؀0axQ{r}ubD1V3))҉)ԈqrԊXsXdPͩ?|X8K/?4R渎6lOìEJ%9$t@tO/!Y%_)KCuy{o#ɳ7/FT>>gcA_$]L]I$T^}P~$^,Cؾv!1~Sw/ܩ^R U--v\C)֠v5Qd_ Y[EPG5Σ]=ˀRAᏙR Wmx4 6m k5ws"S ]Ebν8X#vy& OM^ ~.2wMu9`(N m!}T %Jn$X 648!B'FlHDD7>d`ǑC&`ɎI:)dɘ(_IC@)\vXkycXƖfѥ%lVrEGh<EZ$YU5^TW'xm5b{yClbbaUd5vgG\dx=c*Y*]biUH&V^[g&jfdcBӛŝ{>fITzS|h}Hաp {RZ-!'kixVߛ~gcnjmVحK-myZ赗} R*خi뤔Kd:jChl9-тYI1H 3&䄽bzϨS Jd`E3̺n\u5oG4%oFw|#6D;ii"bPY^d\#_/ tشLk_>4pW;S5K;}Xf̒u6bP*J/ǜOhrv)ym)7f! VDת,&XGN$wXΏ.3?r `ۋVzrN TYk\p;g_5 A ?"0SF,ytiҩv]S˜7uo?c܅0M21V)/YO|JjsL6\S1!AםgZ B `&\V"aX IK[[D]Ie:"]:x-'wƎꢽrQ QEJtHHYR/bܗZ87~}G<ڑl6(f D2P.K<&Ꮔۭ*sd~̀t@Eve0$'evT2C@+9f8:Q+`\wUa0[8gisƴgei%7oI<׬%"}bVoisG<7m(D ixJe7 PVFS4,d:YǬs3i2'01>(0SFů%iZX4%gXYcVU`uM6T=zdԡUxkR2!wAtPf "q,"*qY*Ɣ JŨwpǶ4}-Di ZDnbyN0.[ي# 1qNzj R <'\ =r*nsv Q;ڧWxb _rjTdmNi&}oY&@D5 &bU{Ҭx\"0ˋZ . SiD4>ȈJ ˺0gW׺抳o$уI`2X^ּtDpRCAM1qtO *&gŠkCOZsfnxc2a[0Ezg[1! ,' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||Uz쬣j ¤jY[de]o( $6iْ')2Cç5>m! ,' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||NT,^jN".8q"-]Y&Ȑ21b ` h5J\xL\n1S CkG@@Z: PƗk}&x%@JD jl6ôE-0W5j@%_}{ 0 u\CecO*`1btpa̙Jb*88w ,@UQ4pZe@7XDtQTxLJO@! ,' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/ ! ,!' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||! ,$ ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||~=E3?jggHbcf5"W'BTC>jcKPA1s~'O7t&?: x :7?#0vd ΩO7&'`yȋi?䝋wɚ5zخ';G ѹs^4:u@ GαhL@! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|| ]F1O^&БFeνYrds%G$!SM8? ˆ=LCr?P)̗# t76AM9H:H= ("HaJJ"t"d"5G)4|AM&D<44"8A㌑01!҅BH1SF՘R& ZNP  H9fdQAgQJ0R1R;Lqd!(T$}́$r# / " 01dDZ " ,x L) L0/c@ ;b~ (l&\4*C $BDE#fBtK9 *1yI8?0C$"&<"C {|g˰02!"@L#܁&ODj1+ "3@203IrA':X3W8ĠAlM9xՠ@ 69ϊ1|!73#x =d4Yc@#0 6tQ 8b N1D?SM3̑7X3 SN8᠓G*N6$6odS3P| :hP:ʄc ؠN83#PC;l:CՔSL8bSco<ƿ?87.x~55ĊpDs@:=CopF`qה uq ~4A@d5& =>aTX# |q3(0@#qX @X#(y? AC&_4q%;YG=K.6.q B(čL H(f.Bш|fsH:0?7 +w2$EG*j/w:,qms?H?PH0ېBD(0`Gi;15b0lj ɳ paT+pS1KE5+DLE80@ݙĐ1NGp0?IHz(;D4n+6`y vG)pxp.pc^5`OI|#  d V  <C29 `"q A]CVygtA7`\zձFat`2BY..3 /o}4V@D*",c{?a 8%s4!(QahH!^cM< Gm !HIAF`0!^ 8%#M?`/D*n!znjq7!y pp >|yc'Bx^(PGxa~+Q/Gcx8e#ERx/P?;zKGXS016wbIw:F #aDxȃ:1rQA~;4 a61*Ҫl߀R~b AZ°?`CʰƳt8!lO<aoF<\g (:#JQ=c0.avWټgh7$q2mCFPG氅o,xO< t$y} ` !  2y6^twDpO sp @ϐ$s 8B=$d ?Z(&o <x4qsc\@|` !LPv}P4*.hE~@nKBApwp' Gp'h$7Q70X}sP>`?Ԁ)a(v `Hp o C8XxHq ȘQdw` pd` ÇЃҸHb ƍhB帎(ՐNX8V%(R؏8 0 X|2oِ9Yy12 ϑsy0ȒP)!!10yD ' #a"xuQڐ#!x pDx׳PgXO0 :sP ~ p(!І1 a (q% Ԣ$0# 9#`(4%.Y`(8lfx %% z@~ .i eW0 50p20eq / % W {v BXto5&sR  0( IVj'_'lt loP*|0Tnl^[ }``;9y d`MR~։a 7v@(#*It &}pG90 @s$ 5(EJ@Eɤtc !5ɡ}p ^cQ4_`X \wJ 9H"щEr@3bgAPH @qs U5yE6@FE3"8f]=V k8+ `@ S7Xw$Vt w90C 3] Az \@ J` fo 4^8" ʐa W xZIp" P?ʮ`0s0  @a 6wޠ )_jJq` K $VP tApq U JېoPӺ e ]}Պw0S P #@  @^yS? @~Ԑ nCVIu  wG1P  #ض#_a )}7f4  ap`   4` Y9o`,Cq =:slp]d [ lȷ# yKU ܠ 0 53 X@xpp@A  ]4O@8/y0`I|p Wr.pЃ̠%[04Esp~TWsb ` l*JQ NJJqD)\X4K F8! PDx@? V%)- pUhrEKx.`52p'cOS j@ [P S RہsO̠ {٠p  pn  cd*]Y0 w`&+]F ]İv`"̀8xPt2`ax@ H*  c5  AOIp ]N Ǵ0'2@ lB # bU ʰF%d E0.BB0t ?{0 B ppҋ y ΄2^0 b &r @c` # V}@ u=I `  2Eh F ŧӘ .Ud lq s:& ϰ U댡0F! U XM!`Ž | Ŧ59c+y`cg PlПz0@`ۃ ` U ' \(ҸНA? s+/0I1 Q~ ¿W3` p C6p ct -  .ecs  `d=] h  YoZcp+@` @5PB7^%D c  @Iy '6Z Pl,0Z<"PA.|P2`A 6S@ 0e P/sg. S]@-^M4tP7 @` P2dX5f@^` @qNFJuĐ(QΠ@ 22Cc` X ؐ }n[/i$KPG=!与y0 7uPy$pooUXN=`,PF=4>R] 2ߘ$_ '30 U` tP ސϢ:!wP#4 L%SLm1IG%:'Ɗu>)՟pk%X a>yKIi$ v9RZ/ШQC)E[o#QytդryZWDA[+D&Š@RI$p)B$"2?*!j AMXBQ '9 %uFdP0 #@đe5P#q@+MR'K/3L1$L1g3MjPLQ,!E3O=[#<3PCE4/ƚf*3oQSQM7ei85TQG%U9p B3稭TW_5VYgV[o5W]wW_Է\#{l2yR!`4Ao`y\g$q?QC!$ H'Gģ Ȅ:ԂT<1쬣@D9&`D@3Z!k+v9TAy+@PF>qTP G6ʁQl V4%X9XAz7.aed ; ÖJVKް3ьej"0FQ8,zDEi ;8D&tBG)]H/wAa-`ËƉL3_?<>|ke@+hTq8>'&Á W #dv(Gk8#R; dB9JDwX:)"aֵzo>uL'}E[ CRUk4Z6TGhnv *:h Z;^׼Eozջ^׽o|9&mE%b|TP1+&:tGV'Ք/HC' r vfE 0{ʼnphSPH )hJ%њTx%R01ixO8G a ylP_bIHճb? z= Z)nQxA/m@o"WF5H16CXQD9|BR'zDA T:)p`["pc:QDY=qi(`!Afz00lä"a %wHPjCe4q žd*Ϻc9 wD( D1‘K/s7ұwP/($vHH o;GPMjM!9 jolf1QC!XsdN0!PG9b?ޠs|n;8ܡxGA uaρA$"尹ta GLܘ#a b`H#q<#:2!Xl <`Abcy=RpwmUV(B]X(Ηǝx"|#+F;5OM>D&!88"@:RGYIP!濔Í(".D  $GQjXI%Mbp#Ҡ1C o9aQ"};لSD7qb JQCh/g`؂ix*tP7$B0:CpD0"jepX4 a H4[|ppehyF3me`j迀1;X3(I-;FCixQȆh b.BІG CDt?`FA4ax a8@. h BHfuz2:`6m8'"xT(r3+rЄtF5(xMkܰ(#`G`{cxĿ,r@nGsTxIP%kwSyl(u8Xkqڼy [Æabwp(wkXThHj9P Aio(68-XL7bxV+fuX"hiȄ`IEf sQ@h QsPT-Xf)s4(NXliGr8HoptKzf PH,Pyktȃ9%obu8ȃOob7ut=b Lb/U_.HP-RmR(u-@Ho+Q#A P;lH+9I9дp "l c2ՕBQPWQI-99+y-x[3Qu8AѺc@4riI$r0{q^ږR Mؖ AF8":PZN2lUG( ˝Bp*8 ?h Gh?X` ;(G 4C q u-;E; SX@$7;h€CP\ Qo3L=xM9yȄZwt0FuPUOuPD= pPFzX!dt3@tGe1`رn)pH;0fbI72Ay_Лsz™䱘kpyX oZv?̈V3`KzxBPYi|y2`pщoxp(ypyf|:w(UBl(a( SPO3&X:w0i"OtHu`6w&?t:I(jƊe@g,7bV .xPZΔtL/H"Y.@o#SVx(N8*;`ƫ.& zCx8w--kHlOxQM0z(lxAP'軟?r V[M(*Kgi>p*ÀD`/tV?ЗKGf!ck(a cl؄<8k4pkF0*Nfg@O8WdLB8i 0Ggx߉ў0-pB0St pthclȃT8< BLE0sAP(4x>` Pȃ yz ;؆sYz@2i+rfY`?qD` 3zB: EعqnJPÆlB2xx#?!yPFT0}zP|z<شp |R"ytrIԘD/XtYCG=x9]:jR#Ȑ"G,i$ʔ*WlrNt|I4б82PRx 4j%!>EѦo}D?zϜJq(G[:ٌI#L~h'S664˲I%7nb{#,k&47R1 !D=gH;D5I[ ??d#PyCZG;;Ǔ/?NB 8 28" #q1u`T?^01)g,HI3^hf0# 2Qx:cP%   kt]V]NsqGN&PT@&PC6 Bh'0H9a :نPTx! aD  `:y)rbZRqD98Qbؘ)*_|#¨FG2!&A UT&%)]s.N c'uta(!H#T4pX""Gq@b~@gGQa !.ͮ|yN; RhE8p O|#a,4{Gq !ZiZ~"E qHF/RC(񽅉ֹoS#?a |ǀ@WKcPcd*4D E!60B^1`BSB#7 6JC$' txb5&ԑSC0Q9<q^#PBtsܢ0!c":yx8sXG)1,h=L!pq MC[ %Agt8"" uh"&X}+[-O@lG!QDHqQG@.B8`&Avd?( nhH O BAIO(ASX֐&bl0pSatcR8DO>!.M{>؆^ߝ D`dw@6 t$(G;kx#N)9(p9T>qciwP8M:#FX t7Eȃ0; jFOn0|S6Ա2k0NJuA!NR1K}7KGJ񍉘#PX#ư7l<00UśhFIZ{ .hmÃ$y|#yH@B#*I?uyUA$ EH~?#gh?  4y3޾#I(w` JdP#HJD/|@D zD3 B#CuD}ڸ,lH4Hw؁4xJX6y86w7|H` ڇ;$S !xXgĈmHF"ЁKՄHAW0`H5Gl)Xa@١GāqHA~hJtAU!Pu:G!{? =(C1Y%VC1AAt&l51x e>8x:%\<Y3Cpie!ByR=+Fi}1!3A8؊GLC:Ԑ!laeQQ<8'Bz9h8NHQx&8G7QGB H(HQ6t$6 m€Y?̅NTI'?l)&P&CO9-CyË]hS[6˜n3d:W&dp“Yky6)IQ8pgG77d;XTx3!1[G7 CjEL3cBXUv23,v6\+37<Gv+|5 8pH;V4t}I$C9( 1*# 58)#=Cn%d5ؒ&NACJBM H(1‚'#GB6C"W$%C0l/踃)j:J'&=dLh3d5x{2U ~Ѕz@xCVC*"!qDާ6Ћ@5L2c%:HA{DtD)lR{GLdX00tB[bZ5K91:L74!7{$CO Y[FB=|Y3c,(e u&grX9)2$fU(<*8BjHX|1C 6W6Ѐt$h?HC 45՞zDBjS!ԑҾUG6G#,)WnC)4{ԅ.X!\q!͊nEC/p44#zuױg^M.u;ɫ9wt`-5 .еްyjy.LP&С_3 CfɀI,D a $S`QiqQy R!,#LR%e &TLʼexzd+ƶ3_`k(+cbL`(3$i&AF=цNw)nzCfA0jcJr)k~R4ʎѲ3DFQU9஡8;ABވcP{Z<h󵳆z6Ci^άCZD& 8C?=T[v"f85!DɔXw6䈭!:y&]zɛIg`WƝTh(l-T9S2w1G]e.%qȄ' tp;1r!8!Bcg`a-|cy Y y%α@yLj s3?ctuckiy6R1i(c@&iFoJiʃ:` t0HrudcHʐTxM EUsV؄[7RPC flI9bHŔe0J:b i4E'ˊpՀk1? BX5T X8 d<vE9LAX߰X題.8Bh%XQݕhRNaz;F1 tfp!q #"A A-͉lPh1QAwIՉ%u 1Qe hslN u0Tj38ll`Ʒjx+ 0v\¿9a2Ї52d(ԑ%:LJByx¿0𺐍G8l ĐR hdca_mxMTF2 MH([t|7;7!9x3z4F?ܰ:LlsH#QF?C٨HAaHEk[z`MD(6f#FSP ;B$ FSjֈ Pƌrc7PG*Z ;Qs~ ԑ Lƨ`rXߚk52ԙG1u# H? hhga"1E#؄| !7`ͤ R( 2AR#fH#<;}hQ;<xC)ш?<S$~c,#FȟZC臊(n#GlOƌbdҠC6A f|.O1`F*BcW$`=04hpя7L4"xrh$, 0 sdr|CN7>! .շ}o~7џ~wϟ0p 0p!0%p)-jCD=J" tD)(!.h( !\p"o,A!qOnlx|;`r'DC,`j@ axBaCB!! ސ@7ـ0GM &F!<:!d'KE|!FArb l?.'e(*LtP(!. E3!ڀ'q bLA!"D!>$U$2bN2`W!>` ϊ_xbAD(b,!aiADLaԁAcabB.a!TaʄT6g(j Wx!2!UlYA#DA`abANAL'Z!2!(,A4Lm!:!@L&IJA.  !4[jF%t1ߞ&t!L!ż-dnX `:rSsH1DD>!`z 'Z64!cv4h-"#*0!)7L!F,SRbA.&sE9aCLaHP-n R>! waD:.'D%'&upW~'5Fo(R"u'蠐{c 'w&Lإx?@6;Oa򓏧vG˕imq9uyy}9y9y9y9y9G~p_ҍg9) RpD!rsbs$J? ABኹ(z* z"cU6 XF!F!;6L9^? +`Hȃ$Yfj0L" s)@ '~$xJhh.h> iNJi^inʩHDc?xb 3ѹ2hJ“azA&8&llD*x"a Q=,aG  @ l)dKvK$mP@ܐvP!a/)_`76сgZd2褴CWI926cZ2:EF1JSUȗ1A3<' NɊEb PQ4bl!\/ԑMkuC<HC! 5h]:Q2dHGR 8㨁&`2* 80ZtF]Sh c˞L,CF R"?`fTIh$̱UPGP<|N9cZjB4(b}=̴3K,ᾡ#BĻ$Ak,DqE+U;OCPqU?3Ud)dQ%XPH,h0L80¡T)Dqalbt$`bbl"7naO5F0 ԰1`t8Ʉm# u.Q䂩b֠WE`x#js&"jG܄%`Lrl 4< zH"8G BM#xFDqJlnF;&ɲS OZ#_+pDDg$<@؏ y6@<a 0A BTOTt |F bj4#E(BR<ä 4Tij`$vݦ kC@+E4]Th#-9l>T9EszgPeD,WY#l!E!qBsPco9N $:T.U-P0\~&;" .[xҟapuԯ;t}dXUL؇?}P'D"-{GC@G9ڈ|GWtD&x?_emw~5ṇoo/)anj<: PDPy}o~Q-|zkιK9]o>Ϗ~aWψ}ޱ#gXDv'x7h͓Ȁ}U h! ,' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||L.(SȰɁ/WʜIa†kB4gIh^F*]ʴӧPJJիXjʵkEb^Ê{?dӆBڐގ5\ ڽK7e߻J܋pJ7+gǐ#KL˘3k̹ϠCMӨS^ͺװc˞M6Gk ] N{Jμ\[n8سk>-i Oӫ)j#OϿ(h&}-`v/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||*(h>@m)䐏HH&L6PF)"y(cKWZeqEХx%$}c;nyӋ`a2 tix|矀*蠄j衈&袌6裐%gVj饘f馜橞Ljv:wِ8$JP}gJh+k&6F+7"v+k覫 lIp6e+ E?t[~Ħ CdXֶpDUEq^ Qn,$l2b,[UwUDZtR ,Rč6rD:7HǥLcOC}',}cM8XS!R] ,L?hJT/}?tx2Ŀ/?t݄VB, 8?>`81,|>h39,ԝNy`!K8t6vN"xڞ`NM'x,vʮ,:uF;Q=h_8l09骀O\tBJhct, J ?l ! ,% ! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||v0GL`2_Y6DoM<`/?[e!Ё}G! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||,`݁JT#AtWYYW=E'|y@|_@! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||*Lj)aJpB/cp@w&vʩ!Q5Zc#zz !\ϮC@|1 h0ɮ?Π:"|!,?>,P2Pk^`$K}aFR,8n$@m<,sKa"o4a[P`;, cpknoaC.rB3C"A`3M?F,,(b6 J̠ދ`B3!Xk5{6'(q!~a@! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||T`?ځI$_6` ($F(8Y0(4h8țl@m *tM>dP$dz:H5vFG)%MW~)dihlp)tix|IyHA.j衈&袌6WYJ壒JyQ*%RͅY_mA驨ꪬ*무j뭸뮐ז1r*&6F+Vkf-L5䶿X݂k.d*QKfk~ы?J(?Ppn '<$`cf +džJ??!7L@9\?J7f`4l3˄|)@a4@-,\aN,,P,j";\K˂~2JSuBE-Kp?`0]u@_^M l4sˈvC'!_(MrDf$ =ͺ_3/c\?q{:J̰0G -c@! , 0' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||`L@zMf'Oϟq2ro+ϟf'W S߸z H pKWb|'B0 -!\@dt w>K-bF|af"".PK/R(>f"`?R.9FPJc4h]79* .{&/crKыb%.g$uMz:c.gv@w}}".U w?mFxF/V#ԒƩˏ8Og)B /Jc.Bg.IOaF',kK/ tCT""Vb`H7?k*ЄQA@:0Bp`1~|B <r Ka$sWdmj ןlO@! , 3' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||~E%JB ΝO8qz2v[r2ϣGpA[)8!]tT'pmxDG|H@A Ft 'aw]4F.q !"Q]yCO!2H#w@DDd(cvX}%pmwǢI'cCgIy6vҕG ~Q!y! , <' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||<CY ܟ(Slr hbLe@͋786a }~TQ u/hS!G=}:ATmй0֠p,VbBhNl1)ZuS۷s| 0FuCq8Ɯ2@'/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||O0Wƀ=J7><}_jǮΜ?o䁅>MOyRv`< H;LH5Hyx#+ax 7s(~_@DH/']X#!>$H3t,cKJl~hv'B/xutan,%?j K1wfGLi]O@! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||G'5QL"-;:}|!B؄S 1R+:9yxrX5lB )_/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||JL?Z< RB K +3 w*?Z[O04'NKOm(,PMmȷNJlB ? ]s!jl.PS d5ԨD; oLd:8S("S\rq9Msp!r!N+fijJGйM*!Z3FF|AZzj~!}2Go! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||62sG9LE8,koH!lb[뺢5DdP5?Icɩz*U1E5S;Zqqx!Ch'L0 7 Qli  G4ܒl`v! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||ͺװc˞M۸sͻ׵{ȓ+_μУKNسkqOӫ_Ͼ˟OϿ(h&F(!ElLXJZ) ``@XJXt+Hc͌X3G]a0?@)'3H>!V!S-@?RI< ,x7yC7dYf][ G,?x3LjbJYuPQ/\1;ʰE0S1Ѥw4A BlB4s h7XsHccMp1LB|sJQ.b1LXGai,A !bB0d?x?c_^T?lu%,7԰nR:DV+H#y((|@"z!L0AJT-S7(ř+ pC<F!3! ,* v' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||B:m/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||$,JFƜ&A?p e>;Í59|t,if!;k3 nr1v>NJ1tqӇ!  m d!XkG Ԇ $AvX~sҡ@ k KN/:O:圣<4@Ҩ5C'd =D-(C3m|#O8w8CO1x@vBÌaM3Q(eb&Г , !Ç'"7 dL8xqǓI_d=hL?( c hxPf)!yO @gxe"̥AA. a?|? 8"aDDdbHF+kꑎ,49GbTdC#&a9n{0*DTRp|a!+.(H)LQRO(E*FcE* r"HEZMWf<6T5|( `"9`Rx3X±P1<1`0gK0)Tt1 nGH0D!EBHn>X׺0ra*e5!+F0#ʁ Ranrh8"Vx㳜AjV3BRy A2*XJY< Bd4 ! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||̥˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKᄲhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνOӫ_q ˟OϿ(h&`_o,F(Vhfv ($h(,0(4h8fu@<@)DiH&L6PF)-HXf\v`)dihlp)ti睆|矀>*q)d=":F/bL?WEdP.hF`PrTH(Aާ=! , "' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||9Fgvf:! ," ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||)DiH&L6Y!,Xf\v`)dihlp)tix|矀K79ZD\V&*W褔Vj饘f馜v駠*ꨤjꩨR*무j뭸뮼+{IP&X01nEmfv[*z+׎kr)Jy;7( '+n֋/1:5KPq@GD\ KDfsFBQ/?ho:R.2 @G0Ez|QX'q;\Q.C-?)ч@ .K `ROS`5'\TGgDqHAweB;DvI^nkԴBwqf0Q>o ?\P?!bú?>;;?%{y;|̋I:>=7ekܯn9\<[>z HL:'H Z̠7z GH(L W0 gH8̡wq@ H"HL&:PH*ZX̢.z` H2hL6pH:x̣> IBL"Hi ! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||Lp L @76q9xc8&`u K Sܰ PG qNF7XcLECHE%mq4MEc !1c\3!uGH L\Ӆo,Ω e tшF5 i\ FQA#H,!Bc/O`DT al4b!}PP et]b a((㚴Kcx? ;t֘T&CťPF"P&74`r@4A>4!nI bl$`d5†1@bZ8⡎CdPhx[t <@! QDx17Ȝ ! G b0cB_Kl8H;>pcP/C!_DP`9`bX @ C?C?@͇x~F)p fCC9D&` y G@̰ 'r0e )e `J` {P{ay @? @w0 @ pj }v7˰}rT S@M0=[A :0 @` @V搀 ֐{`{ PwN[K^@ HT,Ӱ a 1v` tg WttPopuQ >VN[ 08ڀ T 鰉pwPFz?# 0 ym2Fimar!'ȋ@ p @m,Ov'ƀ , 0<[pX }=hQ! PChyPhq ` `ۀ n  t t<%P P 8yPV `tM a Ї @[P#8?q q Ypr @ ؀ @fByR @Nǀ N&9qN ` 8` t0` ׋zD0JqP ѕ ˳%`g$ Z4 4!N depo'ؖ  J:g'ZJ p 0FG yP!(P up<m}1 # p 9s  zepĠ t Q f 6/)հ` ԋhltVZH  |p}0 qQ'  ʀ' P ߐmBH^`2ФD0KT  S+̰ zi @WFR  @? 999Uxy;ǃs(s^0 q|G} P]g_p[FoP @@3 p`q0 oP q@3T s E$_0Y0+ x%(C` p0 \ySu xBT ՐO >e  iHohAiX7 ׋ph V`cy4;Ґ;eրI` oV֐ 0m6n6oƆХ0Y *oZ|c?JsŅȊ5l r <89l@ Ayʀy Y 0Հpl JY `i`  0 02pw y` ܐ59oPW)v BpwajQ ( P`1  B k# a]p΢  @i@k@ jU ղ;#Е s8X0j0 Qqp H:;[PdT(1`0!ftuv n XlP+' )NC7?tϐ`zP2b j# JYs @`{6R PHR<]g{ GS|}l  4]hvP 'Q֐ @` P R0 awH!l0py іYT^@ $#@`TÄ €B4pX<@ PfFo}A;@ ఌpwP_o C > p  vJ@up4 'FDptP A HŠlp@f0 `8< Edv]@жa ې ֐OU hM"Ow0L>Y-0 ZI-u!N;qt CPf0ЪΘË́ A ԃ@u >  Ua:gXMz:D@`Y `N |PfNՉpH XcV!v 1GBMμμ Tq&yloPX@ Eϔy0\:\Q M@ ߩuD T dTgÎqS` |d >wV]putpP __xu`~ xP ఍` \ ~$X' $pŘqfX@m#9yyP`2hpəS ϰ!H @Pۀ \@ |/}Pΰ 0@ }u,K4apuߠ OڻwiX[  cޒ uPϬՀOM_ "t-V,-:"_&n! mXPLhNHl \dc Ȋoa LDTRM>UTU^ŊMY[9ORUtD٦Х.OoSeO;*aEu&Xd#Oa#,@*'b g5sY GҁTFmz)s4YUL}ٳ!c\.! ț*$IZKő33 _QHōLi#?V<%2CC .C6yĘ`R9&6dQX$IaHIfFȎjHfSQ30YXRFG8`qC F8a#t71":AE)gH ^fLIeQcgfof`)D"i)G3gG]aU9Ѧi9kkgÑȦF;mfm߆;n离n;oyR7 Cijq@:'+A*oΝ}r:b?0dW#? vsVGj)cيN9tA |-; 9o*rH oi%$pRM8p n#w>yekIT)L qGtqP#@!8p;6`}3,E 0?\"۠F.VS)p)s34vQx -X-EHE*q0C( bc2o:6HL y#RA$`F6C-K8BU0Đ,GCcE(:ȃGTM6K1$hAIcshĐ:dD)ig7pku˚"btw} ȧ?򖆜&e<ن5#aL"3"Qa(a#YD0ER" UEFџJA C3x#8bG9бTHEp81ψY!zHBh,"jC̨#wk9`7w"HeEH|J;drp(B,<aID 4&آ"αsApG"dpT(iĺ#ց|FcF,ud \ +3,X¤HĊjhnuۓDR$k;/ qMOPF` >).b|ĸҊ,#% rܔ-p B-xG5r85$ˈjEMTB:0y8 p|‘J7cEpQ0d!mvh ^|0E8$%5 ĀAIG{!,#[2G:7ZE"5Q 50Sp 7lsD!RDP?Фs* m蠋"?ڙ #ssD,x=ps4"o(ǗyxXcA9Lc7E␍ .R!9o{N`"1XH9 y|c]q~h{G]!BN$)1@$8~UU=3dS`"۰_opo)4Mk`X#8]d`*`}^"NCQg\C&7QZp"Rh:(;+-f){3!,G<Azx`gxTc8-rzPrR <Юh wspR/}cv੍ | R9+x s`(6ႊxPpYjzH&Bs(v !cЬ%;@*9CvZ uPfy( B~o/sB BH!sk8{zoiy9p>-itHyX]${36 EDM9Q8 ӹ?P=Y:RAQEC8UAFR?t?Q2 CȄxQq<7fTFJO|KDjR Eb9oܬlRXE`B:(e؄g*o2.A0ײuE^ɞɨǯ=9ʄQxZGt`򵋈 tp9b u-˰KQT`iaS9ȄE7LKNG%|z˿L^4DTdtDŽ\CqZ9LKΌ 4μ7668MRͤτK/}ÊQ;.UQ8gR; RN!tE(/?Τ4`u 8Qu8@J;Ĩ"L h4iT8 xs(CӋ sՊ8U(z`˥0)>pxPʐ@t0nD8xi8@Y-vHYaɑrؐhQRXg%hPSpb\DZf4>Ik`Hs)RXiQh CHD iOG`0Gj0H؄Kh`-``ljiX ep0.lЋi@!)˄_q: !͡`@,Duؚ}`oQ7PN0HbH?;L1c? f_iA``2>Xj Hx5GLi(OtEi1a`eܩf8Lh1 Mg8 5fGu_0ű`@a0sh2ee Eh8ܮMH:XS1MlHx?M-A3cm(ʬ(>s0_h7g@ueHJخrԋڢJIgR0Ai؎b*/ r8t؄YCguFy[c {yiaG!15+U/ 7<ᣰ88QXGp4$K:H(u pJxxRk:bfHF- )V=<duHx Ż1C SxGHQP78QpBЍFfHP-x&z}],I8L< HIh3. E&u8J+ϡl8޺%bpˌ-Z?;af:˃0d|ixإ8QiphSi 1B uQ QïGro@N:= w;`y' Dz8]3 ]9A9`{}Zxh` Ľ7P7>blPR9å}MR臢@H3R{&w.iGݟA  y2I0$!zyp0X.2BG?;Y2P̚sdR&3j2EO55'H+ 8G92nP!>G#>JmZpYJ(u tD"unkM/5-?B\?RmBn&z (<45οD KҖTe v[Br{>7'vN"@Hypy8l5ʔ6HM b w`3[0؈/V@Cp֤?cxa#S&!ɛcMj}cMP$  2Yy Kd wP应) >y‚qAb0\Bfp PF"yXB&ذo$Ck>|C31Сb$5E8.р; 4~ p3y~F6XA4;*Qp#=D5q4/;dM 2KC8nCd \%.#8r/Y<1tKΡDXbT2ęjH03ݬNmX;t03SHPlK߰Q~;Q.~qI G7}D{Cd$*s9hF>( AqD#*I2GV~C 8ZpB5DxD&V9HX5a#hEhRLN;4bEF2x;7HCKa8yd 3(yc}]hAXăp@lȃHX;A<#;6h,7`PaBEQVt)ăSH DD4TQ26Lu1$䁉g 8GPuCӅzLbf($.X} 7‡pw(zJy0#1 e$s9LDtX64l) gXcq5Fo1|J9,m  `=͸p$`j$0F6QQ\01qp`fK)Ә76<(ªB:?sMx]"t3L1+021X|KxC&:X$_07xBI96XkLBBLϬ47($C픫_:=C8|(C()74%X@`V$&XÖU; !7hB=QP|:H_Ԅ%C6@&O=MpCTCnW l-78$Ea/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|| %Tl怈 Q8](fU`Ufb}Ԙ!lSH<1ԌtHin PCtreX4Ҙ62sX 5ʱ8#?Aĥb!ΆB0Ļxl/e0L#PD(P;L.t#‘cA֨wD]nP%hq@CB7q+Dڀ<<2'X$Ra <%XDA0`H !CX0GB&҇R84M!L!SE 2 ChEy(A ;"^萉T/ ?]u0RAI= dayC* ?(!L? 4.8( L.9+(E*.a5`p/-C>ŦW&>.$_G' I( !jQ O1FVb7PS w(*h@wCI%XhI7BDq"NS!X=h7hL؄NP(1SVxXZ\؅^`b8dP y5G$3mV0"fblx!ߓwRe؈WP #ň`6  lk?h wQpߐG! H7 #\{ D!p6Ĉ `$ qO0 o pqYG1(l(Mg0hk1~P{1 8ql \1a@ '@ 1` [!bh y7na Q' ʰlЃg oP }V]b@@ fb,!~4@ `@D v".axf& ~`@w1@lX,@1{ h H@_uf /T"i;Fm1 ŐlnH'~/J>x)O1v6!P| (srHl5P/P)s oŰ/'&^ @$| sB!ߖ0=h-Fl[ `{A>2z\Pcd P{.?Bـ#`j@ llo K` @t 4B Nr[r`!'V">k#఍lbW%& P +_w?!U'0upwPHv p C7Ұ/=` Ed )7 %QBA ?f0(`PA ` / U@` S`QX0@6l5m $pHh!  *F,@"xpNk) 0-πK{ `EV!v9ڃըJPA(6k0Q Q(to[   ^;"  ҇:v0d2pMS P 37 0/; 4(@` @X ۳PBBPy@/0 s l`q0@u: `@ _r.3` HJ P] ҰW  /bqҰ0-ʀ +ǛqlES ﺹB zp{0l` x $eVqZɖ EaؤMLC$ru]1ӈ@ ae1)4rD ¢b PRyp y$ ΠSOa Ґ Df-tfPwu0>SH3`  yc?P p 0 ` gaz=@$%KaV bÍ-Hqm ][0 y\s NT`uA V @   ʀr}0ZZ/B `KI |@W BDe{Cbj UO@51 ܀OSpi ^`nL0hѬEqVd 4,L耋 D"x{:\̈́Qk` ge^K'0kPt/!+!@J4-Y3>$ P@@t0.mW5AоРSrlە 8% b9aH F"95 p00B,i̅P*Ꮭ "0x91kjM#~N7%@'puŎqZE'rPWη\u*g1A ::mP٦ ]`1sb|Vw&y-o-әbkۂw pL ),4a `K&! Dg ȝݜb@ ,)vs P"16sP ̐ >^~Z \j'3 l} %a t Ƨs . e&sZdaP! zt3b @rou&dE } v"h yP6ƆA &Ă'Zƥ8e6!c`6s0DQ>瑢vptp` PQb tj 4NkМ!ԭ @ P  m E yv Ր n` P`` +c p):{ p[G *Pq ʴ01 Lހ  0 Pr1~ Ɛ @Ci@.`RА WaWt^񃱚oVG m@ 5d Po~; s bw"w'd^sp, T#? J H o | [v[O7VlߤsP-vPq' tw>p w n! @ /rP ԰/Ġ >@ Z@ A rƠWš_ ivԓ iۓAx  5 @ TEg |mGV ,@d>A 0@RtĠfy3qP3 pR_# "gqP1K@AܟmДTxC-տ8DCQ8/ľ1?n#'$$/T= \¬!*wiFMT ֥9C5'-^b>F-5f|eۡM)^+> QVKuśW^}X`… FFI0i0g[,k|RnO@ԝK5!S9,1s 8Dd 7Ke ,pT.2b YaKZLQ.ols(stw4dp}Ix"B exHuyf*ːxb3&ycRCe .2xp(mz&x#tHKx G9e>l4t3&!hF) >hupA>BF>!f BkP}k0.lF1s6i?tc琄1%݇1H&`FT pe8)ǙCSxg$Ƒgq lI%`2irfXyyǔቕy$Tg/KHH# E4`bjCrw^np*;u R#] -SOⅩmjW3jHSFWgB0 F 2ѱ!@N>$IK#%uGLaZ7bBMUvD9ׄR%S yv0](iT֫MEm?:U !0V@84"qzty+&Ext}H˘1!m"h9|b ?( |!YQ,bB6ܕDBP5 8 b"HuJV@݄8<3Oh٭JQC .(W*arbJ5.UQZ򳸭5CYuO}?KmgAvvez6'q;[dR ,b 0#֘=$z]uC`Gd/1A jЁXy:z0 (<U `;N& 1j fxC0uXI9_4jU^۩qǜQ-0vڍ-PfC،  5aipS Jp ip, .pI Ex.@hC&X% j0(=o@ >*"?53S33s23T8ق 25k8*K-A (q f m( :klg` ̆p6ao KM8.@q@Lioxo+%XJJ{qZ`Q(E{lvȃJmxmK((Gs胖@ ){?2S3bs~3KDZ8S>ŭz1cDZ\8-?$*R\7X'T0 ( * ȃ9k]t|IyB.~Tr؄SȃXJiXH"s=@8f8fpG9`,0 Dg/L0s#x#VI4XȆ6g ,@sc+[Va ă8DPKȄl؂[>@70ҳ1S_>tbԱPdc9۾'vR&I,s2~¹EhF R@j?XR\528@z8L80˃72Q8A>pM= s8" #%`Ch Y÷hQه8[ŻLd\*Z2 mыBKR,5#.U\0Sq ?s=\5\UEWk i}<@?? 8zHW%ݬݼ "o2mY]#=UҒ]eނ [x%^# 2^)[USѕJR>K>!QF ]%&a\EEK?4KK{߇b˿ {8u&cQqu?Ľb56g>QWK[S3ӋA[`J}M8\F8W1'=#F]Y?9z2{}-̆rC>S\E N3FCb*KdeӾm;?K8Q[^˨46Ca)F*#dRb >Lv%K EQ&9bUE^ a1n^dҲc,cjqRa5P[>c]W\C`a;Fbn-7ZVާbb]Eph_ovw6 jExz6}~&6.m 6:k(TBPE?-m';0Cy uR97FM(CE2y|`8 ;XYb?T ieߏBR. a(u TaXxhv[]$Kb ]ڋ9'b'KZ9.dT„2'"$&@`"i =7TD^0ݿ9uGE5e!$Nȃs8Nj 8(/0a›mXPƍ%*HDSC%!FP&sBdm TY 12QH"o;2 \*OfgQ9!D!utj7qpOg,X12k*J0H om\?հ33~ 6ē%vrЛC[n5زgӮm6ܺwllYX5!U+Z%ޑ(DswB\7(h^Jsd E4?D x86!XQ?0 G:F8~8+8;8p u6}:FGlPNldG;|u뾻aëw&nlS52,(b5P Yvuw53XsNy9B9D6KT#mC*eN5Fvo-#Mœd1``3>#-B- 4aHPb|=5ءpY槄̐M$zD=CS|"!G,bBVoI2^oQ]xѤ C9шB.#PG8B 5GP"\3(] `"qGY" jldժH|%$D0I#H1 g!BE ǡH$J&GxßpLXa5CA5J#FTDvdb"(]S<ҡT4(GDA V(A ba3%hOa2J1`Dh ;8(2j*q:XCH57Q#:b)2D =r?&O0nRGT@VmHR#`=qEPNI$F` uA)QĸGF:`DӠIgj۲F0!s'f3'aHAN&0EJDd^f& ixC`iJ? D'ĀOJKV^ QN~B)iJohycN et{DCaII@,^?st{@ǰ <@0:n?πGtQp,RI0bA bĢ4N;BWب/|Ta z8BD#A0̌oa)1 ;Q;G rmɦ8yc%C6a)Pk8y? :X2a M6@q"q2q GGq:9+`HPu1E%M0nbhDdҀFz'1cXc}:1nr|gE!tKnT0D"boW-'`PVFWÁ?yÑҘ~ =c3o '&jI@ ґ-A.<`Q;0l "-2xz8 ;|fYk>¹|(:.gO@eAD6,+"4P40_C4p"hd3@V&X!59TuH |`DAJM4B9K46P?<``X:ClL8 tAl&]8pA:A8B6AL L~4C84HAB9tgiϖ8LšN0?C:B%5́(Ç@Pá7d#ؑ5QHU=8E:JED3ˆϒ`)d_MM#WddE4h ˿OGj&FaPÄ@hAfToPрFF0WllF l}#j>'t"$܀Oj&lXGtMņ#E'y'\'y{'|Ƨ|'}֧}Ӏy*M~"¼IpzF:I'\ h~gݜlS'8(!mIvf~(( th0('E$Ӡ蜘m\In bhbFTh$dyK:} 5L ꧔_`0FjQ!(viMLFlS#hƩR Ab4ƥVdC4:FA)d=II, @ j)QiBIȋ~jD4z,jkPIe@ĒC)eIꩿ|JRc ֙b)D:ygBVɟDҨRe\V;(ҩ_*^Fުɪ j뒲Us 6;H#ƨrkXIţ苔0tȡfTjfPkd`,>.Y2뻦l$FvfmR>ƙMɴFDF*Edk*k觓n z6 Л`^dΗlRIbh)aЂ]Mj] ͮa m jh:RM")F~&ƚDlNFf$ b|v L-Niv*PИ,mSb ..v>9q+JmrmNFj oØmg( $z- B lNjNVvVo,&j0'/07?0Gpt:D*`CusoF́1(1k4ep7yMd F$PC"|mD4D DBDyhڌ#@(l/Ɛ68$PCwF&< QC1DMӾ08Dd ݉qmۭц"pDl:ıSl 'hDԁ)tU1"6Goc8tA p(0 \1"|CyF\u)m\3@nF`!S1LJ%Q\i*AA˨RP++H9ܤeԲX:47e,6(KV#D<{ YT֓45Ѓ5ċ!U(AQ[ьe(?lG'`At{+L@B:  E<$sKD?3B5]0OBp6CC:8C:Ci<8D&$c1l"2ā\Ieg7zia0! (d+pA‰:45:CpÂ2|¶`Y@b`CAM:B{C@~/dQ'4ha N#IP L0$4SqJL9T?tSH;XdrBPFوbL0wxTv9c_isTD1K  ǔS2ӿThSjhEt7@o?/pT\1'p&;zuױg׾;Øۿn-v#g:p]LJ|C9b i瘁Ͼ ,LPA%;Qٍ>1NiA Q|D;QYlaQiqQy R!,#LR%l'R)+R-/ S1,3LS5l7S9;S=? TA -CMTEmG!TR0L(/Q2 GJy"Ra:\)6ER+(֊! (S(Y Jɠ]{JQ_D!^cT'Fi)sRHft0(+KCm`Xm?M aΡ‚QIXyaH`$$`oM9߃w_"ia!G;k iQ1G`MP!G1/`&``邅r(B;x7 )86a%K!Gc1ŀH 77Ԋ.KB(6$ BDSnŤffp<AYfTj&H,FdafFCpn,NRF9 k>ŋ8bA U)i7DcM 驁6bNە=a'~ i1&?LHNvhAt؁;,8('"A mxCÖp S ɵJE9рT 0Aw?9TQcC ynz8<<@+sާ"8(=Ȇ5 ZHG9cCrր:4хo|cHG0< .`Na1 5cУ1 ;A z.9B bq@m0ȞH+#刔`x" N BRPNr¹wt(J0,$_H5M u5DI!]F1;~(VM` !F941؀?ps1)̋?aHG4_A rL VL @ԱCc)|F5A ak@Zd B4A8FiH[*Im%1qД +VwÆq]"i T3E o̵+&43HጅjFLGtXbG xL㸪9+,T³bF& !Eܠ r/tbWP-zJM0_IU kHm8p1!}q ̼6Law?ȳ`-O"Ȧ}= 6JfA8} Ѐ(BOMva ƃ5B09qIxRc: I#@GC+/"! p{\m<"(Xùh)8DD4/%1W>yXm9q yH*Ν $R:fhǤQOIVyJl8YwKR/2aAJ`̸1X ! n`3?T"Y!$fA }UFc QEE1+:$ $hJ **a "4@q6hydE8 n):iC,vf7 ?>.M@ao"X?҇-H`)+% q:A0r"Cp`CQy+!sD9ZzyggnQZCQF(BPcQѸO4)z"L٪ l 8$X$CY!0%p)-E&(! B+DAz ,^!Vef!EA,N"o "!x- WFΏ `]!  ݾ6#%C .H!0H!=A! !!%{bn"T ;fo gVld2(k]Ag!J!,(L!+bn Q`> ; !FΡ& ts+K%i>!a` `aA_ aT&-1FpJBH()aJ |bh b- 0G#!e ( BXtF A=2@>B @(h[&>%(āH<>( 02&!A a@ !6 lz2<=aᐢ@%"7+$}gA*J!$Ha C:|BSCcrBs5iQ!! 8(2 Jf@to 41sb*s)`Laq`[1l>(so'9L5`*TPʷ "a #{)q "[ b|S(c"  Z-! 26a/y!4\#[/`AL!BXr'ʁA2(n`FA^@] @!;A#'A >b! UAQat!X(DHd@| CS`1Ibs`YE uv2  $6ˈa9 bbp@vX  ҂L R C2@S$ BRa 8afo,!aÁ†jftP2audVaztJ!c2{14Ӕ@k@ `N A_;"Dzj+EHXQ p~`Bwize# !!6p#^%iF@ !! ,Bu,V=P^~|} {A\ f Ơjq G^֎">X &! G=N,aހP Bb4$:, ,c![a&5\.D+L!&@qH"< aAB.؂;0… :|1ĉ+Z1ƍ;zHŞ1c8WfpaV-ܺ࠘^/Su 22 @3 yotO*ö2>lrNqJhC R@tѷ<[aiAyCkrm fl|,U3.G,w 6b{ zЏw+){39BJmS΋Sp}R7ib梆>˛?~(>BSLe͙c"!-PITD,F']xpN:xNC?DŽ|D%:xn`JcG3Lr3AH)47t"qM4#JHG0B3_X$Ԡ|A֤FPC?"q?sUyHs&քA޼ BlAv3I͔s?`^j43.4F/񤟁bG/bd  J2?"Q aV~,7M:| r:XSV=1il) 45XCOfİ5tr0 y۠c@ VN8  :_ =d":P +$r/:x7CdK'y _VR5[<gO##?N8(woF)ߜfV+ MXBv Ƣ 5qeb 4$V$uX#i0( kȖd|}w<$5iacx?2AQL[m#䱐9U6 <@#C!b C\3o@}4Uw/ySîӜ٬7xЄ `h@JP!Bu`j@G? `p3! d`Dʐ-p_8\ϡ5 Q(b?,8?jp*v :q3;HAeٵ pq `rsb83癐u2󆈠 *m,2̥V6!XD蠪ApL!U<;@43`(&K&0Đ_ W{p݃dbG!g 4F5(H8UngB <7͆Y6lMgCɳ@7Ї_Bg5 Dpˉj8q/ rt#^O@2A ~(Q"/@V$k?['66qtfba[Kb?~hxLFwh|a("rʬh{C"q Ev߀  b!q: ` oR ' 8IgL `-V`J0pVP ĆvV D p8` ` 00 "0: }fg~d`g^gypW_~6MvFf[ScH Ѓ#Xkp#ް @ a0' t&OZ3:`$ :! "1p '{ApJ @Vۀ^ 8v Pw@.(_/F~(Saf9fwfob^ޗoi&_UY(eȌͨG5 Qg`dL/xXu$v n wnϴMqphgN\G+^ @ #&  鱌 㴐ɑ !)#I%9Th)d c  :Mq>! aĐlѶ { O ! p`V&ePK9t G9̀V i( WQ{ѐ ѓ a m` @t`;q8tPv(t.% rcFT}؀ 2H5IGt>&01 a|Pa(b U4}pAɗ r 1! 03 p~Ǡp`P `VT Ϲ pa R pp  P0pyq @5 Bbħ P ! Y0&#2:00)<6Qi w{]  Р8WY$ h; B %p I[!zsq?wd ` 0P LA` ep ܀ P |/&Uy ps1 @00 i W!lU ' e0, @ Cɤ|@q̠ z0 pi @ 05U P$pyʯ!) 1  oz P !lឋ B q pʕr ` ʀ6 y0@ : zp 0 u` s .sp`s0{^K >xi8+= `۠ x  X!(@@  ;K/@-` 0 -14y`Y˻ `EPbD#İ@}Wph0 mQp P ЕՀ |psz\A  W ]P1b a= 萷a 9v( 4Ի+܌ ~I @ s  W ^ ` r9`dsP mP 4^vP ǰ Q{` Y&9 :2  . ' V 5j :D_\`L@ə`Gɟ }plʧʩʫʭl5 x2%e5xLqo4enu܄!o` f؄,͍Q 1gw|l)>۠ @ Q (8~ >e ! Divꈵb7S:^㢮@ ^dWh` [06xeSP ؀!weG#|o>ޝN짼 |@ rFvt0P\ Ԃ < PDUtQFmilM!C&1I%8I8`b<ō &"B#`&s- a#%+<֊ ZõI.κtt^z^|%Ao8䘅~?@+3!50`5&"8"x07+l&!Xs`D"ldLXC4Oa\ވ9.NP#y栈̡sX8Ɋ 1|*: Д``c5qF bdC5ƎoXhF0ʁ b?xP ?Q!r|+H9RxG*p\;l8;T! Va`XpEE(&wb y  s|a38`Q 3Te >#pF4@Vy({q6!!0q.,PBkh5θ/x=]k#7d29(XаiЃx0S1N/)JaOdMgAD<ӅxG!>xb`2p q[Gq YfjoyM00QEH 5bljb#W:Q.D`C7``l V1:gDs^8@)^3`,v8&s`@QIB8 TT1Ę3VX# .?gLV,h0–l"a:Q:(lB $#}.Z膳Oդ51ShX O|A6-ψB|N!灨yq*aϚ ,I>(w-g(5jw$ȏ"PcpAΈB]ڄ%s(,L?Y 0t& e*:* pD± XAC@W)]e$_!/6fNsN| p;U @+_kNf=1 E@8S|mXRkHQAx -ئDxI?N`-;<Ш)`9ے S$zh>a, "CRM8-B M5خ8p#MXMpBP5k PP>نЖ릊6u`]l^r)M6ϊ ?SGb w6xxrxy`Ca;>Hp&a ty0N,2XQ0m(j)txt-3\. lPoy3>ӏ "nXIcXl2o('Pk † 0?;B8>k@Єwj.ClyJ)uy*QTH?'oPwhPLͰo2SPIPz@Dms(BRxwD܆oĠ;E{R(܉7TH9L@Tx@pCXDHSО>GОvxGR OPP?`]Q GxڰLK!+ GGER JTR.Xia ׻Fx`DbH?X9x81,Mr؃`Z5Pl" CCSb( Bj0'8}NH[ĀVL!xXKxgo8!{tDȊTkG?@9r\D9oYyK%F !Q `e`HIgQp OA48$%``(`(0dza@ $EHQ{ G`qFXbH́uG:?p^G@>;WÀtx٘ObH83QɄ_Q ل Q)zoXyCiұ8ar`͑|[GYНaa K2D*5JDu Aa%sw@,*Zr mh`+q5t@kȆ`i@ PhY/ XखPƱh `yzsHnpwt7CPJI 6oZ p8jsjhTز]BႴwS8pL?حTPamAkX@tЪtP fPg-~yC0B`ޣlfu褘uxO0NfK؄x! ! L,;爆h Ш-qu DL L(.pТek9". "1*7㎰oN/@T0Mhaes31EZ>(l8r QbP{bN:?X"njVhky8YDhQ( ՔpJHj+n Nm/JN 2`nR 6xb8 ` يPRbhL*6и7(؀u@1z NvE)1Gtpu` 98Ȳہcxl!ZkJzcxe/Xx`aI *">p˸6b݄tS>xDDl*o h>¡buI NVj889Q(-pfDh=#p`>3apiAرj iho0< zʎo"NmlA8R0: ;5H| b@5 I?aߒai 81D;Pc>N!Bxg-!9sDLk IlLw0<w@}i>;Ha `Ң#P+XjQfPyx.FD `> A:-ps8\G/c0B8XZ?tGJk8wTR*Py-A8` tm(*0rXIHΎ d?(Htx!eHy*a$,*^Rs1wlc;o(tBtpt@d,u4j47 c݊ylmHF JRp0tPLw(cwXwuݏ phO~%ʄLXl4GjyEp$ZLEl)!hA)D0fZRV#C@+*X@~@S!) Do'p9"D I(ۦ]`Q  i c< z}$aR!5vQ !;32 ^z걄PvAlT#R0C&BMS"SWU)$:$͆EWR0JCI{D#Xr"Hll4"41"TM8eb#54aUgz="5X#d* %0֔0Ϙ?C KX#A6u;tL mSʀ^wBt&H<eLRa sdz?SIlb[`<?>m|A QjdtʱR=$uA0# 7l0@TBDQ9ĩ8á'MCa'Asl aR6<1 ңVPbhPP7w0^gL"10;%˅F&st@nj ¡ @ 'Pt!ڳ5z\s0_ `9[@[69UG*0eDwaC@'r [0)cD/愭JwtBVUE;yR=v 06Q/l}:Z)y bp}@PB6b<ن%3q"y5.^p|(F&stl"br>8 FđfkE(Q5y"F P0#X=H$Ck8Zst{C9TVxE-,o8`"lH ::o@9†t誩, S"l'JA tk`sacVf9QJ1hX#}*S$Pƙ7_ZBq^ o?~H!cA}$LЁH%L?40P D$ B9 WxS}Lm@HC,<רٌDo0<<Z93x 4?^87]r ,SBl7 K7WB05l:x+D(:aLx:4l8\B™=^#@lA=5HH4L1lCX2<AQ"e"Le" : ?D|^|%:`A,::7MhCH,B:!%R2M4\!+帡7p@<24(@P7!7xC L W@? L1@ 0H"*"UȖ\4V?L:`EaWK=)'u 0UI?d@DG"@! XdYQI p6<Q6@@C8tDI_)BX /Tā5u#C'M6A9tA$ĉO,754GB9(HD"|CL ?EVZ: M9AE h<\BJ(9lN6\$l D4 Sԥ$ !,J&C z02fKЃQ4|C:˙8 D8X 5ȩx*tVxC*$%(!H;` 3|^B3\!D  ΢YC3#E6iz" ?S@" DP7U, hPF<ž;'(d]̀BHKA,(\?p ,#;H;P3\B%nZ)1\3؇1P(dM9;I¡;T۔†$63lZI2VR0߂31C9CY=`C38'Z4P1p&4lY'2"Ci<0=7u<,Hxe&`5@<_nbe (æ<+<3F5:|X0:‚((C1X443H^"2D50mC,lC9t'<9l7C9C@j4C*34@Ս! 0O5U"ăΓQ@0Òe. 8@^5Ă@,1()"@CLEAu44@`0Ph 0@OYw@ښB*]4䤊B@f 8 :$BrGFAʠ5 B,CD3.40C84X5H( G*dD&`6H@Y*pBo8` !4Xz |A08.4xTop.KY( 2VC5X1lF$ 6J&4038CQ2:7K{|/1DI@6T%4`!D)`p:L7RT&3<21X#L5Dj@2|oΔf^N-34l2D\2lAloKH"4`'l|(B54\.7P:|!@C"ĨC07l,104 t7Dܕ7p#D=9EkU%A[DjCR@#e]3쑲:&J$@ rܨ7A3Ր}Hl]P(?A3C6:]r؟:>?A<l! #(ȝ0%.g@l A"ArDD[ŋnXbD)VxcF9vdH#I4yeJ+)BҎ:i҅cRIN;yhPC5zG+b6*I4JVzkV[vlXcɖ5{mZkٶun\sֵ{PCLE0E%'C$1e,^T6!f#Z0g7s9tU6NhWOsks… фSnDHyj8aKV< @\prRIQg7;d]>b/ [AaCE+n&.$ð1'f : !&(&:!\e 6,Bsr2%EXGuI h)'fl+~Qs0:&D vٍ1tifu ԉ' G7N@Gh$$'lˤsڻ0$S={# oJrt2"^ ,&?{ʜ<ҠdHY]YXjnpDT"jQg٤DQNgy-> ` 1'Q䰂D%dBcySC`Qg"YrBIQDV&!^ҿiwVխ i$]& 0q&SZ}tׅڊx zf&1o#GM9C1e`g`Y_T R3 ^f>Kir>J2u,ȏo< Qk5GnT=g#2=UThNZ5JXq3w`ő;°ڜveSkG1,hJl.yƚL2G@qOu XK(3BPA52k$d!!p:ܰ9lo#+G,GnH(C@ZNHWDږ›hPrA;;RL5?h='^?l|`4LbHظt p y|0 K ' QCd)CPHAꠍKQSDRh =ˢ$iR2*xC;srS6=LUZHimIEJ.JYy`8CшC!Ą!|0<6y0(HԠpC!Ȅ:A̰?q$(Fȃ1h^D9bc"x#kU)kr;Dn3a=k7LsDH2Jr>UF@|Kv+ذ&&?b,Ha Q``Ft?ld ~A bxC~ lȱ ?crMfDj34\3d(UK?N^IGf)}FWPUi 5تpټ`nxh4P$!H>%LlJgKmQh[[v#"JmU[fyś,. \>4]^v]~5*`5qopOj^*']!~}T0pWRȠ )2FAd`[JG` D®)^jD#Ȑk&Hj QaY&#rCYUqbïwNh'lf=ze%UQkƒS"A& '縄.Rg2ca¡B!T!r"4aFh>Fytwߜ"p.Kx/Ӱ>2$"cca D gޠ}|.@l! 4aX2,:` A<8"b6 @TrP2nΌf#tp ŮlH\qO$+͸P+B6c  n~q!@ Ձ˖ &&  *& H. ''"ʋa޸!r{t<!OR5؎Dq&@!N.Wnmzr+# @#@=A` Fm!H0X@ ! (cJ %юK"@(X ڋD^INP׌i +㬕2^ [A҈V N,-R'b4\"(:jbFZQ-R/-ѬRZrq/0'112#S2'2+*RjT"j4S$4iM# 2ȯ#/P3Bi.,C1Gig'rR-s\5/ooF$#6CJyS7 ?9G:ӫ6y9 !I1;ײ;Q# 3{.X#z"h?gS?#+>e %pSB"4oDˈA}3#Ar6k;Nɯ*p VEO*MN=wĆrv%CSy>Tyb&FR'\Fv +~XH.Ų?f Hoxn4J'0%G-GOTT'xHc1;,9R=L!"i?aD΄\NDIvXtf%tgŒX>91t簘0M;M\CKJ>`w Kfg # \jrkvHԳuEJ) YGF\.rBI"N 3LH+40YoO;۴P7pvձbIITůk/[!6džp\#aT\itD=a@W7#bHz$wz[BIk3"jUozVN SDBoAiU6>'0]gUj*Yf"(4WoXA 8 ktP.OOdKU- $|'ff6ߘJOyC{iy3W1*Og W^6`~f)N. T[eM]]O rJT '_dj+PnaUj4Xo6WF U[+jaTHWMm ^6|0oZS ktҰ7 gkdI2Cvff֌^gYLvm2宅_<5V�EI0<X5f]I!Vnc[hFfZ-HqSOv*ndDzA'ApIFPufEQb;}Q5X2撤D|?+$xU˱GI*qdr7֣:X1q/nlW8uaw+S8S5Ya2SZW[ڥ_cZ/" a(# !af,Aa$ԁ4!a+l 8`!M2%"ڴ:36Gךb@,a` !3A2AHEDB! ,A "桨ښ]AЁ <&6Ĩ!48a  DaL6K!2ad!KgzΡAdaLr΁6!J!aء6 ! \$&;  eLoЁA!V;`A!:D+ N An܁Ϊw:M聮3@ Fq9:FJ`.&!ȇ˜2A@ ~`*KF!یRD!aJA…A8l/]2!~!XR/DAa) 3#=DsR!a^D=!zӇ]2؏ٓ]ٟٗٛڣ]ڧ%>:ź۫gK"n1G.KjKVB4uW~h& iInCd"%f`L~0TG7Rf9)f`sXF%G-L+ L˴C ` `dO3cYD T>rB燞%SژWY$nW-СQ!P΁ &!!a AJ~!! Ma ;Rz%oS?+x2ɮ ޠ؄ $ƣ @(@ M` ~9wwxV]@#O s"0*a# 0^l9̀9}0tu5cӳqtL5c S"ql8  N8 % *\XPA<@#2ȱǏ CIɓ(S\ɲ˗0cʜIfʄJd"<Ѧϟ@ JѣH*Zq&EKJJիXjŚsׯ`ÊKٳhӪ]˶۷pʝK]0ޘyoKÈ=^t/Č378q˘3k6{cM77^ͺy~DxwEɸkwKiun(\ӮKNd'BOׇW ;'wydn"xWO춁?6wfc9hqs=y1g߄Vw%zVwy`W QbS4OIh4ָV|=1qzy8\=:AD1Y(Tjz_z=%owny6\[vczUpz8_Ov7$F"Pyޓj\&(`ЁėcT-馜v駠*ꨤjꩨꪬJjm+Y!j뭪1SήܼQP#y47bBs8`)H#[8 1k 6F#( K}(qp$61˞4' 3ڑ 0 IJȬ82YsB ,{([0ш.#5ʎ$B33H}4%(5qKl0LS&CLv"UL!"#r͊7[0 :t%QS=Ũ'SH*B]H0ҌA "2<Ӆ|CL8%3/LR`2I0c"` 57/<S "N+xΑ"q=~6+*mM*,6,1v: =HF!oh} 0aA# s\ 7| P9 hP!wp#w0;ԡQaQs4(4ubjHaʗBy0_ԁNx P* j$ F fd"P&(rBd-HXm 48!p8b7 t!ae/8 b71 f~QB0QgXG0P ja~?VHpA  zXbH!<'ftB1OF8 ȍ ؐ1БM ;bJnH"XG[f8q>tEM"ȃ:q@v3 ktA1Qx#7<Ѽ$262/bP0zj䎒 ߘG'DAB*aMCN0E3?|ן0G, ž9oBަLC`0! Ke /lU"JmxFm> IXCK7&l|3_! o'qJ;};6 od͈'цV>ǘ3wK|Aa 8i+Dd7: $B.Cv[ dBd'B kx  Mր6H($a@Ԁh& =uJvpU @^  P^3 ! j!Q pjX0 0^0 ٠ Q   ǰ90 wbP $;0q` pWg q* c` [#W ! ̰\eKXpWǰ PtO :]`x0 1h 0d`wpupR  .P]`@FP@!X= ntz Vp\ P  ft @LT 2m "]@pH ` j !O Q cy0@A R߀ Fdp }sP1։t31pw ؠ! Ԁ$yT  APu̡~>0 Z J`|0JՐ ("uet0k  9`   ] p\(8G'  GaG5hn I LJ HdHf0 duw`Cc` ; Z) X a pZœw05`oVpސ   p KS`V^}Ea 0q KZ40n bx@{QP 7}  *+lX `Hu-:a=p QIkJ:pq`a]9ѡ qqs@y0Q 6`;9C '> dp $uP[@ ,G ;a@6 ]` 0!l @ ؀6vYw ZĀ U  ]p'P | rPk (  <1_KP`7 0 @ c @SZ_W9@P>v dY 7;P9^ w_ \taL+1+t/JA8]ᰗ `dh@ QPP SyɨZ~ >0Ӏ~SP(/ mM0 D/v0PH$H G,iK5og A܀j c"=E@G6}FT 0 X@倸8`R3,d+J@ V14 }p.c0LKMJ` 1@ p S  P r 0Lo9ǰ.o %s`o ͽ4^6~8:(p2ta2Y)C}1mP)LnHUvP/a3^vOWt 4044 |܆! R #A[" @Hѭ|+/ /ѯeV*1u ְeaL Ps*` a"L {%d:T P "̖߇ ` II%wмs p%.Cs`G `a+;&a~@ `~^eڜp(h2 Z PQ2! @iavPy^4,sd[ P !pC R ӹ9j P "Vdpy43 (j@0 n k%b( $ʮ  5 ܐ՘p _ Q1_,o fYS'P2K_3PLk @ e퇸v%~`JO€xPO@-|0 sƓqۚNu 0 zG 0՚W p @լ & Bs~ƎOnHV lP`3mHU9h(&Ny#J砢?De'#A$Q?3iRAh*}Y醝΍\_&:<1,'SD ߼rҹAaϼDR3TXe͖ŭ3k#՝+#uʕ =y˟xС ŔRS8u)ܤpޅ;.ϟsvIdV<>R:k7:;Ig:k6v<33ÇG@x2Y#ٿ9JSFHZp3AwY}w|stQB0k`&>s¡GS r'Sa=kPd/X8(5@ MLIxѤm)ǜwxZu!$%f⩍-Q2w'sAb6R2#ΜgJHU#a:YT͚)E2tC-1ʱ3l{>F9M@ǚt-*G*gCg`Xid?єf,;f2wpD(KkhhriPf Sr1\1eYgRrH!%D 'Q u.8'? Dfa哾RpCC$)g" `bbAǒo\<I99 a!6ސ)fjwLk!ƶA' ɣB O 'uȅkf-7P\M!3YSBu\BN;ȴ,>QiJС/4BghH$d$^, zf:0? p u P%,a-|6>;!l">Q(Z%=!P*f|a&:b l|aip'OBE-b0m;1 )^;cjrU6縄0IMP5/B ސC*Ro8B<$1uh0X!Rڅi GؘA/h&2ʁ/P/HEA!A0#c@3:q9"k|Th!6?W#sd 1Hג)Q M܊v<u!j~ tR-9Gag.![kyy$L98>i> aQzB[PR!\?T^.T9%{z\EeAY<`],;!)4 icD>8 (V8As&PQ:s p}gJwS wP:6p*8RGF: G_\@Ҁphtpy YlPo0y(zmKxu@[8娇yPO3?H Iwm8@2havA92w8ws mrzXyo)iq( Q( `x 5BRlGu`i=Вcpws[ o8o0oئ.7iAh(Bwh_pA`)Кoy(o:;)jؑto`s`!KolSwP#XykPlZtH lho8(km jxȘІTpE,}iPduy5(EuxA ?yQ H9 m;y;ĔR>L+L3:@^qDxKEJ@L c8(j DXʔʧ3Gp7N`GPÃ9Ȅ@HQ @؄?Js.@P^II9C@IJ3Shˌ;<8JxxD= yANC G(Q9)MPL?x"o>DTTOJN :Ru LMhJ І8 6;+?aOŔ@b؄ J-;jhn!: ( pMYryxP%"5#УDR U-J]*=Fl($RTсg!#kQk)D89:;<=>?@A%B5CEDUEeF!QX20aiX`%h9 Ud_M҈Kh> <9xGҕ(19;+lGm ;`5=@Rf`pU _HKeDXi(gU 6nV(@gu% ib,#  )ղ`”` Mȃ`ϒfJ<\?`O %G`P%RPO4@ bЖ '0j8 I h 0-R؆p>x/hӠP]K'pru0V\׭Y5 ])X0mAg`P` abp;t megB8Gl?o`U)Rr;Pj.cVa,n jGH l=CTV9n$E Fl7=i#;8(HXg5mnKPop#m8EfҦ)a`LkeLf*> O4`۝P= 5ASȀ TWfȀ:[8@ރ8Ex B;7KOwIHT EPj؄r`9_ry(mo ;oف`Xoyb ?tX-ipI fzU [A8#jk8'h(V@8elpK0L wO8ԢBbexAN(ap?sPd aM@G˜Tcw(b@7z@ֲ;u@_xthpDGbVx`HSH;oeڃ;Rs LuKos06gXs8'<Z;0MOgX @:8}/0y`M0sGRq  @3M =N 8.0Wk'CVjHg Zn h@yP?x ~v1s-bkpg0C0[5/P%z@r0j9 ޲9cHP+~G <(/:tPŗ_! гcr?C>?G1BU tA9B@Pu =x G+ߙ!<8t ͚܆X4,P`"gw~K(o8IvH2X]Ta)0LP? u/yF_iw<0Is<<0iBmTX`mQ`?9Eg#{0D!@aOJRyV^e\)s6by۠jbmc?V">0e@} PvWyp#T OlmGX ds&MfscZHB2s"0p G(ɄrJT5zaԌ'lќNlL(_*ki n@X(9Rxqk؃sX;<0<kRsmPC(ذ61C0II fc {UmxOņH&O'U @S؃sPH b#S_ @Q8mLeE`vSc݆b%aX K0MxwcG".YZN0t>syhyKloHrnQ0B XiPo Cpu=@@5DG'0_ B;M6 ƒ(¬y*wH 5f109 2v#D(ܟsrMHؿ;/PCŞ3"eJe Zr+ذb* %45Bҷ7Pb?C,?gdN!0QFw4u.ܐ T_>!i`5kD )&ktԦ9Bt1d*UB`)gMC*y$k5B[kL./Ift@0  `uhef=̱N*Rs 1]@cA&T1q :^x'?XI8v4 lO&r)d4 :m7"@J#m$0G& TJ8w :`@c "mP&^&@ +AQ5TpXv=XC,HB8K(0Xa*14+@`#e APDqL`t7%,:h)V'0(6( +E5D@ By$D(pX,e$l~~D W!9\B.ZtAgC3}e0^+W81k.34'hFCd)`PU;ӕxHbJZC)l#"@$$x@>'\`J B$G!DIJؐ%cŠW$8x}0 !|CCX: C<!L`IC87t|\%|C*0L C-0a®C>prBL9yѺL3?XCϜ[: 6U!r]i2F 27Xީ/9A`C:A5ϧUZRbX6@8| MM92)@#C"(4@"%5#jCfÁb2B= 4(B:PѸBp4TLY&j (,)phB6B94<V1C&@}g&7S "C14@(IC%)rTY&ծ6"̏#,BK]pE($Hp fER Q@(x;!C0$(B}#k(FdW8f1#pFC^}Ôp{"}wI0 3 &dᲉB0oJM7b~Dlc: #\qbQVڢt0&66f̹ݘ*\oDEdbyc"jJH14dZY;'*#gdzikl٬[gBRG>ֿ?\I0R$f̡&R$Ags'B&d?.ȑp Gřp*lو i9A2$150@C I4ApĂ}E+A7`qtz| m,ﰊ0r9Td%#i *Gñ a1֘?e - 6q$"]<;q?SxH66b#hQ×TRqCX(E¡ꔣ7 zL0E0 Z>4L!1  @ `w4"#OE0!ȘȂyP';k``P#G$C?A(FF8.\4AtЁ.&Im*)ٔ F PH`K8O3H ġ(9sX J NChuXe Bc; f!l>My@<>!h[ D8P Oq!;Av#|!jXD"p%a-"9-Ck,d"N$wM?a8$B͙3C#(4#n"ЅLCSGT09B=Ix3z4"C!!xCG$+sPdF<9/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||L@CM8`B@u'" ϓ0g+$lʓHJ*Ljdom6Bp5֘Iªw {p}R@! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||μs W=>`uK~ ѓ_Ͼ=W,nϿ(]X||i?lhfᆤ V ,0(4h#]"6A Xaf}IH&n.PF)TVi_AF87^W?[D! ,' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||tAgԢ>F7pK.zvt 4wA:t )죡Kbtok+a:o5X(!$cp! ,' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||uo觯f/o HL:'H Id:)[4h ~e- Npx?i f5Y}U|U(e=NpWcP_f! 0UB({Y\6 0xv ,ڂ4ZV%je[ކ$( Elf˷`l[^%t+ ֶVu,fWf7|-,X ]Wx-> r7~ݣno/}2v=}P`J!ymTZrص'&*~3`A4.\!+0 _d#`?U 0dw/ ! ,#V' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|||?1#<ɟoHx%7ߟ J̀_5uj?aKmn@`S,a"Kx!A1 %`2ה&Q":Da Nm0,OBSS<ʒ t$HaxdX_($@4A[ $a"CF4EAJbJQ'p¢7>L 9ɗ*ac5}5*! ,.' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||x`$ƒ&S\ɲ˗##L8sɳOb"8tϣH*] /MjʵkK3QbWkhӪ] ۶IQݻCA1~W*^X Knr奺T8@`2!fs.^q3װc{,?eͻ Nȓ+_μУKNسkνkӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&LnIPF)TViXf\v`)d^Xhlp)tisywڝ^5Qh 8Ef袌'FTXZФll5)v駠*ꨤjp) 무j뭸뮼+k&6F+mVkLkfL+k覫+k,l' 7G,Wlgm ,$l(,0,4l8<@-DmHj}&%Oẃ=ґHyL?U\-tmL7~.l3طWjMbf1>"ڪnWnǐ_⚃y x:N:b꬛'yZn5v)b3h)O|of7f*PϾV&/i^P{WڿZ~?M}>/#8K#K", -2/ +8%>0LL\7 jDW1BH ? &ą[X@! ,2S' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|| سk=ewJ+E>'xy?Ó?~г~A % =ÇO1C̲cP,AaB ?$< $KQ"-3a",h"=#KH,2@?$ 5ވ#.bEJbdm0Q )³a,b4$XyP`h N T&C8T*TcZc ҐyJ'b Om"*| A`m R)jO32/O7cϗ7ð]|cM8ǚC 7?@O@! ,:S' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||N`-DcP4b~H`99"%,. e3@Fa\%p9`ST ɸБY,OyObb2J%~$pi3? hp hL?J 4C!0 >BXNޝ^*t²)1!X5֔HO?-! ,D' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||iUZC7ܠo&COYO WLݰ+X5[,@*J 2LO@! ,GW' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||&#@^F.HʴӧPJJիXRcu֯`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹sTHCMӨS^ͺװc˞M۸sͻ :ȓ+_>УKNس37Oӫ_Ͼݚ˟OϿ?.(e@N 6) JTx|Gp5>S?p #x>@D,H! h"HEL"+V>h>AnM`!',?@^KQ=$%P6ICܒ3(A` ?RJEc%ИtcS9#2dBA+0O(OB gN(@ddC7$D/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||٥vp k TrF Cl͠CMӨGjN1װ˵l3*%hob/5mrCJ>oUlVE8SiسkνOϫ_Ͼ_D OΧPovNbma!Rz:}Vu94A~jYFvaZ~(bK2N ͈:(q-iXC5Y0^'?(DiH&L6PA>)TV)VWtbJ[ q !hhHO4cQ)tix|矀*蠄jhA͡}E)MiSj4H][V駠jgE|gF`>i[7F dnw[q@ĎtHҝdEʞcq @GfݑK+k,l' 7G,Wlgw ,$l(,0,4l8<@-DmH',PG-TWmXg\w`-dmhlp-tmx|߀.n'8f.G.Jp|b|'\~ 8?6wJ:KCYD嶳? _>³MoиF_ >?:5KEbnAZ7ᤃ;ZS@$F=Ka7qQ ~ޓ̒4VAp-"s61 px3@⭉"!@51I|0w  -T?_ pbx ]*\1#v( 1w R |V%(ZT F8'*-%$: \$QB@5@f3-ou^3;=uFݓ9ɑ鰆5at8?J&@XR! , ' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||J05?W3vVPPZB$FCp J@@8|}Wt?@N0d nwS7 :AV" И9D)؜,Mէ3u@$O X3t*6#EBA?fFhI`ToWOTG>@=:D3)֘;M~GD4 p"./>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||(QɲB7N`#|Zj=SZkI\9C|L3Fč5I54솸xLHT%wjN0ɛ$,ܸ3WfW8aYoe{0Ϟ `O@! ,b' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||9Ej؎YcIueum覫V,mM]uuW<ڽ~toSU55, ?FqŐI|U,}˙)L[chr,0,4l8<@-DmH'L7ZTWmX&J%`-dmh.jp]rMx|߀.n'7G.Wngw砇.褗n騧ꬷzBlʲn/g+A'7G/Wogw/&r]tqTw$Lz͟9WBUdr߀5Ā6៊0 bq z'D᩟D,]`h8L 3!@ -C1Y:ĒPH4*`qx!*J^_0#LQ"͡ A H8/&΄jbX:x̣]=$~ ~)ȃ/ȌD~$ɥsYll ]4FDz ehC:WPVŕ"d95i?`@2"\RNbʥԃD4%GKN+Etitp9H" 7͠e %61#kgG>:VEOS J@ˬTMB,g2)Tl@JR6y,ю çGGJҒrJH)J?~0iCT5?rRp!PJSzX:O7wF p(F(;iR<^a;q@SEBՔ*)O˨4KF݇VV"BJMLF8> -07 AA0]3msҁ2yC:K#3ӽuwfpBx8g4VYj!6B[-yF0(XH+jy3P!e32!V0Πi?Y­x#$oX71QI`hY:2@Jp[Bnp%A j*!]7@CլTPrX#ְF9 C5? @& ! ,xW' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||$J""g j򗂐aETh .Ę@0TGf~q !&pV ~ $S1?}R͊h&߀g91ғءhQPI8yE` dg 1L>04@' u`Z:>~#iG<Ð *A@c>H905aoPm0emHV0! ,vW' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||Iɓ(S\ɲKb^|kMdpBO> :'ѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠC۽!ӨS^ͺװc˞M۸sͻ Nȓ+_yУKNسkνOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(ba7ċJG|ȇzxJp>>7> >գ'BA6 TZF  /l'0s $̚BlB:39"P%=},y# YhH RFZj<`f-BĨPh13Y@gP4?~ c0d"R.#(@niC Aj zb9d>ҦjRqJ!p lI!|5֤#^JpC/@P?! ,z' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||}Ǐ Cxb!,o?kFʜIcDTt,f͟@FeK ]TЈtgӫX* AT*OYÊwVH4#sر2 =j[U0[z %)N2*-Ddh7NEj A|KLehĘeOct4-G\j: wIz=nw߁rq[#ޚ*=8>Uz%^_P 41nN-oBSZ8k֨3 ~$QD( X! , 5' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||scii!?&QUk'?sZHhteWg~N^hv&{  mJi!:?iRuA% 0Da΄DIh#4ѦzjѥL|I-qdzn"Z.^]iѭ)x榓ZKvnA^:piNL8c4i$@! ,O' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||P6+0b!x$(𽆜cM8XSx 7! ,X' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||q JѣH ^L8_3<1bS`")YR 4l n.]6]d|8߿ LÈ+^̸ǐ#KL˘3ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄J7&袌6裐& JDsǦ{'Pzclݥz?EX T%* zbBdž67pZ!ZY:i/P.1DAF~0 5L.A|Na!9nNq :,&*%:Q! &DKG2TP20D' KA CTK]}B! 2#|sD?-'ETASwvq{1D] i ͺJ%owC-7L lͳΪvJv6v.{)XcM8`9! ,V' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||+m^A!p.~KQb2XI88˭Y )K-y5CM8ܪ;mUg IB2D?KKWI8`$;l5FIg>t-)a4^3$1Ϻw<Δ 4xX2L\]]fPG9|`bNeDeH)H0q0cʠ]oSS4$@)U\SXzAJL * ;= 뵲b>sg9'\QM8[N1YN plQ! ,M' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||g MJjJCi{h̠)q:fh2hQ!7ְ 2*ZҘ 12i [Xt.J(q#&RhBBgd*9B1̬l>S.q5dŠ2®-C qB\2^7{?ͮ 7Xy&6sv+1AXÍ5֨n;?`r! ,U' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||ʵC6})P׳hъ!qfڷpʝ6ݻx^˷LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸fͻ Nȓ+_μУKNسkνOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(R92ָn6<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*kodҠr$裐F*餔JEf馜v駠*ꨤjꩨꪬMv8jktcJp*| ?@< 7B> [ʱQJnۭ˕]   !n~$wv7,ҺSG]8gG8P&|H+9O  VTS9 ,-=TA$'UB͋,32[?fD Uпف!2z.AMݴUKQ8'IOPhdP|TvDVR: w]uDf,PLypJ 50?{đFEFz >т;+?mF9X5dr(~f ?! ,T' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||r:Bܲj =֌r*j 岐]113ө*tCe0 JL/0v3@P'Lc 'ظErOyOUR!'B 6L2r$@ 8sAf lq 0t'z/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||ҜCŜy+\C!-^$Khx""NϺAN[h, bŜlRI6ӷ@ %vY6I͖JN(9' _8D&3ܬƚ'X>5P6'oJ `! ,' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||oF(OܘeFg0 ~

/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨ:Q*무j+`Jpǀ+첆hǪ>B 3?XHk|4YB.R/ڽ 4X'?,$N]-N8{TM=o| /-\傕t,7քc@+B>?H H!tƸrusBȦs-&q@r'{ 'Pqa +A@9LγOW 5[SEh {?! ,|' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||)4 "' 5Ѓ sS ᑚ^ ӡ# 6 v9`5dp^LPGJGO&# J0cL)P~S3 s}h#yY@ \ .H~_ )悢S S=Չ؁8b2}QR;YT,H`bT}7%sr@C9\B6¡ܢ}pw7Xx H ؀ <vI[!T2((6287$6'0ёl` S ~5) /sasT617r~ T8h;HdX! @C_T@07l sDq;aPM6|0N!"WSfx.sP  İ>[7 \X`bT& (No]`[^W x xa^wk -`@BAP2%!V4[v   0>Z2Oy#rN Q2H2Շ W`iDxqPOQ9c,7V@*0= }_} _4!A``EC-; 04۹y0%\&'J44p)aho89p& Uq@Cv?Bg ^#1 pA~ڻj3ra zʹ۾;9}0s%פۿ<\| -v ^ (` 𽔡s4sKЇ)@l K 6,\X^R Z2lƐ 4𛽥PAJ } j ^bXbc7yBᰕ& ƕa .cv"ذJylPpI @<Z)jyXPN i߀)j<ʚ!_vf v K#`n mo7/ ְr`fX .j@ hkh @2W -c}30ak /b)J rAp`<@0Lq sbf xEH }p떈6 y`9ƂBb rQ?̖!!`0 %ya ] 0, tC7Nf&  u@ ,UMU @RsB  m w\8;iZ%7.z ^Cu |҂-KeХH-E^0p@ XHp`q<>U2I k .D. /I "k~ "rZl V f JXb M@V_k E[p Y tv ߀! y?00  N^V4k,0SeL`sK OV V`1@`' 0` ZRw0q9T~ :$3ְ [ 4%GHp[`R 1tpĎQ }^Y!@" !gPxpNPOq[ n$ `8! lJP bP .X M`3 0- ְ : 9/ǀ\8C v? *`P@B s8$S] . ɜmπ~`/3@109ؐ]b*@ sH@'"W `$ j€&<x .Ҁ wAP ! P"!Q$!4 ! '1 Q py_ .h}m PkBx s@ РSM(qLJ`\fgcC>Oɟщ <a0 Aؿi?  >QDЌQÌvRH%MDRJ-]Tɱ qΜ3]TPEETRM>UTU^ŚUV]fYذE>=$Pe!W\cg`;ƍ7lrük_~;,qu-_Ɯ`X%3`= ,赕E%Mұ7Zn޽2pFÁM89MG5pH8s)N89vL!-ᢆoyO8|C0Aq,My;-rq8q̵b˖]5T]WR;2R =ܳ_0fk]]DUK#\FS]UOexWy)>yg>z駧z>{{?qD$CFA(?ɣ9DhMH>r yD&9!!A_B|JJkP#"0j`%б?c|0$(i#A!?`Ұ9;"~X:P6C :DH39`CB8Ơ3B sO9'QXEJCUDD9ThD$ʘr 8"L"8,QGCdDe*'R Gd $0x f !KGD#2 wa/QId1 ha (0!Hd"p4!ZbD<~HK0#8!Q7#?8 i 01R1C aCiT054D9 f (AQ؄*URLCx$2qts#YuDt4B8a @0G!T4b!j u@(8PMH:ֆXCd5bj7j z$p$G0z7>^rgo:4AT@!(y QD9BRG!Pa pG1ap@,[m |<@CUd; k\ XG81@:|h?7x`Ac:D" xxBrxV@'utx:HK0HwXγ*/H C yP @hi.@8>M@  :(h?*.jA5Acx DBن`3 GjRbPsPsL/EA8QX] 6t789:;6/7I؂X4xqΠ`BhchP3)GC̍HcP*xD ("\ ;b8;Joxp)ͰiȽ$sw(𚋄ҹHl s8:Gd((k9ȡsH)4XcHGhrh.[Qh:mH?<hTx.Юp 'pxkȃpl`whKtxg<`PZ[6~|[kvT(le {x@V>">XCt(APoؗE,mDHv8#zIX+R@G@wH(oxLD!? @Gt SyDfʣjQ0 pI@40zxI(a KXT@Rop2p@B=%SM%hjCP s0oXȄKʃ*n@7u m?5M(VnP>=@?ءtJbXN<.$q$=>8㚃pP!*79ET=؀pMXb؄9PD0<QKVh's ~A SiMwH($kXG?jxn>IXPXxm3`;gT x7]j&-cOPa$Qd8v@҂xSDBsFe/(~J_xQUBOHh nN89s=04R|"R3sD7L,D>XG<ص9ams(gPV1ɃlJGP[d+fb(G,(ؑ>|MЄIރLN8ne2VVhePXb`fu6Hh5+OV\XRHb Mox(k-_0%K<2:wpb؂K؄KKSpD?B5\$B'u#8Z&&J:/1̓ek̀p HuUTpr؂ `-+lP:ʀE1I?sOdL iD(peh(gMf [9IXPXh YX80Q" cJwjG0IA@!5j)# `3dsX<2OxGptx;GX;6(R%UaJiHzPQzԛgZxhS`FX<5P7hcqS jx[&P>Cp:C"pȄy`Q _e5 8` z8ox-P.x bBuG05o~@`lTs [UOȵ6PG"u7ofвO`qʷ*6fȄ]x6 0RAܳCMǙd"Mx(APC@! Uv;((;.Ae$#D821tP^0 n G!C9PTl^ƁR) dP^~%`8G |&4щML T8a!S@G#y:To<U9JG5XQ7k|s )Ȏ @Wp4 [Ľ" kl!I7p8:> IB)j'<C3aF,|R3!>|'fTC (nгbֈ2Rpt \(3#a(IEQ.(&EKs6Da>ܧ %IpP:ADn|Q@$F3ц|IKd,2rc;wX $6GlB, rF3Ka v? ]Ĉ&! `C0L֑ ́0<&Y bF9 4jp!1_B!m($c0.7^%nuJt*F9";Zc@axdv9xdGCs9a9hcOQRʁB< }awM#+!}SxΕ z4CA @P15 }(C0燴d@13>Ҡ6<蘔3MW H!t#u(i@@c-YCp25bSZW$Ҽ5-a>6e3~6C(JB-mj >An@mmw1HnGn s"}/;VȿA2ou'德mmq8C}P?72pw+'6=/HGN@#ݝמ-n\.ī_#?:͍>cf:Ab^qwVͳn:7~q|n0q {9.t= u]݆7-W7ޫy|ǽ $:汞|&_9-s$:Iw˜9yٯS_qڜmzw[nO{;͓z\}dȼK{[>q<orov9З9CxN6]ީ]•M!M)i׵ޱՉ]_Uq^ͭ  \ 1ܻ]=^.י͕\Myܕ۷]Rٸ߭M !=_Aߥ\%Ѕ]!ב] ]^# bA]_!^&] Ib]a ea zb&!.^!!M B"c"j!aEt"]^`c2N#3V^N塺`CQ1ԍca ;Z%"5;RD<<‡2#?[=fj>@A6)B.$C6C>)DN$EVdEFAG,?BABĆ( "Ed0@4D7 T)D)pE(6A7:ȗF́D5<C@5eW,B50x9ВB

d :G*FL1h TQfg Rꨅ+WQ9>\Qᔵ=#ICل̘Qp S^רƌ A4k@ 4k]lCgX"T>zuױg׾6Qڀc XU73p"BM=7S:0§/ЌfHs AbJ>) )!*'$¦1 o@ IB#Kr`ᅄ&an&0 L) f9ƒ Ȧ4@ka faGIkɮ"?9gBjQS=?DQơ)Ig6gr&4c'C@lBhDQJ+Ɲ̩x94Iy9Ԕ-r`!ld k)f a@Cz`cD` $!UT O9B&Hh@Q'BA"&j ~AGD.pDQ,KsF>2֪D@QNYYn9]J1f"bfB`87R!xkHoC ָWOƛDy2mW!Q5B6zNЄTE)eRr!Dh9'Uz&gkn % A'SPabޤ!f)'GVs2w߁^@I%>zDj&H 7:T8Gt󢫷|;THȹ ";x*nv>|CyC*a/ t!X@QX$;'6|(G9pH$,L/Vΐ5!aa$ AA@A"hJ\xC%.MtE)NUE-n+_F111l0#bE8**7#zH$Z$HxfLQG@e&hIDȠ22nG%as`!WHl$D jB="a!%N6RMq;6G]$.p(q*Hb"6ʧQP7(X|bmaB:9NX 1s @: xSS.+A s R>$=o;$"'2+a6i#C{!Ґ`:3*PNy" aA^0qqiB!URGJƐ 3fh_a *!|:iAV\E-|O"?Ѻ4f A6>IzĤ"85 /w F}"RK8ZiCBD4Ѩxb@7R1c߬qM^x#}#!^x!'y 193 \X}QfrC^ol h+. 2c8z11"pk-zlΖvP?Ů)!B>a &F9 Gju]:&e !ʱ-'`D%. ن3 va 4a;Rh6 l B)Q$Y!DB8QM!CzeqQkUhe|m  )I!> 1V(XwB` & a0! @X a!sB!)/`M!20`" & $8&C!Rc@i{|A/"&`(vhs `a3o #"4h2 |a #*lA@†Ān!.Hq(B!R ,P8M!#`p!`ss-&apA2$+B2!qB `N`zA@: lAĠ*`'@$  Pq"@@ P@ڠpaSk(AARsʂ&Dz&2`$z"` aB@Laj"!veD 62 N!J" rL S~! YapO!(M$mS$$bd- (: Ɗr493!@tÖPD!JA6a4;38pp2aJ;6;G,aFa;ES2!HAsAE!DABY3~ASnsE/0 x!Āz!("` Gt!tHaG|TA KIHHHEAoΔHG#;faTXO! .PB("\L! 5R%uR)R-R15S5u#@!@E)*#x#. !=V Fa!2y)A52^CUСa."#T!)!  FV. ԣI\.\?`5NVo 4!Ձ9o\K;̇H{w9|֜Ԛe=Pl5qAgO~Jzw ﷺ{0 hk(tW;әkg}YfV;D2x8d]9ݳy]ݫqW .>~ ~R 2#w6G%UE)םZ%"..-G ~m8Fxq5! A/b!{z@;)9jRAUQYB"䕾m >b%[v株S"k"ʐ~f`!+LA )bF>Ie!a^ AAJ䅰a0!f< `G iapr&L(&裂v!!\f/Z~ǨW܂vGZ~4-0^bL&jb`!5"bz l4:>]#-J: y`?`MϘ 'eV?! Om,?G0(9OC!)Zr$&\d+ N52 )X3̋ZTcFf0!%.Cy7!A*qelp*tc7I#f,Yz3-FY1Y^Joh\$d>4)ʎWXMvmOYUvOgZvM-o w-qzA:9$wU0! l$"(5"7_`-vEܜe{#UW:'"6 @u Չ1! f59Qހ `d`pRA^ DA87:`ὥs7Rov1Lx&0rj0EGQ(ч"H?Ј?"6Xр BߌlV<sm4$(7BC.7[0JA)UD}A k&Gp刃! s"Q5daG0YmƆ= `wK7QB|Xc`e BH& , p&9BY13;@&weģu7х?|1 VH(H#s85W>m998?萫D#2j` @Wg8`G2A&HEr"o8Jnw<|0"‘ j#PDM?a6ſCG|@!z bp-$axLn51D_ Ρ`|'~Q8⾎QP:#G<iD:t<Ԥ ,#K(.|C8G0,ss+8y0;хsqCb٬.": Vcs ߐhy2 ~4`:Ҡ !֐ ]    V C `}p.[P .R 03!Q ` /PDK\l0lcx[pwy-\v f1a[@ p`J\X9 z:qG @A Ry}ЍP "8a p-C 9 В09+P uAni4)qHh# \E D})3p !ep.@<#!QCőxW8V,@ C \Vr) ߐ ~ ԰i5:Ҡ ԰6 Of  {@ s}Ѝ@6v swlvYӖ`5 q8ްst:Y@'Ŋ|l )3>-{%p^ۆ т\ B[ٱu5?MeO.>Q]N3|@nGT9Z">g4qz 7` ,:T ox pdXVf沥e p&i!v0|F @ppxK+nE3 ~%J &5slؕѡr<1p6A4|c ]5 pr H)RP0,Rh]@ P!N7(paq  fT' -BJ[ @+qQoX _s;)s j`^yƆjWch rq= Zs|F3Yց̇"q03up PW,' x's^FlH?!30y2O|M@{1, 9!<{ Pq}g{ 09 Yj,Hݾk0 Bک,p3Ydހ8I+62Ψ@$KD숤ND鏎? QD%A칅pYoϑmf6k@ 8.~Rbf(# uH;0RD,:'̄HmαK;ěp!  S 2DPTHJN7D)h*ZyRJc2&OKBD0"DBʤr29Jbl!i9GYTjQuT'Ǒ>yGWfRr aM9CHh%(j_'0RB"rXs0^;N(1Rcyf3cc=@j"h zwiJ"_2ӑw)no f0`J99'h`$gTn5ݤ@'W! )lT,*s OÚu9rRBp gHl$.(WUf~qNDy%F{#`N(XgME,(#b>dz>a%<)Q`Y:`l! {Xϓ&I$_M\A #{!6Lvǰ DR8½Su_.,'va=9/hIeчTHv!F0T;nH bEl!B V  /J CF ?6^efF,"=1h,cl"@D?б ?Bqb)A} @D0ԻxOIu3Z\"o5`," $ Qᅗbxs8fOvI<&H>lc"$C2ഄ&WE\ h쀍x̌!D`o0΁Mut![pD"pcHcH-DaCa g8LX7Q*I0.xQBaL ̈ a?!j8@9fmGQd 7u 84AP>%߈SI1=~7x4!G9!XG*T,'X;ukD k0G8¡,G@r!+nZ5!(L1A9i,:dp`9!,eݱD{P,4aC#^17aG0#H:t܁0g6葫FX[*8p3!"`A 8`k!&H*!sF8Z; /RJ! !z 05y6t!Q8F90g!&6r|R?q:i0^(2 Q"04C*7`Yӑ?psP!ZR`"|cxYF9.\sy$jb `.u' ^bP#/A)A`(X)dK(i?L?k9$^t@? +<`,&K_Q6Du+L̈.ϱ^(F8sʆZo'D/,@'HE8gBDa p&Ҙ3 unzYCsxsDHHQ$R~mk;'rW1NS|06Xs`RCBm8@@ t {VZ>" ~G R,@!h3!( ! 5BmD!dҠ?~1: ?6;R0wX1s_)>胹XcSguXH 560kڄ (*KyDPtx88oQ.eHG-#m kr ԒfHSHXuXp@:(9kSYE8qLxkr@v GÐ9ІwJG8yBs񋆅kfhxÃLH:Ǚᫍ HC)uЧ7 6Œ 0tJ5+t>/gȘc{p $9zI o6ue/<.:8.1 .HƽaodC+/<8㐄e e%eI*S, Gwȕ9=t@;؜0ɻ ӉqKx4 f,` Rk(f8)K-pwwCI BATUkV?fp hbrX)DB>>sp@t`KG w(T H|upJC@+aEN T!?D8;h; j$DQHy nPG0$rE?hVp-[еz2k XQ"FhMDs.t8kX;Iꂼ\ j;H:@Q`RDG2JLB؉``KȃIl@G,j>x|ltؓ@xsRܩlB@SN L<<؂Tx;IL BGe(7icHi`,$1ɰT@GK 9t B@YԦ<8cpiXxSHS8)xT0:B`1z!hX=)l *?T?p`IAHXvح9xFk˄s'k7kLr1jHBHWt]t8Aj ҫ ?Bg09x":'6A}kqP5(azz78.(نGW`P P:@ 6@bΰE"Vb!TD/CGe@↘Bg `QF ٹ,SX+rh6N6 iY@LDdL6ZD D8X;c0.0LFB8-bu)9GBEpXxGQ cR>x 9srP:i(rK0x0f~ Bu:\9d @Ú>+bq QXwpy8X?>f"&#[w@xPu8X?a(јHbXŔk"&> ivT6d% h;ٌ.CiE^iȄ DJ> ;pc`<@lv\@.k>kNk^knk~kkkkk֘VV6`l0,CFk †ÞOHA0"@~얉mEilmllX X-sl&mPln > .]nlЎ36nE=Xцun> nȎ 醝ϖnVgoFn^@lxo̶f<o32O[ ϓ FC ngn' 3fF> 7,AiOrG 7(1u(_*쬓FoP廓=A#:u WvP΍]kSa(Q"pZj]cBhGu'WwN gv`os[Grn'_2qxkcf%pr;2gHNNw@&V^?mKttmW DϏ'v;t/H<'v}ws2q OvS"?mQOQtaVorNy/W2 z \nzyX/"R>iuv(~l"6j'glppv}x. vىg EVDpz_{ TzPrℒn4Hƿ 8lP  J !:\RdB5bqIJV EYtɌ_wLԁ[MF kcΕ,Lȑe̋9RTpOZ|obbe~ҫQK6y3Ξ5 Qrd1~(8ШTzphQ](j.~YۦgjZU N;wI*ʄIQq]Mܲ)n:yشJ] aQ#mY%W _vRe\_}vQXnu'xƑxtYUآRJPIGa/fTNԓk&=czEGdmŖ]QZ֍YIUDuUfoPJY{*z |L7VzVJZU٥(5ND\C1v9|q!H|*uepdߙ%lvkYZ^nmVy!zxG꫾+S*J ^:VAz")쯛9 Rڞ 5)o2+wڛۙi۬w\-yp)(6+\1@Z"ufܱBJ!\r>L+ܲ/3\7㜳;ܳ?sѰ-K3ݴO1P#J4b1`h6@]g6`28"lR2FÇgq$j^#:5G BFbHҊ?]dIaP AAyRFf B?F;Fs?s9$3u*>T$P=Č<ÌAƸzSa !71D$#kЌ60c9i3F9Qo`X 5뷫( D%HE8;9g}`3, . %1tB/%R4 P gE8_ FR f\&@ @  SkmvH40lng/c8?A<'AԔ03Z P16xo,@; eAV]7eo(Q iLuME3HjDzh?.|hNSRC7`!Qc[P 2`k +l7ы^2G a k #n;p8:4AF`GChK(C#m؆&T:pa ɋx˜}tj4Jaafy@1|X th%Л0St>:4P$0S toۋ5RGd/(0i.ӡ@7q B(\y; ޘD,a_0B\= 0"1#M4:4B?6 "'?ā5x5(Lm`_ A8"\B46<BECϝ7L(9,B4@9XD4\i4$7ȃACj: 0FdBM?dq aSmlIJs? C X!}F"!F\A "$qᡸ%bbٯD@db(̬ᭌb**b++b,,"Μ@ *7XÄḰA5,ɬ/`DF4?f0:l'e,!@}q$!0<`"0ËF:l5 1 0u $ږ< i })9ס 8 C=cF&BL! ?L@A`5dR&A\1;N*BrA:X?#:O( 8D0D X$ z1x=jdV"Dx/`KruY90 |Z?09$?01tY8(0[J΁R=4 =%(C@<!l A "h:(WQ(9`3Uy$C014B@xOݐ0aD4ܩFhfa(X B*)¹pU&=`3jCdA@݈>}%jґ'zk븒k빢k뺲ko k62&Fy|J#(GL€܊("l J,ϼKt`zH,~TXĢɐƼEKGshxpHJGbD,lLXZt}H_TD9oJҞjm+FbXHaeƈlʆ WxG­ː Qwz I\HȼGKPkn(VȜn0j,GDF^-\r( m`K…d, QߠPavqJɕ-FͲHT f,<,*2o:BoJRoZo!B0\o&ONY<E)l5$(i7ooT3/H4dO6)B8B),345Dz)K760;M0,lc:` |3:Cx)XC$;3K΁7pC*Ta*1ápˋ4Tfݰ~$h? 6 1!|y;5<)p'(')]ƁA<zAC99?hô07e48?(B9xC:P((0?r8C8 6C9!t8QtQHЖfgbgd8Q[UcuVkVsuW{WuXG :܊Y΂Yu[o@ۍyZxȕY^5E{LHqvTL`+lœw"'y\FL' h 䉂@Ne'b0$` X4T2\l4`(B5P4`jCCV0@ p05<4T4 `f.CACjׯ B0`f)\/C0 `-z0? 0ō"ztw /*wׯ$TPLGVxC;6im9x%Cى2ڭqev2-Kx8׆Hm#qx"y,y;CyKSy[cyksy{O4d k䒭,l0I4-yG.z JKȓ¾ҋ{0x!s\Dy2;CzKSz[czkszWg31C8c0:1id(lH6Blf40'A@B P%:89M]JPTC*D4:f(‘44C02C6x'!0fbp)pA̻0#"d4>F#C Jk B"f' Eq6tW$(t#%`uD5Ma˃% gQޡ#>MĆEP{h t"wc8Q}xUbtDH:LAz"@е%VUGl JVY3X>ulbs:,|B̐e4pj"倕a B1"6 bte0!,\ T%2oX&v Be5hlBc_H:%A`E,? H146h;&q,azA :`X84D`^dba?%68(:``Dy" h8QK .$pQH ympBj1΁r+{_`F8;#3tL9d?h%B;V|ak"!?L[ ,E 8?D B|p lp"pDQS^ 7Cj6:2bQD˰F |*Ѡǯ"H;Z٦u*&VP&,,}K :G&ud6`&aeEU-$B^@Mq*kA70CSG$ܜ@g.pu 4Xr8Bް!p ;D:AJub$!KOk4Q@ˊ="c])2"b$G@HTPQuX Ҩ?, sB9.q8xxxǨ$Hd,-?`B&Q@a +b܉7D2?1 ey(W) MCR6Pyģ"0F;#vpA< fхPҩ}D P*TX`;"QK2.m(G" @IQ4mFcCn&gTXe|M{D@.0&q<_p;(`>@P? /(C89l#DP$ D MvEC,0C(:L~+&l=TZZc }I`F4d钶d"$357 )7ɂ P96pn=) ruX_>c[˱MT:,l+ p|aQ8/bu;j X`x*yD&G@m y"xGD͞ZCt36aotPF!rQF9@PKHc`P

bZlyΘDIߡ9$^`W, `ZjUl buv=vl/ylq[`A#H!dX@0! ʁN(!AYa 7LR4 fxzB b5Aa"`ds"N@P !EД$BaBʁ1,dH%w,AAa U AaiO0tt0(6d6Aˠ"TQ vcT"Ef_OXAk aC!`vC:*iJ!}.8A&၌To"f_؁P^rd[ [Naa: !வ)7aFO 6஥x(S !&` xsb/$" |\Fg_j]` O]KScޔ@@ 6cY'x`1`f뼥mƉSW6yƁdծKj$0*#E8d(A6X "&8X0jK0QQ!a`!J - =g6L7&Alvʺ"a*܊Z"*L~mMA 7Z_"¯Baf%I4..Z'ba +-6s`Vc!(_ ,aq, A4h 5!5a RA4`"" hO2*lSB#1ayb8N"d2yT4(z\C@ϕ M\!,A0a}{U .16 6`_zA 7c+xܺ)|΃"[{tQ|V`tVLA菤$bԒ2 ~$6H/e!.DH!O! @ B 1 [):wsb`BIrq Z#Q־|CYBkTSс?iP Qxl'lŔ4iiKuk9`e9cQ9DI!t|`u.do|qGՄ7R5r߁je^yCCy s:8ĿJNt휧²5h?.< SټqRYE`[䡹I\p"!x7噇"aIpcDb` ]PQ^`0|#@27f,`;la԰FDegx' qa"iaGb '!t"TIbf_$ q|#" `nnQ1  0Nj AqrD >>";!fu3~?0,&kxE9nᾑhnW4 ` `B~dHH)1b9Ep5Ha$ļ]EEx~L 40dT(hMLZ0yL<VBG8Q)vp*pdbN$B'kcq(vbG"wS6Ua;ߠ'A5Bm#Ab?AiAC< BR%b X)8v*G @i:a=pRm'P# Ԡ܀Y~TW K*Ԉ k Ld: ]( 7  0f m'9eU 3uNѠ`Q u XHB!@HJXۖ O^'_ IGik^-Y8؁fX:iY VFP xXc`P`^= ! ?io yfgt 0~@Ґ@ K (d'Jp^@7[p U̠ @2g_مHĤM?jYDIN^Tou\C^p3/ryp12s gDGG&X \Qx`Mnd,E!>&?v2mb()ѓ  \0i 0tsJ;} wv l6'& ֶ|  RYŠp zd@3 \ʐ а rk>6) v m00 WC5 m ,c7'vPPC֠ q` t` 'pT' ੻Pk  # spAgQk'3p1}y cj X`N0 0#=xa0 Vya%֣67 @ {&F 93.+ʁ!{b F 3 r* 9 T?L(n>#Xui)2! $KEsSQ`+Q' &/ĥ.|! /H:wIǀW`@R H}@ հp;հ $mQTR!N#C 1v C -%48^+ m, pٖ@07 p +U,Sdl-GM:rkpπR_$˰  U0E z^ ]m gYs( G  pSpz ϐ ai1BKVD 3X֖Ҩdakj0 :`PH a0 ` aj 0y ` p` D4#!EsUY*?v UIOա ov2RTNB ,8m\*#f_`m!E:< Pb#^{l5 yp%cn&Y°YūRb%Ul演 , ` `5j!ѬİZ Vؠ4nPc!_Ppp#Go{o3R]@4^d?2[mp,T,c@P|p7S ! E[9/@ ;F @ S4j@J!p |Tc q" Sr 1E !!J5` jP p_ D kLlh 堤F x2v@r^`w@ 9"[b?$i p0 S^bԣPP ԀV Pd ` #~R e0V@ P\o b yLtR#ji  L\5>3 ` Qpei pw0PˣI&,b{PްG@yUZ.]$#ԁSs Z,R\l< ]@VZQ@A6A (~T!Ŋ<禴oZ/IYcQɚ;fy2lQȏy! ?B.M\,xR|f*zwƚ5413OT|$$"q9bnBPCo,6)?Hm鏏IͶ_ &H 6yǔ YjnC(Qo`!&`Ƒ^ ˨5d)k6!$H#DǎuG2+,Rs!aTㆅ oαS^l 5%Gh>ҐǓlқƔeG88)&gSÛy @`Лw2 '˴矵Q&hFeu8#iQbgP'QCiub:r2fyhɃlnb9Gi>`ynzT)g(-d6Q@f&8SBr6'rz.g`Nء3f p2DavFȗ-u<hhJtE l&G,4jQz٩ 铪#ˉ|@')IG'tvt,ηQ!E$9hR⍛@>RG$Gjt.ىTWcN}'+ 3! A >#1yAAdmo#2 &82b%8c`Lbq%8gtMd Q\RPbÈMLUt(>G#j0D#pin=5I.n%1U6P(GL)HvG8S#sY&2yi#@|>Gh&/$Vk $+eLl~hxQv6XbFDG!K7MINv(9Fj NaTҰ {JZ(!!X,yZӘ̒BI@LN!PP*i̝`C2Lrp""4g xS#f=) lTH~$J5]Jn&-0,MR\EQvԣiHE:RԤ'EiJURԥ/iLe:SԦ7iNuS~!2>i3x$Aa3t j8AT~neMF-:#wL(~1 4.l!B"(2yujM~a QH2*"5$GlYM)6t>G*\5Ђ*MSl*RNq?d&=]ڎpA#L{/ṰF@^9;<e"&j!c0a"tə0A(Qbc۬}3#44\(K# B A & :~e`#TpA B8Ɣ  @\a!v( !#(*a# ` 4A bֱjudb=B?pTG 4QEmܘTxcGJ5G@0LeKґGE3&@1}gǝa0 gthpxm$p y O0>s1-ax@O0.>CBE"ɋ"iPG03NnRQ e9o4a7ob̨!:+$iH9aE?rG؛:ig`aIds|a.[xxcFa .vpz(K:bpEʆm(CvI.caHrQ3am  8SfդuC 8BKObp@n.<(b;l2*W`!wďDc4oX?8vxXsw f>X s(pХp5;C`jZ;HX*H8S)C/t0ebD=ry8~`g)#eHHrk`rur j 9750RPsp@:!tH/Hl+p O< ɧH#fHFXDP˳ ܊)y(-wxXpFogLM nCkhw`࡛Aj\Xʼ Hs(e@!!s@tH؆R viRHeP2gPt0MؘN{ttLPO xv pX Qxsۀ#_Jb!tƛVȘ.0 prPN@zl<[ |0Ju02`<@;x{SIBe B +,pУp2sƘ>nQ (~Acpk (p gp 0S(:XzPhxC"{XrdGPaDPM6p~`8A;̀kARf,8Û<z@M~S HBs:CH;<`tS$e:MP\M.-`pBD$ٞRHQP(sC]weN{:Ecuɒz Hyh؆nyV͑9ɅM8LY^>?@p&ahO`Qʬ/BPCjNDӍ0twGM(+෣^ҹlAȕNs4\RPH A`mf>ȱT`i MFKO+q_@X8a x ~yKHCPHٙ7(7̾qCXjMz!qUnnhshTX ms f `hj>2ƣ2NɆ[xKjH`Mh/8)u8rPqM m4WM+7nhxA #~^0z,`PQدh).()~4FtxɩjsQh?H4_CV VaS[SpC Gl8Sn`Fm%h0t U@jc`R"r |7i8BlYkIHK9І? %V&T8:1P5`kZ o4iBr!xmHL3/7var8֏`pN.0fPd,Fتk|oRI`5 k , yWS`lGGXSF`M8 %,AVEІr`}/g՞ɹph5b`JHa98>p^*% ? 6.Q A,CiH (l̳n$&`@Hekn&8b#hP+g(ev?C־{6g"Hacgbm2$m⣗Md&-NHu+g?f$,.3M]e3: d*k'n?Qrm(N[ 3$*I 2\e.ސ@A %JBn:C+7%gby +9}!xb9CD[zK2ʈa NJ\>OmYtxNs9ᚫ?y2g|rӏ 9}ϩ9e>P*b?G**zsŦaI Rb|*RsAJbڏq$'yRn&($@sS1bN2V ҊH<7/xINϊz7NS>ZF8v銮Ý[纋p3pBi cBkx"$8=bh/ઔ%!hp y#C8Ab2Ak!$pB3`i 7#pUFYUYE!'6AEr5 !@Da@ QRL$^%b*x;>1ws!,"nGEă: z(jfmHsp<,1s|7q bX Π,`S"(`W%!b4#uIHBoGkPW ד]E0b6(aQ(|c{8;.LQ& oB#@]vl)@ h!sٵ< K|~SQǖ#Fnk.RTn+_#U0_e5|Yl$ż3,d\t *UWY"ĜUV K5 -}YQ:_TȠ|,\Ih:DO5`(F yL]Y\6`\0LLD'|CĂ',3e\=?`9#?l2hDFBo5!?!& NDo$D4 +99\MC@EYV4 ҩWP$F*AdB9Y(Lfx.LŐ7?#\jn ;\=xhPH%A ȅ!4&ѡ BaQQi`h=UI mhOۡa}qZuHDFL$I!YaIdFFօ  KYV/P"؆=4C#cH)"|A;l6l6( @70;(;"h3/7C9??XP :ihMԖfP2P9Il?C~=vDf_# &KbAQ`n}":^M@Cj(0$0T_Ё<,MDžD)C8A#zdT CC8\|DjZP(BdC(o*E ֢|$H|a_Y^ՠJagb"eLT U_Ą0dHM.GNE^?#$bMJ(t~Q^C6D!l9Dx|T͈ʼnD,H$֎ˢʞU!Z,@"0OI]cɣ4 4ä,A5ڇA*H%.D5t Ln휴׆؎-٪̆ڶ۾-ƭ-֭-mpL 8IDt서?,l #<<ևs#)-1߆(9(CjzA. k<?.D0."ÜmT o^ ; AP&l ?4H/#T"/I)Í}B@sp L:?LDoHOT/A͆ƂF (li:DXGb=4B: Þs@3D\(g14⊫|,T-@"nA%A @WB%C-L1 pTt/ l\W)!(A!Q) dI?^⋁B5p(T 0GjäUTZ)a 68`p),%jpo/h6BeC4.j{wt.nDbiJt 4,@`BTÂL)ޥSϟ&<,߳'6ĬbƂƠPZ*WhT|[Dͱ1ډXAiu6/?XƎSNC Ĉ0a72^i݇\f83Qb4^$:/=8*P,fnmNnA8`Xh`K1dzӆr٢xU]" M ,ʷZP`K=|b#P{]{sn4p&L۰p% ?HΙߖޅ=s=bf{1؛tfao}_~˧rf~5iQW= p3ۦZGҕ.r^9 =3*D~,d[>^2׾!eS 6383pf%0y%Dlkt>ES:vhtI{DFk,4E.= .kV^x/]$ᓌwvM{Iȱ+"s{Jg8 CTG/)#AYa8׹d߅1^<K.pX+sX#?)ir3t!/|ڔFAސ>5Y)R+!Ȑi= H65[C9ǚ*gefpOT<wk깙䈗M.TBp*qy{ ĺr =ƐdOb(5)dKarӔ;cΘL Ɣ=I҅Tft$S.# .QkSa8a]B@NۄҳU! ,8S' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||H95_;O`aYCL9LxG/Qo|CsQ}FG< }kaf^(sI*pEE#'D4ZG3t+'}?SF$G4?='G@8ny?XA/bd:4pFht+$4@P ˆOm`!FA T?v ^rGD̨<$';|dM0 341dO !r 5H8Uy sFG Ai7TlP4!l!w |ц-P >dG aF6 "x? 6?J =7HpA-(p8, B@w_CM&ỹ>@dBȐ%P odFALuH)S!|*g iCﻊ:x@J `q P1=8qC*aY|pEd `6OD7qfN"2pL(AB xAyɐ%ƒIt !pD<Ѐ{@B2y ;D`L yF% B npCxN*a Ԑ5W(bP0E&}9wE!$ > M OA~G0. APF!X#>Bdn&|N5Rsx.QU"ﮒ<%K8S9ʂ@ppk*iQ@Fox"ӑA7Gǭ1b zS.(Ӎ@Q#e[/a$5q+D#j@#A#nI5F"Ha;A0@#%Q!d~pl|1˂#F'x~;Gh1Nr=ɭeG‚A;smY^ "s,b>&,Ƭ!S&|X5Їs<m(&+(ٱaR+@G Q5FhH BG` QtMJ!jY5֛>upqR +]qŹ3ba 7bb$a KԘD)sy@:@,$a!F ȃ)8# aUqZ0a!d ;~(=cUJ (APwq<l }.fӞ8@ptk$7zPn#>SW` `  lm`U z,З D߀>RBB[|pf pn ~5JP@- &/3sPzAr {5$0 0 | 1! 22#YH@s0tPzA|%&1] Q` ޱAK0Lqo sZ(N=W @E  _ 0}h` 5`t` Z7,a |idoD0Pq@/S`Ww RK`PL~` ِw ᰉp+7@) D`Jpj`J ޸o@̇ %1cϣ4t}}$h `IS  IR1 :u (/HE*^ k1H@FFeTV@ Rw./8/q pmp. ] es0+pWE0qC A:E QR!W[} 00 TC2 XYPi0 mf`0"'`0f q,pk`'@;7 hcS0U\0bU6@$İ|04 fG>a  (@pt~@Ov w@аl t%^`aϱ  Ԡ Q` ` ³O ]! JZdeJ%-3 y `oUթ y - : 吞F@NB D_|4~)`4 )^1 ^a1j) ^$5[FXPP /? h)尧, p'J)0]P0]PidtŠ| : &o sSd}*t'0Ň ` P ^ @V`Հ ly lQ 0P0 St \  Pt=*  d* ̀':1 ̇iଠcY!`P>6u` p +*/  )m.b=P!'YC2[7ӣ7y4F$`sqqk'`9qo! D^׾ d +h` iFC Ő{! Y= MFevTOa9E4`~ ŗyP' `,% +`  $hT2 dLEƎ` [ #p3 1 ކ>t~޻t_lW  lau }\k P}`  lb ra ; pM `/ vh 1\{{PԀ D 1#r Ĵ4! JEQ@j;y d= ?A+z]y:AuR@y{b5t@q@o < π[]} x(K'Q K\2a ^ DP &q o/*@ sVЛ]s ܀ `vA$TcP @60PAA5 Q @F;`J'81[ ۙ<12.^'2#"_4Ap҃BO8s;ؚ`q]9j `m(ayQ p= ف1=&4sP (b ޑy U"  ώ dq L nWd!@m JF@ ÁP]+v]qSf6` > BO͉| )J` !!#D bg` [ı^pm' !]s@N &y`W# "&! eF @ .s!'!aEl@ J  Q  0 @ 22O +̰  `@``  n ġ^ p }@*ė Bp & ;R° ' 0RsA`yZഈpϥ гnm][@k "$.> π vd| bȧԘ0 7;xay` !PllP5q ! dH6  8TP>bHOf1wTF2(H5gF5y&(FB`MFfJˣ`AzLf&)QrQr4l:Z9R3qis{Դ`o N5X\}Ce1s93:o":sycVԿ_蘅 f(nZ3]8FMO `2 HݥMa{ZBSDrÅњv3 X)Zz"GBhCXgx >yyr#r>:: ü&&V9$?yBt.aAy٣>&M. p%xeDj€,a>'kĒ5[f(f -r4 c<kQ~y1 :s(Ȑu<teO & bЉc-aǜ/6J+;9ǜy `OkŚ'վBršbwȌ?)'`B!DO8& roαĀGCߡl r/.PGaǒs1u,hp*9d?V:N fxR9GH Fe#S4NHOҠ3T.(?5BK"cD'`3Y|ʲ1 P0o(<р`'j*"aa0go+L8q8bTJ2asOY!rBLHEAJd (eX%  Y Fq32d!? rEwtaCS1'DBԡ 9`F)mV2om(7xF2 fpτ&BPpYB0Ɲ@a.3D'GDBa)@`1 fV.`0 JIlPoNU,`b p2Rxb#CO~)M kH3kćjB AK{ $AjXؘ9@*[AB0kbՊ11 @(d &P6m8XQcQ9񅀊L6 peբQ|c|H::!9JfA jL" @Y`XC_F:Kۅf  (@`A\k:瓇@GVA`s!/>ޕXObwy,Z&(w6࢜9-}CvE V`5M W?-JG! k ޠ J XKa%!aD$Y []!u{&*#QÇ. UAʓuCڬF4p.Iy6\6VFqJ"! 'xxS5( z lX?A fD?(!0C$bO 9cŽp3>kL6D`; Z1^H<?P  G!(G >":|2[ 6B7#OFyDDc[b0p"0(Pxc (l`wH?#iu DC89aX !3s4pi9"alo%ƽPYqs!jD38|v@"C(HE&dx4rJzr?o,d'JUш0:K:Ok~G!xqD2< h ^-U`c胟BG &0 x~DO!M~8G&@#[FPV @'a !<lnl4bmRQT.#YᗄrMofLp83,p%cXKۅ0nPom((,z?RR-: GX,HPl9(A9y8Rt(9jkH(•xoPt@J#/)<)j,@Xx a 1wH9iҊmu(u(Gj9g Hy"zP6H oLCwlyDl8! Q;Dok ipjx6+h],A:?`EPP6([`xƂ86x25pP<@ G[ *[7 h6؂h[;j<GƄ;P(_80=Hv\88=`K؄;IC`5pG`<{#sp B0 7(0pDy0LtT IڊHGCpapLs&dŲ4˽s шb)f KG`h$lj :Ȋ$4DTdt$L`9pTmІj0`@l1h>>X$۴hKz&FHHPQmQЉZ;80M<rsøj1iQPk%fNThGkQP?TNt|ቔ 8PQayeM&c8)̮Ph_Pтr`sj(Ȅ2kYX;PȊ-] j,y@D!P%PlX/=1tMXvrRpS(Ć~A!A)Ʉx OпyHs `8< A5>xnh(9mhZf71cH%F ux1p?D]j7 sB A=@RBlذ"lH,9dm7D9tW5Via`PmթNHpPAL4?)(NY @6PoIMQvHpIŹ?qXk؄QX}ƀiR?(]Y/B2( y+0 tCt`֩@h81pQQ11{r5 y@?bD4&O7Z1e`>kJu`BLy) ,8r y |mDx1OpCp.%i@mE囃k8 LtgXKz4Q6pKxV`hNP-%d%0(VXm.KTXl`(sOP\ԄX'VuȃeX-fh-;"8,q\SrcE.2ЦKHI(k`+JoI7fH'lxDh8Yz[4JےSKԆ{9*;x`D yP/3MX<0=X m)`Fuh${z"I)o࡚]i\U fo02L?HGLq@Gchsox>0IPPp ltTl-K˲c68T _-xppg#A#u-qb)& x$֊5]ُGɩB9SXK; E5ТCepCOJ[؄ĚD8X(d(# zğ^Gmh#?G CGrt.s/ZP6'5y96FMm ,訷"c0GYK~MR{t `]G6`7<8t؃gPi*pw O$hpbLxxHMzʄes񦀆!HEȆɍe8q3.p>p-CXXo<`/EFu >;D ? *F߿i@D"ƌ7r#Ȑ"GY-ؿ똲A7X(Eɉ8ԔfiF~EgH0`9Tӑc\Fpv)gH> by;ABJ{!"G.|! G lČш13#4!߰ jMJ 3"I~QtLRv!()R 74+r^"Ոq%ߤP5#(9ұv#=~# )A >#}C80U [>J$ (6! )HA Rylh$<9̫eːġ,4FTdʬc twE`CԕG|EAxhR`ANLk /G!,˃`CClH61y"/ G!/9p+#GGhx#nG q4]6@, BjX_}p"1!,GԦ BTD9-8L/C(PDBaCw!&RyB!T A t}PG&o(##suΐ#C6 bT_4RAwq""mE@ oArL;"ПQ9nF#sP"2( e0&\Ϝ7b?q  kL"[eDķ#~0"JAτ0q @ #休 '!EB})+YgDX*D6ZH97^ʭ\1"hQLPyF![Y_q=A8GQQ BcXfjz zc;"6eHW8Ng lA( l#9etITK3`AuwF=pC|,g̃(zDx9ޅLHCp 6q.(6`fju<,fxDùDH9P9(ÅA7 1&lUe< O`@7\)yC)L<8g XCFaըC30;0C$ mGIUC9+bCJ4C3H=1k!#Exa&U=\G_ "s!77|C< bYEA="(3с<@PŃ_=99hԁ5'Y)wm_$CvI£79x$1:|#I#!`9F|D|֏3jUtZժ4V_;lYgѦUm[oƕ;nFq*QqzAX!GxݛwC"Oh6H@sJ)w`_y2` D]1nm۷q;ㄿqfM5KFO7GpTLvFya9|F vJ}A%/~-Q9kP'$KRětLarAgR.?48hlIJPLQŵI A8)%]6ys`| aˈw6 r%!fIpK) $hKf+Ag< /yiOve(yRi@)Hk1dO ǚ)D;)@tOA u)%J柾bs$/zU/4bÚMCDRaʘYs4P'hR a`I4i#7B7NP+hhaDN: nĻi,j(^đGT5 1ռL/eIJ,A7aakFR_ީ>J1f 95D6R f'r9D!Q ij QZ,VD8NTB0: 묱.&Tҙ/X 9?YrADmkgX@3GyKdzSDєU(?ܩrQ'pcrRܑy!ly2F%\/5DJje%A$NTkXqyбf6j `AAl٢(s.99aF> Ĝ@ '0@P]d;O!ΑMc]P50@dB))Å6!q)щDêRT Әx]%8  )@(Aqә^@AU*+1B 8D81M A!j[%6?XxBl*iHB!A<(-Aeo$be Go*:q |9s< Q`C}.b "i;,)px@2k'hN^W> b"2х@cw`$ebl!>cx90Ec؈0䄇K8dDCQH< fho(=F tn'3al@ PB'+, "LB0CS$mQXzP>LhX*AgB th ч9y@=Hх ^(qP? bH6b(`5*KH_56t3+rCU &9csڒINjİBj9[pNh1/`a {af12dh4hj6!$ScCe8A 0YՈ2 k 0BgiRi,,yȷnBGA Äq A:l)!P:3;pq_-󗯑;O#B?6/[70֤wX}pYΜKl pDW 6!Bc Q 7a /8/c.#:ОH>I$+D C t=۱P)NJY `:?:;@ f~VA3aR?n/)@/(a~b&h j&!.105p9=A0EpIMQ#A2ȠTB: T 3 `>Mi44 ޥ/Cr%mR h$SR$_Ơ$ys *!O j za7,r l  msdh!6qdpo$ .Ar!@!A>y`~XIs<`LFsBB|*!aQچ xAXM6a#7$bR:3Æ4h 44jU` |A & r@hA *):jJ)8@9#9!Cs1@|4AB&>g]~2A t%`~!(+$HHEl!N 6`J"DDe, ` :SoʁfAb @$ @ .rOD~/XŦk^\H6f939"TMin Ӵ7aj `RaŠasPObTρ6 .adRab@u 6AaT/T1X5eN@ 2R!zCFP,!Zb#pU1|B$1;5{9=A@O܄$!dw;BY;a;!@40M{ !ABAP$/ (UZN !,Pj!8zL tH Ő:!A|f`aa.$ApObt}EڠAd @aa?ޡ{mRRE #7"?؁V`!>ar. L1Ap߼0!κFaam+f 6,!$`!O6  ̡4a fI΁@"^~ !Dja a`!eba*oÐSk!`꼚 hBAr47 Z&^oK%Rg왗mPK}Q&(zTIc#liLܖs&3N9{ 4СD=4ҥL:} 5ԩTZ*9OĘcMp4ㆱuy:bQ)r;CϏsz9ћ?Kuk&G]O;@g#HPy2:S !H!$=4=9Is'C9dH s# _a Gsa -"J?lرV&0(+|A^N> eRNIeV>tE5ؕD)>B <D7xfn grΙ_XcOgh.:'> iNJi^ini~ jD*FA d?zBOJ0 Pt,IUCC 4TF*(g)& 34ꫴ ;TU ,[^)3'5! `lN”(̸S7#&,% 8||M@jQ"{C)2Qt oPa7:PTفM68"X 0iPQҤ!ls9BP#nS<$uk9*nEUl`2%J9HfN݌rg88R#?xT)C7@) "!D85C Ov̛L1}V)r#L6@CI18.Rt3yC\SJM5L,q?d3s`K^S8wl1S  j0#=:h54s(! `HxLgB4 (%0D0D D0cnP?plbqXDޱGuB9 |(NWhXC0H*恈.|F q7[3AbõʑMPc H F68Bq,z6yw˹FG{It01 C8D̠F!&z=$Q\D<]B"`F5<Bix5ZjB̃];t؉b9b4 '! OA9^gp4E90ьg0.jx"@l<=?a8\"޹k2f4ZF1PU~>LJ*KcJ/3i-$eD5jCLD'>P*HlrP.YRX=7^mBǚPk|[Tk0 :ɱYXs+9GvGcz91hp,,+,6@WU 0U'P T>Q g Ѐ9 @ 3̕ 5 y =Q@N~}ٕu ,5Uo no=I0f?']Ba@6'Bz y(es 0 =hP= HЁ}%`gƒT5qb;"eR&*g2,%|r@F7P~@Q G.36 RbV@ ؠ d0F %` <*>S ̠ :{qP|bAX3ww+yق*6rm oը t=A_DRs0 2 89  7w9 @ 藔 6w &,u>r[K9z > `g}p]t o)8?# `q@g&K pp\Зyh>}P s 3$X棧AA&S *|QI ] p  7 }  x-E0іАPkƒep06o9  0\Zz1Et\9[ DDz#u뗘] ,bpMs | Հ p0#0Ǚ ^ earp Ġd}wl 0 `Lܕ [ [ v p-jv  @ 0@~ p_4Q),~>רz2 .l@ ['c!83JXC+@cnmUT$0&u'GހU Ԡ @) B38 T v |70Y@;2R1Bm1 g pL u"؀ p;`@" ` 4y p J[00%(TK&  ߐZ%y {o@au*Ey`}  ذ7upQ)%h>a0Oq'+A*E-2fC>qs@W}X3B4tpSB9!>+Ţ<}p#>AFA}@GacRQC1CAdmB?|tm QMCpWUK}5|(,7EzJ@! 0@6! 0Pw0Eh ؃9spS;vFM< fCo'P Fg܎1eD U  ް]9E8Ơ֐uoMD!"=^(jL~cW@#s %  pP ` a vu" @n  0Gq &i$p?a86m2$P+ c1 r&#! `BdPOm#sP ZҰD ` [0 [ Ρ@?a |eCwF"Q 8D _p> Mt QW P00uÆD`"p$aZQQEc(xH3‘;g˃`fbACx&Bƹj(P7HFRa l3qsH(2хrdbR؄Ѳar| uQfb7(hLQ .00Q7C6d+(ħ@(2Ց(tÚz3uHpTD=z%F|c0Rb`,9.hB@B;G:U[41e)sB'c`Fq91p QT<.l9";\g8\ڰ tl"99(݊:#id\rcO Ϡho81Bn_&`8G!#.[iq~ !3Cl!Pxk`[U7 [܀({n/(QEvecy8mx[Xrr[Eqf~hX%b@縧c82!DXJ+j1> K*q9;k9@m;A;\+>(< ڄ>h˄F/uȃCH$\v j˹Mx08<Ȅ8LDk. x?A>Ȅ3{3 <*y۾EB9Ȅ:8 C9 CGpn{LȄ'YG<ȃuxSp8b,oD2;N/;XI9xC0A<ఙ(L#20G:鯡D2K(VAflFSA3AFKȀ9PRhfHFtL(؂+X(w7C>>w c&%s88u zXs 8Xɝ90p(M?p0z0 ;{??0%(en8tRpuP)z iwXFH8q@ߑ0jDx@pmbz@$BCz@(hPpzpp$l؋RyX@`t|s) lPt@Gx0w yIpF4>siTċ|Ass;60I( dD RʃKDXVh|wAK(bx <8S(uAVɠT`x$VpR06P 85;y `̆8Ԉ}?pp PtsQQDw98X簩]O:t0rT2,i<@Č]3y* ሃ9<0lB8'9PyBP2 k[L&E9& kȄ9t[#Bja Gr; bxK ٺyXVh=j7z#`GQU@3T (`|ي Pj8Xh[jCGm@'5FvkO8 s xgyS(O c8 gy&L?X s0TJOGmB8 sȉK rȆgyB<0N0AhC`'x<0J iz lRTk$A+F,MgdAƌe bhh0B S"3 %pbb ;rx-)*A+P8kB)P`j``Qxc[Yj%8Ϟj `g|X`C>?S82)\VuCpxZ"D Xxt r(R0mHi|ErejݑV` I@7@)ݜk>Դfo@ck!JlX8~FF. -䅻ƈU58C( ȓ,HsָH/: LrhC%hЖ.0ܐx6+u7CeHc-ll )+(SMԝCQjMXM. `` ,7?HMǁOƅoz%mԟRbbn/؀.fX+5SH?K៣-y1jNH.C[8`>2 ;P>0;8@;ȥ8H?@s<քL`Pp>>h2Gi+6p? kiM9-;^ t,$$*%Ջs'TxKsX*pn`;ģ{sK:*QP7i,RQl@ j[/y 'pqqqqqqqq $C|F."DȾߙHXcϡ{A% s2?>0ߊ9CV#6f8%f@ vl9ϻ-9ut)-G/sJ/smp?SO19܉$M\Xل DhDM 8 l(=;Ad OȜJ/ oA5U6RG <:x-k%`Mtf(E؄RL?{+Lſ֊H8@ܢN5LYR̤XɤFkxe; iO;5vR~D-5C;?_ ?8f0wxrsɥM85Kr

WAyiiXt?xrF4\,}A-sx/OއBNAg^/GAx5_d=+8"KjK Oql=ֹS/aB:Mf^E^9Rsvt$xHzbgA/wtGD?d_FOd1[:9s؄I "TQ}ogX&lY`?M؄kexXyFȸ ~gRo{/r&Am׷xPo 4p!ÆB(q"Ŋ/b̨q#ǎ?wBȑ7Px2!ƒ$!dY̖vCw4k ( 7iœs(i?D&=%(c"*n+:j]Nl)g`pt 4 ;۲K0Dx"A+G.L2L߸X)̬y3Ξ?-Z♃U.y-S3Y2!zϺs4o!j̘_#77쓡rR"U[KuƜHХ4Qc !OrWsh"?AJ HKٷJфC' ?bbه,gA^Xw♧!IsٱN0LP@&(m$yPwPd홪*{m&:D80EA`H#ҹꈳ"2؟6 5قbrۭ߂[碛,zڽAS2T 7\H0xǎPâB gfc1f$뇟N& ,aN0<*3ļ?$2 y2ѳ;kBX`HM[MP\Z?.q :P 8B@AD_~'Lw߭s'?_&a҉uC#x{4I`KV9733d Db$ 3cŽ3& uL8ܥ!M0!@DlXsjjAMZZuҎ8ueL D I[X4hR}bZ_l3;L!|7;BQ$`@P(wh#bT L^oā$o(M.3.ST8}ɰfC5 @n(k/JGCEn0{aȸ٠A s'PCL\$Loc](1 g $<x3@ pXY?A I@j^ÁXoI Y6+6cиIu/.ahˤo\fd.yjG kyDٜ s"p!X)qF*qX@3(#`28b'2G0 2!HVc28 ipLx9drg+',YC}K!;L&YЇ/z{/EC`A 0#qAX`A.(7ʁxD/F}'~4t%6oWѨ-u2 ѷu"AL)j4-(4k`2; "Aj|CB aa ̠11eQ+E Ql`ڴ4V/xA3C锸@~F/[W;)%^P 5ҽ FU}fu\bJo/! Dh yD*! aIJ? 6:i lR Q<"HlXc sk0P\7=qWgOfu|9d8OaýF%[%4l.)/ xE=[2-A]*EA0\a8|f^)2[~Kx g84{ُ~dgޞTv9\UIb^4GC:Ғ4!j7Lȡ/bG v Q, s0eD8Ҳ*מSޥ m 2ь0۰F5<ϩ-Fo9aY:l mH3k~gz<ۺ52jc q";AH5|=K@szGG#xf5C3"F*vaC5޽D#5Z "5R 43E#;ÍC!1jL 鉈C=>lq ` BC*t0 A<9L[5NA8|$ ФZթggy>ƟRnB1\fe8L&]nå0HVT]}?`9X:8;??`8-"n1^H2O8Nj)R0bIllR6HOL5"5xBræC1&pA(* <@Xb,`,5P. @$b(9|Z0B%5d䖚7VFK,1MO D,*mWEd)9,ADLh"LB"$%#d:`@x:lWb 5mf@9C F [,4@,OpwY#Bcʦ+3q;C1+&hiT0ELDAGq/̈A0T+0XGd7@fRځ(8&p)y<`*y8B4[S.T4 D zq%1HWN 1'2pF́7C$eml?:Bm04 @>B(B=X((DȬT:6QA+DЁB9T%KAL D?,e|"mJ'6YHbHJذ]><3t@@įA9'$ApC5Af57pÁ2@8| "647D4O8Z!=#n3:X$E@<<04H6Ѓ:Ȝ U63xC<̄(o>9t3A0S04 4CtA8 ]@z{<n;hCsW\@1|:ZC-w: K./%LB8l|Q$:d\SCyC .^n*L5BC+(6p'p:7u 3V|m}v56Xdy:p#D1@PC43@PyApA84C9:rߋ-9,HNC-+7Bsy%ہ7`\ |ϳ|L[ElDrP$ 5vk<2M+ٲS/c{!טKR: yoF9qڋ13]Ѹ1gP9L3̠Q҉bD"SM,tF]}֩S[-8 6H]DVc}h]QTd/J25ht ,t<7u2,Qx.GA8{~1A i$g>^~%-.+TJ/- 1+j;eQ21h:^ 8d7:ԣ^Q4Hڋ*6ťJTRIfʙgHSGMhYs Aj|G\z!8?4ÛT9O64 㼔rJsNK%7Mdήc֚b>9DRw&1 saaY`!s rÆ88ǔdx6v@h'e(`r]e3HjwwU˜}ٗcuZ5u?6!g8H0QF9p Q&!f lG5 AxP uG* yX41 1:jʖј;q\_ NEB_%FW`l"{ѯ|(YQrbh k &t܁Y9hSHcCA`\& Lf )GA@  ul"!40?x169cG<x\3id|)Թ ;jSuWHJ4uuݨZLL".OUo(:  |#x: @ XLxG,k8JA k!AJ$"EJd Zb#h/EXr,@;5@̣T;6&IQY):wh*3OGm Mi7)-CytxAca z ,BpF7 r 2NƁ8BG[ڣ?3ʉ[)n:(2N"m niLQCb /qV5bF!чLā=.q 0`+8h5!Ʉ d2MԐFq;( #Al 5Ⰰ m ?H]\#T~oS/RB֤L'4\3kLoƨ` \mC" fDoHG+ Sf1y(p ^]rd D?Z @"mq&.ADֈ^A1u?ӉHd):4R֏$$fJ[偓 U?~o2hVXuQ9)L,fll0JaG0 '4 ANXTP! |o\k0#~qal4A3C1&[NѹV"a]sxjPdQ;KSQa߈sbyBBV!W8R1 oHփ sP`T*=MIwDE51//Q}6a\ΔY~(w6J~[I #dJԩ^u_Y:߈,ϚB@m8?r<-hq!v;$aӍn+=Qa>V0Z$CHP\aI"9D2Lb'*H#vny/d RFzQpB@ ~MO87c_OK'S !oJa!2+!A6 !`Lh5;@܁z!r! A)L0tAAwb5C 5Gaa R!~Aoa Q$A!A^#'ա3a¡& zDaԁ $PVTB@F B.X4֞ aN( P2!06 ] 388MMaL H63@ a mI A O@e2@ !=3`@ ӐAa!r4a6 irԁ,T@@'!4 aV4AF+Jm@<'z cAalajv F!؋ T@a @3?nԟAB`S1aAa<`G'a !!¡` * Z8z䧪iB^ Vaͤ!V! !L 3/,fDX&㈁!VF%;.ª2!db$ƶeA BC`fADϬZE& P` Aޡ!ĊYA6R +  zsF8' D !J JAb"L6(Jl IDhc='ba ` 8hƐl!< &@ P 3R -bd֬!mAa!' ʦ@!1gfn`z !`M"7!At$k>%!CwրQA8!!.anD; E=884rr!&T6;վ $!686U!DHHI:ΣC>@A%A u&  .A@&@zH!-&Ҍ!|@p!4!X@; "Fx%u YeB!#!2<dϘ9>!:T&`h6#Gљ!b 5J eaX,F NY aF5`!`|A4!Ha/J!D!a ,)X=AСx |ˁ<h):)h La:havJRa82>g*M/ : 3A[ZڦN /JH(ձYzz60 `LAXEԆʁ  .ADU9wZ 8eÀX!8 4! z`fVA{:B`R܁&.al2bɠQ` ց RAE 25á+" lճ A D h6a:h,q|Nޡ@A.iFa:{( rfs4!hAE 76H 4& Rg ̡m ?zz#́ȤVX`r"([  bo2}7bz]eApxޡ Ruáe>cSL9֨7)SpU* (SY*T/(1K Z_ꮼ.556lRtg={ I-S=м귄覫L)BQEc(kォ/,l' 7G,WlUAz+RUlHHM]F&B?vp" J]T(\񷂐|rNU,LIH!9L(a$QJ4Fh,ִ=-̋QM"#LCVJH nԩNo{)-z a҉"z$U&*7ꤒR]xM/u7`6#jOQ*06؈C Zn5ko2y pч!7c:F&Bz?tr28lMwlٴm 'Kվ}v.U%j4>Ϥja )ۀ# ub PKX0c.wS3ҥ(b_2 &@ E. KذTS~訄(GS8,fDXQ/{.{G=9jVHElq9[l@XpU@}+REEj s*b(c|;`l'Q%sG' '!\h.t>)F*c*iz4E@|d58Bؘ%`op8GC W"j7  ܅-ė,y9#C` |gd&Ls W P 7gɵ`$8F/cs81!ds\4`|o̥oh .X4F- w9# }h 0_k 0 @ pY5AG>6o/ i@ ]T3 `Y!5 A s y0 |@01cv$ P P` q ҰР S-@@ {V= ؠP1W5'7A1_(Q0=c.LFj@< 0 pbB@ҧ/WJ@ Đ,0?nP,P  p=А,!00ߐ uq ذ^`{uuQB dNy saf Px7'T`~_uP0n pE*G*]x^%/4B(LTsappLCB4'q6PJ $i]g$qpyI2,xf .L4wsAS`1 W ne ߀+! ,`+?\)2 & ڀK;v%`,P Q@4s   P I ̰Ab ^' qvra Q@b(|`K 61 ؠY[v,'Z!,ynv p! [ 0~  gdR`cb;>H3$s X[$ phIJ0c-  qo@tD8doÔtKтJɂ4@U^Nӡ[` x`W`  _s i  /J uƄs8oQ`r TJ |q1q P 3h 2j ,vP7 qc 0wis }p `8V Z |@Q0 Qd{ q q0|䑁 @ojvwx׳' N WĠP { 't[ϰP I0PaY:Jo Ik~1qhyƦmNS0"wܐ t^_* `21Z:(QetAAY.8()h!*Q pA  @O P; =Bz Ġː `'0 B n` y @ oOp[" IPМ0 K }\@ ;q\}!P,ʰK!ߐ |{x 40 y }J"jؠXq |)I0PG Հ," @Nt  0 = 1° 9#@ )6m0 @LG!`:r@ `p]sK` " `k P0 < P:k ?g Рk} ֑̰̒>eP)bVL P  H@ E~?譙N1KE0!$ Ҡ @Icy@2 J(P& Pl@ 0 @x M Ojvi 0^6בE1 ' ` =0a0c @QyPviTo ѰC k ?7b?js7!1l ԰ |!2 d@c~O1`}йOf  o_m5qPԓrqC(̾.s4-+H`;(̸dcYs-'Ί." % M?O1+Az(sGra pkdWFnesP9;.G ..-V#/4 O@ DPB >QD-^ĘQƇJoJɑۿs†X0)!Ḇ&.2(Zׁւ#oNdyh? c tA6 ZT?A 4TX :MFZj֭]yK,Qy"$IY=Qj'@z FIG[@l޼n dULf±bQ3HyJ%7)Ahl6OLd]w߅7vcCȣٴN/dnˏ~)hD9GQbQacf)e'J60&H P"1ÀLqG%hچkΣgZ)VeQFX20(p1I؁&01 8CAw9HpQIPGQ w7p'}(P~I_( G*4BXh8%VHa72֘9:GLp`jR@:GgN pJ 2=RαF5X&AIAg #uqTHbSeAPǑ=IK C>1*DC`@bq 9 Pk%sF)L|l9nl W@j`Åؠs4F/QXnQ `EpLxc qFuc.0`yh 31Ox(qC/ǡeCgDA H xH%F c\,2bd&5Y9 QIOhC CRPt9$jG\|?^pg1)9b$HԣꐆQ/rGyFQUi0AgC9 txjˠ9Z 0_C@ƎQ2DCCFb OC& D=aMvԣiEr@^'Px "EdK?딀!a@w \W# Kj?MZ"BFOC%则"R1,:8bZ2M8vF Ghb[,ׇTXM-:` 1T!(4C:YVֲIi<Y+d*aG<>162g 7>uYֶt)GF)/Ec?g"l6 =$mt;鶎RhF0v׻ox;^׼Eozջn#$ |h](#DT@ux{%]xr,}kB@ $,E9ZdV0R1R%|boE ЁM " DD*#8 TђZԑdư zF pX Z:QEL: o h7 oـ!B~AB(6bQ5׎NP Lul6ђM l@3y!yPGsRӐ,E&"'xp9s-Fа&D1DRC6&Q"S&p|T 8hilgP^,'Q'QAS h%I0isJ9q;("aW0<+ `F fHhbB~`,'/` b0)s(3R#GVXƜ3é85Ȥ Gb@ ,YƄ tHB r b` QTTK,G=wam 2'AfP z n7t` F8Fĸ /)mrr f ldYȄ+\Ai571ßKGQ X,T B1lc(1qqᨪ0pP̱Q#9bFpHc (4bÆA@op3 71yT>b8`<(c@PzX{ipfxg X0/f6 *(`x)Cy`o`PIPs ?eȀCP;ACLx(pGoQ8>@lp?@ yH.8CNa9h.@z 'ЄopRxC9Ps2G,uPPGoM(BK u@PDx(LCm RQH $z! D(<)&46j8!H.ox;AlȆpHLazb >8tn9lPĀp8qt0l+iPp9z>@pLx@Jp6X$i` d@ 8`2\4G`>/ |mvҿ %Ȅ`RyLxX/0D+x@ܣr ̃SিDSca(.(.@msC/Ȳ t`wt..boMm肫`(0&R0p(0wxpspeE0j(HH#3;*7{Rڬ_xFl@zG5fDh?8SA/&AQhۜ>9\9xXSByScxAGpIBPh{QDh>(:_ȟT0I.@x>p1w؄A@;'%OQ$Qp;;n?MPp+Sf8G7y(.(wx7PRP> <)F Ɂ2Es Bp8t rHM$Uۢz0a4QPeMB K;%85 !I[m9*EPuB>8Eox skX[,o8atX:tp.oVL|Jx%= Wjm%cmnu9 p3oH(oPs4 oǁp 6)՛-P٘ϒ" SE& 0boGf >؄O:CuS&#KqCFAC;K[G>`6:XLR\mxp`&DF܁Gas݈6::H, 9"]uׅؕ٥ڵݥ,J1 $nT8zOլǡ/T]^u%HK x+`@,^xAK߅5_+BCڛXN()Rނp/h@ , H=_E!;޸UN>ǕRF^Cbr)XK'  ̃+aO- ;ؒ 6@cZi(f;0-Y̭ҩP.1 .b,b@5kH 1^ Z<^}s)b (/=n&FՁ 1w X^z1~7 >J)% BUPjUA`,b;_Kveb бpeƠ+Ądz@`WVeQt`EJdfj慰3knopq&r6s6A,<.?(w>]pY{F)6P>P}ws_rTBjBڣLealyⱃu"Am>8/@.w| LVj؂C:+:\lXhE! >,_D861w(GNGqXC2 )DAnv:FGXU<81!*3fW2 Ɖ &G,D{"b _7yp 1kX(4Nx /o zỜ<Ƞ ?$B?9dT#; !ihA7^R8L90(Xl (>B9쑉Ib6"T;9H+ 0Ș"yCp9l-DHFBA`?4:6Xt@Ӱ) "`65xDy-@)T$B6X8H8H 8(_hG9)㨆(8AhC*| I!סlfc!9LB Z :BV;\N9TC17ā<02d=1e Au:p_8RBdlC0|A9lC1!)tA 1A(C=A<@*!:A"m3"5+([-CR,$=ݪ8)?|#,9A_C0(Adu !XC0C)"` %D!CA:h"0N3lB(X^0T}&9"J!l$F#`x?8dA4U5% 6Obz8=p(U6C#ިR!DdNB=|LIj-Ӑ% B& }CB9\!8rf|w))/DBh_ÍȔCB83C#@<@?dŸ3 BBRS:ӓ1LS:M0PMP7h &8ԘM07@W4(xQсCS'6,J x(n0.2C95$B]( dC99LI4 3|3|UQq\XNلaZ6 "4&JC6k֭DQpC;~;4S}C)"|R8BACA9C,IMYY<"Dymg͔!9'Q5|%-B7lXM?:a9CpWā-uô ؉7Ȱ!K1P o 78C,q!BjpA,8nehq6DH`aMYpv zhQY&l7:b l3(5bC)p8Pq) pi0I>n1:,pRiAT;8Nlӊ3A<=8V7(Cl7RMFjP(Cç,%'/1CS:W'Ą834CMC"L@.`2;^bC,k#6eHS4icлE 47;`L#|_xwQ=\A&l&hvA$ŁCS J"UQ:Ѓ);)(T}"C8CH!C14?1HCz&|C3<7HD7k+0NB$I ?\C|C&^'CNpt:H dH "49Ц$=:(k>'Ý(hU= Xt9:4K*#])6pr <=~rA<`!,=D5p 8<3|m~јSn:@vTaD&(BZ<VQ(#3C:0C&F'B^Adb*#7a5B=@H'_Au٤-ՂRlK%cGA9dI'QTeK/aƔ9f͔B$JпRL9ԥ`fK`)C@sο94 vYR LQSv g:*rʢ||P9QcF0b.mTUQz}5悛q`tiӧQVukׯa ΂mܨTցI+Ghc"v|c'WysϡG/5KSA{-F>|yѧW}{Ǘ?~}׿ Py4;Ph$; y#;`?#Ap 9CؠpmⱦeabAR2Dlф&G{)c4EAȢ_Q'RJD9y6QrQ&Dī'DRBcM`Ex$9p8 ) -2L8 zLCٰAdÀtD qĒFC-ArM 5:8&H03G@򸵮 9Idpd($GۺX<DKxՒ(~CX~Qyxq'"h)HEQtyDyAlFoޱ?C.c bl9' KY'sX8raƋjap豄p9gL8P]jFf oH @ $I'rKGU'&j-Qt fFķ"\?:2QcTƂ{z#Lǚ8MIj ByKG &ݯ{t6 C@/1EMaUrx0Yu4yƚMMR)pY o.oa2.eug }$ Ύx} !"zG`a(0C GF T Q0thD1*%:qqB D,}#$ V(<1Bp71yXG6†l ؆B0 Mb36 lNg<|Cx6&1H[Q DQ0j8mHJ)B|C9nǫRKYFz'8EApF, R!,QD1 VĢD"7##IBQF-lPF8&qgDGp)6aGQȀ1 ȥ"8$ʒp$v0  ">pH#)HF j  + bo`,aSl>98fTC)'ᖂxX"Q|A$b<@HP#`] wˀYA,i7|w&*bzaT!'Kֈ'G/3qfSHh 9b!CUahXC6J QA ׉,<'JO%^daN-}9Px)& y`;<Cfp0J0G8Dtp#-Ԛs8@ С/Dks,'w0#x;@eo$oWO"jv f{G| /u^Dny%?+H__;?F;Kn>| n|x;Dxφw 0S C F 쇠lF) ΁)!ŃRbN<0$ZM|O޲ *N &ތnfn $.nBP_/dpJ.Pgo訐jP;P( mN/MNaoB/8 'ҁb Bm,©`ܠ ڠ m& "baʁ"D nxoOa ;ppQ^$:"q+ pxP.괭 m$q0h1 qi/ѐ#0G qc _ Yq놏0G{#"bR~& !24!a~A&a&9,WLb!`-റਐmOnmPߐǑ&`1fP/鎎6 n nGb a*O{R+9c*r )w-` PP + n09N q!r#_ղ54!a @ & a "~A !8+4A6:/ fP,)`(D2-$n)0)wP PKѰR.aOY" ipI>nR0- U pޒ@_O/,]A_o!Q .g!'|O4-..2y -;" $aA6&8$Abr A4`` +_N aD6a!` a΄,)10+ꦯP3FQ At+tHKp N"D5WU.Q-.o.#4/Q1RQR}I -qu/SBRV5%s,Ab ]oX3Rb1VMN9C@/"h 0.`IHjV8`a6A;#!X JPao 6aг2 N s1 Gc34b%0>5N {1W en`!oHbNHBS6\T n QGQv69[RYv g14Q,30!=SwiVjgVŎk#ZK.*{VWub{v# &p` 8@ )~`^!o+`a%],ꀦ? V.AA긮j,yb2KmQ S)0xiyD-v/yyGtk1-Q Uz{߮wnpGvnWW$hTwu5BU%( $ A(ݜ^f`#  ʁ`%z/u1[TT^S`UՄQF Nt?c5GN%JtYE"eqT!5o$ B=Nd"Mh6A<1@X$%?&7B#B?LH Pw<=#h/.y Y&^u=‘9%6tЏK(UEBf!Bf!NVg;aQ&d+t&77Svs!֌ 3nx"@$A!NaV4n9yVrgQhc8Akb*Gs[[㷙u )qQѓ5}twcb X884fԺt3A%SUs6P`X/)t~ݲnV[X ^1Yp#®RONUJwun [)p.80yVTYxmt0kOwZH!MZ>9:lM P 9C1Օ?g0vEAqh' ^` A nU fGVñ/})wqKynpVczП1H.=B/j#T51/5gUYKе10MOD-sWC Tqo0e:A;TS y/ u1 %a/*9 Vxa$*U6 phRpf3f۱2odg9ngq5ecw 4p1)Iг=xv!.v{GOV ЧC8Xq5tS;㲶C/w5gg[0+S©< 5'cUx)N=W+yWDsOZn6Źvp+Y/>];{uBv9]09sP@Em3ܹEq\߁Ź?7TK8e!ce8?ɭYboA  `M~ `%[`M *؀'8fZO[#1q~pޖ>s(}]7*}yXer3Nqٲ~ 4ĻX}R cȻDےCC^֚Z!;Sջd_{s7[>ᖑ0 3Pca!!nlM @ n N D aRj57Xf!1˰cQ}40D;0 :B1RD {wk7gQMg5T~? %JnP2P? <"1# %RQaCK"'< R;I7(ӉӇ0aoWU\ee)I&f?'I&x< '80Fp@)tJ9t.!,q_h7x"Ta9O xP>ey\iٯD]^G:SZ [TJY$Cv4QP4)V]{8,&VLs\t#y5>nL~4c?DJ0Q TSCd >36zQt9Cxs))S%%ubr^? y\8!-"X3(f jtpD. F*` b |3@?`@60w b`Ap+ t@k|uل: l,#vF*".%!5et &Q08⊓P$}#LDᩫkA س$*7=hi2D)da͹"i=U'sil:rA+)H&vDV*F^Κ V\K(q4RD!  1z_`6.8D!X_' eA |3 3?W5,KP(G9q!Q҃E]TSa)xS'$3wv],_a<Ѳ$ c ݎ>5 ^y0Q-N%322mOD$5 z$X栈9DD*r b0#!\94@>"!Td PvhqH 1(!"&0Q͠ctZfqr-&CdE6y,grM2QnxBv8(kf\Cw=En!23,-^4{b|Mqun8t f8R~М !t8#>Gls8qfD~1 5HpG&4(}8QD=a y%~!2`oW1;d6r=YNz 5WqϠ`3?hw;!KVG+wvᗜ0*e'g5y]q"sm/`<`R#uh!D)LqacDCA$lx@\a@׹r"l Wz`@ ̰, b|DT vy=;ȅNp]a(cHehgikȆmoq(qed]D[ J%@b5 0.apӆ*:e]W<#%5a]3~ Ic/Ӈx3#'7v҂E* @ p)Ґr pp` C" 8 orq5U;+[rz $3NQgLHD]u4$RYx!)y$(-^hZQYDCrZ V2x q WVyP0 `x<7A]*1[5O_&0/F`v.}hIC.ڥ* 6.x>hҍR<$=3SS6QVH m0 , հ @ ` plnФ y h ʰG0$XOA[cfar5p³~W*yayZ7m&<@1q[%3jQG("y7c~'E!S!;8cEc(Le$ .P\9& C * a0 2$pqq.v 3^@ ,! ' Qp_Ps[mȁ=Y$D 2DV:;23C ́Gq0WYf+Q#oG[bwoAY٣l:Z'DR49ojC*dÓ"y;ji6wa;ř9/2AOHnh` ,DٷA[` RTa @ Xv@ }Y- 9i[75Qeĉv:p4;g+AZFirD%DnSϹ&DI>dF!DamYE2 3pBCəZAıFZu:<9XFYvLj!@@,M+D1` ]L |` } a4  ŀl,m ķ b[;k=.yR&HcdDDfEšVY9>$Of4=l$*. 2orm+ me1[3eQSYqnET[GI$EXŪrpX!MqE~JY3SGH:1 @ _0  P aQDFXNW)# a)@ [$@`J07ZlYD^=辫,Źww׸!Ѐ Hx`ro:r4#sH0P rzwGI!K^)˜ԨW>}̐+08!  t+m.P |)!Wɘ / TU@(0I" Ї'p?r' @P P@ 0py L $ C S Z SW Yv*Eڼ`Vm\AHfWČ@?]C | Ԡ0jJv 9V}Pؠ@% N" @ |)4< ++D 0^!AAl@b@ ؠ P8 0@ p@9π |o PX@9x@m S0@!x<=;6!FUITf8~R` ,S @1s霟~^ t հy ϔvWpN4tB40J4 g,(*"Vʼ/.78>3ĕm`zGr[SQ#_ATPCi*dMGʉ:?%7nUV q][ڸ _&\aĉ/fljQގ~/g\(e|ˣΑo # ڄ̞I3 ƞ~/x65Le@Q|*U.FQtO(*nTcuRؔ)y4M u9urB$>JFaŔz҈ě>Z;1%O«-j 2sqG{G -K,2#4+ I\dkϲB<#ւf*!`$W@x᠞c޲/~ʼn8B% )L7KhFS+0}Y$404)Ϣh3Y LYCRyO|ʲE숓4!O&ɴ|fq3 g2t%uF5>s!IntF縶rZC;Ќe$Q>mzSG.nnQB;L<-g{PHrUe8UΌd*KUO`ԣjgl4tbSƵ*R#S;%[&U!Jռ$5Fx,`"ɤ6JiZ \R5GPt-GXKzTL"Q_*rU%#YZ`vŨ9&#BՓ5_czL_#Ψu-3'Z:dQZݿ"%VrE*k{^yH,i rl W(lLS^3znьŪHm'T٢2IubZV77J/_N<4zG2|W#*AG.z@|brƸ~u#'ѧt fQ/ƕť냑ع%oݚZ-/|e8Γ|lI: Lf?uݪC][Ϊˬ/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||qٸs NbCƓ+_μУKNسkν/ӫ_\ GϿ(hU 6F(Vhfv ($hV ʼn,._4h8 @)DiH&V]J6TZ%eXf5ZZvC`)dihl)]nz JYA|Gїu (ϡO=egk(>*:B)tq z?t`s:BAt?pXӊ2A!@ ) (Π92{+=! :q*ʎ*1)m:d3`n^u24sIklP9Hwvk< ĨsJ^ϦjgŶcOpAϠtJJ?Xr]"A`5/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||](b8PEJQiˆ0?dT`^Ĩdt@4A`tÎH&IfHD!P ]AtA b Eb щ)^\._JADOjHPT&TKAiPH9P"6 ɞQaJPCr)Hwaꪬ*무j뭳.뮼klDh"6FlV `tfv+k覫XtV&ak@'u§yzjj˥֪/kqƥI$l(,0,meY?t3gQ+~FQ]:o@\I+N14%;YjY I6-mu^tvtkw͵` *vR7p-.Wm{ rWX7.9x<9iDU[k"97!N$-VQDMT{Yuȝ3^y&[Wogw/oFt|~ƿ/ZB3sn H@)L(2'@PĖsMzGH(L W0bHE6r+i$s$!a&:PH*oV̢.z` #'mhL6p\<9΁ṿ|dLRG1!F:򑐌$'IJZ̤&7Nz (GIRL*WG,gIEi.C] 0Ib1Jd\; ~t"s}3ZLqr pJ4Ŝ/!gj2-畏g4 ?lN . R D'JъZX>rzGL@VYA؉q%KH2)$V3L̊g΍joByiXxrB١J|ڿj\K/Hu(j[wR褤j)QB@XLp\J׺ƞ,a]Z&F`Mb,2d'KȯQEwjZa)hKW`vI)-jW&%*L`cKͭn3dIfeh)okP6 ! ,TX' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|| HA8\Ȑ†#AE uc!: /Ȏ uc 2b@! ,Y5' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|| HA8\Ȑ†#AE uc!: /Ȏ uc 2b@! ,Y5' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|| HA8\Ȑ†#AE uc!: /Ȏ uc 2b@! ,Y5' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l|| HA8\Ȑ†#AE uc!: /Ȏ uc 2b@! ,Y5' 3? !!"'* "%""&!%"% $'#)")#+"(#+$+#.%.&/&/ &* &.(-).1+7-"0%1&1%4(2)3)3(4(4+7*6.7,7/6 *1 )0 +6 ,7,5(8*8+;,9,9,8/9.9,:,;-;,:,;-;.:.;.;/:-<,=-=.<.=/=->/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||/>-=,</> /8/9+1+1,1,0 0;3; 0=4>031628295;5=9>'KBLCRGX)@B)AF"CK)BJ.GK,FO"FR&FP.JP(KV#KY*O^4MQ0LV/PX-S_9QW6S^;UX/Xh5Xa>Z`=\g6[h>aj=cp>gvDTVFY[uXQMtFG\`H_dG`hEdnKeiKemJioWmgQjoWkmHisOlwGkyPksXmqMpyYpuSqyYryYu`qe`suauxfwyax|azf{l||zJRN]R-۷i2 &ݻx^ũ߄zqBUM̸_f.|p2傐/ D|UdU@]o tm"lRCVR2suvP04K8Jy7m6B^H9NsO$˦>/w/ R@8f=D;&=0d"m77hAyM!LLۋϽHx>)~ JLyc[N8co`9~)Gf`e3)h[^?x;duecredit-0.8.0/examples/example_scipy.py000066400000000000000000000007671362001701600205270ustar00rootroot00000000000000# A tiny analysis script to demonstrate duecredit # # Import of duecredit is not necessary if you just run this script with # python -m duecredit # import duecredit # Just to enable duecredit from scipy.cluster.hierarchy import linkage from scipy.spatial.distance import pdist from sklearn.datasets import make_blobs print("I: Simulating 4 blobs") data, true_label = make_blobs(centers=4) dist = pdist(data, metric='euclidean') Z = linkage(dist, method='single') print("I: Done clustering 4 blobs") duecredit-0.8.0/requirements.txt000066400000000000000000000001511362001701600167440ustar00rootroot00000000000000# install everything among install_requires specified in setup.py # and needed for testing -e .[tests] duecredit-0.8.0/setup.cfg000066400000000000000000000003261362001701600153050ustar00rootroot00000000000000[metadata] description-file = README.md [bdist_rpm] release = 1 packager = Yaroslav Halchenko doc_files = README.md CHANGELOG.md LICENSE CONTRIBUTING.md duecredit-0.8.0/setup.py000077500000000000000000000131571362001701600152070ustar00rootroot00000000000000#!/usr/bin/env python """ duecredit -- publications (donations, etc) tracer """ import re import os import sys import re from datetime import datetime from setuptools import setup from pkgutil import walk_packages from subprocess import Popen, PIPE from os.path import exists # Adopted from citeproc-py # License: BSD-2 # Copyright 2011-2013 Brecht Machiels PACKAGE = 'duecredit' PACKAGE_ABSPATH = os.path.abspath(PACKAGE) VERSION_FILE = PACKAGE + '/version.py' # retrieve the version number from git or VERSION_FILE # inspired by http://dcreager.net/2010/02/10/setuptools-git-version-numbers/ try: if exists('debian/copyright'): print('Generating version.py out of debian/copyright information') # building debian package. Deduce version from debian/copyright with open('debian/changelog', 'r') as f: lines = f.readlines() __version__ = re.sub('(.*)-(.*?)$', r'\1.debian\2', lines[0].split()[1].strip('()') ).replace('-', '.') # TODO: unify format whenever really bored ;) __release_date__ = re.sub('^ -- .*>\s*(.*)', r'\1', list(filter(lambda x: x.startswith(' -- '), lines))[0].rstrip()) else: print('Attempting to get version number from git...') git = Popen(['git', 'describe', '--abbrev=4', '--dirty'], stdout=PIPE, stderr=sys.stderr) if git.wait() != 0: raise OSError line = git.stdout.readlines()[0].strip().decode('ascii') if line.count('-') >= 2: # we should parse it to make version compatible with PEP440 # unfortunately we wouldn't be able to include git treeish # into the version, and thus can have collisions. So let's # release from master only line = '.dev'.join(line.split('-')[:2]) __version__ = line __release_date__ = datetime.now().strftime('%b %d %Y, %H:%M:%S') with open(VERSION_FILE, 'w') as version_file: version_file.write("__version__ = '{0}'\n".format(__version__)) version_file.write("__release_date__ = '{0}'\n".format(__release_date__)) except OSError as e: print('Assume we are running from a source distribution.') # read version from VERSION_FILE if os.path.exists(VERSION_FILE): with open(VERSION_FILE) as version_file: code = compile(version_file.read(), VERSION_FILE, 'exec') exec(code, locals(), globals()) else: __version__ = '0.0.0.dev' print("Version: %s" % __version__) # In some environments with too basic locale settings # it might not be able to read the file with unicode, so we # would then just ignore the errors with open('README.md', 'rb') as f: README = f.read() # We need to decode it reliably try: README = README.decode() except UnicodeDecodeError: README = README.decode('ascii', errors='replace') def find_packages(path, prefix): yield prefix prefix = prefix + "." for _, name, ispkg in walk_packages(path, prefix): if ispkg: yield name setup( name=PACKAGE, version=__version__, packages=list(find_packages([PACKAGE_ABSPATH], PACKAGE)), scripts=[], install_requires=['requests', 'citeproc-py>=0.4', 'six'], extras_require={ 'tests': [ 'pytest', 'vcrpy', 'contextlib2' ] }, include_package_data=True, provides=[PACKAGE], entry_points={ 'console_scripts': [ 'duecredit=duecredit.cmdline.main:main', ], }, author='Yaroslav Halchenko, Matteo Visconti di Oleggio Castello', author_email='yoh@onerussian.com', description='Publications (and donations) tracer', long_description="""\ duecredit is being conceived to address the problem of inadequate citation of scientific software and methods, and limited visibility of donation requests for open-source software. It provides a simple framework (at the moment for Python only) to embed publication or other references in the original code so they are automatically collected and reported to the user at the necessary level of reference detail, i.e. only references for actually used functionality will be presented back if software provides multiple citeable implementations. To get a sense of what duecredit is about, run for example shipped along example script, or your analysis script with `-m duecredit`, e.g. python -m duecredit examples/example_scipy.py """, url='https://github.com/duecredit/duecredit', # Download URL will point to the latest release, thus suffixes removed download_url='https://github.com/duecredit/duecredit/releases/tag/%s' % re.sub('-.*$', '', __version__), keywords=['citation tracing'], license='2-clause BSD License', classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Environment :: Other Environment', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'Intended Audience :: Education', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Legal Industry', 'Intended Audience :: Other Audience', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Topic :: Documentation', 'Topic :: Software Development :: Documentation', 'Topic :: Software Development :: Libraries :: Python Modules', ] ) duecredit-0.8.0/tox.ini000066400000000000000000000011661362001701600150020ustar00rootroot00000000000000[tox] envlist = py27,py33,py34,py35 #,flake8 [testenv] commands = py.test deps = -r{toxinidir}/requirements.txt [testenv:cover] commands = coverage run --source duecredit -m py.test [testenv:flake8] commands = flake8 {posargs} [testenv:venv] commands = {posargs} [flake8] #show-source = True # E265 = comment blocks like @{ section, which it can't handle # E266 = too many leading '#' for block comment # E731 = do not assign a lambda expression, use a def # W293 = Blank line contains whitespace #ignore = E265,W293,E266,E731 max-line-length = 120 include = duecredit exclude = .tox,.venv,venv-debug,build,dist,doc,git/ext/